diff --git a/.github/test-spec.yml b/.github/test-spec.yml new file mode 100644 index 00000000000..789f1a4e235 --- /dev/null +++ b/.github/test-spec.yml @@ -0,0 +1,257 @@ +# This is the Jenkins ci variant of the .github/labler.yaml + +"CI-run-zephyr-twister": + - any: + - "!.github/**/*" + - "!doc/**/*" + - "!CODEOWNERS" + - "!LICENSE" + - "!**/*.rst" + - "!VERSION" + - "!submanifests/**/*" + - "!MAINTAINERS.yml" + - "!version.h.in" + - "!Jenkinsfile" + - "!**/*.md" + +"CI-iot-zephyr-lwm2m-test": + - "drivers/console/**/*" + - "drivers/flash/**/*" + - "subsys/dfu/boot/**/*" + - "subsys/net/ip/**/*" + - "subsys/net/lib/http/**/*" + - "subsys/net/lib/lwm2m//**/*" + - "subsys/net/**/*" + +"CI-iot-samples-test": + - "boards/arm/nrf9160dk_nrf9160/**/*" + - "dts/arm/nordic/nrf9160*" + - "include/net/**/*" + - "subsys/net/lib/**/*" + +"CI-iot-libraries-test": + - "boards/arm/nrf9160dk_nrf9160/**/*" + - "dts/arm/nordic/nrf9160*" + - "include/net/socket_ncs.h" + - "subsys/testsuite/ztest/**/*" + +"CI-lwm2m-test": +# Not necessary to run tests on changes to this repo. + +"CI-boot-dfu-test": + - "subsys/mgmt/mcumgr/**/*" + - "subsys/dfu/**/*" + - "include/mgmt/mcumgr/**/*" + - "include/dfu/**/*" + - "samples/subsys/mgmt/mcumgr/smp_svr/**/*" + +"CI-tfm-test": + - "boards/arm/nrf5340dk_nrf5340/**/*" + - "boards/arm/nrf9160dk_nrf9160/**/*" + - "drivers/entropy/*" + - "dts/arm/nordic/nrf5340*" + - "dts/arm/nordic/nrf9160*" + - "modules/trusted-firmware-m/**/*" + - "samples/tfm_integration/**/*" + +"CI-ble-test": + - any: + - "drivers/bluetooth/**/*" + - any: + - "dts/arm/nordic/nrf5*" + - any: + - "subsys/bluetooth/**/*" + - "!subsys/bluetooth/mesh/**/*" + - "!subsys/bluetooth/audio/**/*" + - any: + - "include/zephyr/bluetooth/**/*" + - "!include/zephyr/bluetooth/mesh/**/*" + - any: + - "samples/bluetooth/**/*" + - "!samples/bluetooth/mesh/**/*" + - "!samples/bluetooth/mesh_demo/**/*" + - "!samples/bluetooth/mesh_provisioner/**/*" + - any: + - "tests/bluetooth/**/*" + - "!tests/bluetooth/mesh/**/*" + - "!tests/bluetooth/mesh_shell/**/*" + - "!tests/bluetooth/audio/**/*" + +"CI-mesh-test": + - "subsys/bluetooth/mesh/**/*" + - "include/zephyr/bluetooth/mesh/**/*" + - "samples/bluetooth/mesh/**/*" + - "samples/bluetooth/mesh_demo/**/*" + - "samples/bluetooth/mesh_provisioner/**/*" + - "tests/bluetooth/mesh/**/*" + - "tests/bluetooth/mesh_shell/**/*" + +"CI-zigbee-test": + - "subsys/mgmt/mcumgr/**/*" + - "subsys/dfu/**/*" + - "include/mgmt/mcumgr/**/*" + - "include/dfu/**/*" + +"CI-thingy91-test": + - "boards/arm/nrf9160dk_nrf9160/**/*" + - "arch/x86/core/**/*" + - "arch/x86/include/**/*" + - "drivers/console/**/*" + - "drivers/ethernet/**/*" + - "drivers/flash/**/*" + - "drivers/hwinfo/**/*" + - "drivers/interrupt_controller/**/*" + - "drivers/net/**/*" + - "drivers/serial/**/*" + - "drivers/timer/**/*" + - "include/**/*" + - "kernel/**/*" + - "lib/libc/common/source/stdlib/**/*" + - "lib/libc/newlib/**/*" + - "lib/libc/picolibc/**/*" + - "lib/os/**/*" + - "lib/posix/**/*" + - "misc/**/*" + - "modules/mbedtls/**/*" + - "soc/x86/ia32/**/*" + - "subsys/fs/fcb/**/*" + - "subsys/logging/**/*" + - "subsys/net/**/*" + - "subsys/random/**/*" + - "subsys/settings/include/**/*" + - "subsys/settings/src/**/*" + - "subsys/stats/**/*" + - "subsys/storage/flash_map/**/*" + - "subsys/storage/stream/**/*" + - "subsys/tracing/**/*" + +"CI-desktop-test": + - "**/*" + +"CI-crypto-test": + - "boards/arm/nrf52840dk_nrf52840/**/*" + - "boards/arm/nrf5340dk_nrf5340/**/*" + - "boards/arm/nrf9160dk_nrf9160/**/*" + - "drivers/entropy/*" + - "drivers/serial/**/*" + - "dts/arm/nordic/nrf52840*" + - "dts/arm/nordic/nrf5340*" + - "dts/arm/nordic/nrf9160*" + - "include/drivers/serial/**/*" + - "modules/mbedtls/**/*" + +"CI-fem-test": + - "**/*" + +"CI-rs-test": + - "**/*" + +"CI-thread-test": + - "include/zephyr/net/**/*" + - "modules/mbedtls/**/*" + - "modules/openthread/**/*" + - "samples/net/openthread/**/*" + - "soc/arm/nordic_nrf/**/*" + - "subsys/net/**/*" + - "subsys/settings/**/*" + +"CI-nfc-test": + - "**/*" + +"CI-matter-test": + - "include/dfu/**/*" + - "include/mgmt/mcumgr/**/*" + - "soc/arm/nordic_nrf/**/*" + - "subsys/dfu/**/*" + - "subsys/settings/**/*" + - "subsys/net/**/*" + - "subsys/mgmt/mcumgr/**/*" + - "drivers/net/**/*" + - "samples/bluetooth/hci_rpmsg/**/*" + - any: + - "subsys/bluetooth/**/*" + - "!subsys/bluetooth/mesh/**/*" + - "!subsys/bluetooth/audio/**/*" + +"CI-find-my-test": + - "**/*" + +"CI-gazell-test": + - "**/*" + +"CI-rpc-test": + - "**/*" + +"CI-modemshell-test": + - "include/net/**/*" + - "include/posix/**/*" + - "include/shell/**/*" + - "drivers/net/**/*" + - "drivers/serial/**/*" + - "drivers/wifi/**/*" + - "subsys/shell/**/*" + - "subsys/net/**/*" + - "subsys/settings/**/*" + +"CI-positioning-test": + - "include/net/**/*" + - "include/posix/**/*" + - "drivers/net/**/*" + - "drivers/wifi/**/*" + - "subsys/net/**/*" + - "subsys/settings/**/*" + +"CI-cloud-test": + - "include/zephyr/dfu/**/*" + - "include/zephyr/net/**/*" + - "include/zephyr/posix/**/*" + - "include/zephyr/settings/**/*" + - "drivers/led/**/*" + - "drivers/net/**/*" + - "drivers/sensor/**/*" + - "drivers/serial/**/*" + - "drivers/wifi/**/*" + - "lib/posix/**/*" + - "soc/arm/nordic_nrf/**/*" + - "subsys/dfu/**/*" + - "subsys/net/**/*" + - "subsys/settings/**/*" + +"CI-wifi": + - "subsys/net/l2/wifi/**/*" + - "subsys/net/l2/ethernet/**/*" + +"CI-sidewalk-test": + - "include/dfu/**/*" + - "include/mgmt/mcumgr/**/*" + - "soc/arm/nordic_nrf/**/*" + - "subsys/dfu/**/*" + - "subsys/settings/**/*" + - "subsys/mgmt/mcumgr/**/*" + - "samples/bluetooth/hci_rpmsg/**/*" + - any: + - "subsys/bluetooth/**/*" + - "!subsys/bluetooth/mesh/**/*" + - "!subsys/bluetooth/audio/**/*" + +"CI-audio-test": + - "boards/arm/nrf5340_audio_dk_nrf5340/**/*" + - "drivers/flash/**/*" + - "drivers/spi/**/*" + - "drivers/gpio/**/*" + - "drivers/i2c/**/*" + - "drivers/watchdog/**/*" + - "include/dfu/**/*" + - "include/mgmt/mcumgr/**/*" + - "samples/bluetooth/hci_rpmsg/**/*" + - "soc/arm/nordic_nrf/**/*" + - "subsys/bluetooth/audio/**/*" + - "subsys/bluetooth/host/**/*" + - "subsys/dfu/**/*" + - "subsys/fs/**/*" + - "subsys/mgmt/mcumgr/**/*" + - "subsys/sd/**/*" + - "subsys/storage/**/*" + - "subsys/task_wdt/**/*" + - "subsys/usb/**/*" + - "subsys/zbus/**/*" diff --git a/.github/workflows/bsim-tests.yaml b/.github/workflows/bsim-tests.yaml index 435d61c3085..290052ee965 100644 --- a/.github/workflows/bsim-tests.yaml +++ b/.github/workflows/bsim-tests.yaml @@ -78,7 +78,7 @@ jobs: west forall -c 'git reset --hard HEAD' - name: Check common triggering files - uses: tj-actions/changed-files@v35 + uses: tj-actions/changed-files@v41 id: check-common-files with: files: | @@ -93,7 +93,7 @@ jobs: tests/bsim/* - name: Check if Bluethooth files changed - uses: tj-actions/changed-files@v35 + uses: tj-actions/changed-files@v41 id: check-bluetooth-files with: files: | @@ -102,7 +102,7 @@ jobs: subsys/bluetooth/** - name: Check if Networking files changed - uses: tj-actions/changed-files@v35 + uses: tj-actions/changed-files@v41 id: check-networking-files with: files: | diff --git a/.github/workflows/clang.yaml b/.github/workflows/clang.yaml index 9108d31b45b..402cbae9f95 100644 --- a/.github/workflows/clang.yaml +++ b/.github/workflows/clang.yaml @@ -8,8 +8,7 @@ concurrency: jobs: clang-build: - if: github.repository_owner == 'zephyrproject-rtos' - runs-on: zephyr-runner-linux-x64-4xlarge + runs-on: ubuntu-latest container: image: ghcr.io/zephyrproject-rtos/ci:v0.26.4 options: '--entrypoint /bin/bash' @@ -19,11 +18,13 @@ jobs: fail-fast: false matrix: platform: ["native_posix"] + subset: [1, 2, 3, 4, 5] env: ZEPHYR_SDK_INSTALL_DIR: /opt/toolchains/zephyr-sdk-0.16.1 LLVM_TOOLCHAIN_PATH: /usr/lib/llvm-16 COMMIT_RANGE: ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} BASE_REF: ${{ github.base_ref }} + MATRIX_SIZE: 5 outputs: report_needed: ${{ steps.twister.outputs.report_needed }} steps: @@ -85,7 +86,7 @@ jobs: id: cache-ccache uses: zephyrproject-rtos/action-s3-cache@v1.2.0 with: - key: ${{ steps.ccache_cache_timestamp.outputs.repo }}-${{ github.ref_name }}-clang-${{ matrix.platform }}-ccache + key: ${{ steps.ccache_cache_timestamp.outputs.repo }}-${{ github.ref_name }}-clang-${{ matrix.subset }}-ccache path: /github/home/.cache/ccache aws-s3-bucket: ccache.zephyrproject.org aws-access-key-id: ${{ vars.AWS_CCACHE_ACCESS_KEY_ID }} @@ -98,6 +99,16 @@ jobs: test -d github/home/.cache/ccache && rm -rf /github/home/.cache/ccache && mv github/home/.cache/ccache /github/home/.cache/ccache ccache -M 10G -s + - name: Build test plan with Twister + id: twister_test_plan + run: | + export ZEPHYR_BASE=${PWD} + export ZEPHYR_TOOLCHAIN_VARIANT=llvm + + # check if we need to run a full twister or not based on files changed + python3 ./scripts/ci/test_plan.py -p native_posix -c origin/${BASE_REF}.. + + - name: Run Tests with Twister id: twister run: | @@ -111,7 +122,7 @@ jobs: if [ -s testplan.json ]; then echo "report_needed=1" >> $GITHUB_OUTPUT # Full twister but with options based on changes - ./scripts/twister --force-color --inline-logs -M -N -v --load-tests testplan.json --retry-failed 2 + ./scripts/twister --inline-logs -M -N -v --load-tests testplan.json --retry-failed 2 --subset ${{matrix.subset}}/${MATRIX_SIZE} else # if nothing is run, skip reporting step echo "report_needed=0" >> $GITHUB_OUTPUT @@ -126,7 +137,7 @@ jobs: if: always() && steps.twister.outputs.report_needed != 0 uses: actions/upload-artifact@v3 with: - name: Unit Test Results (Subset ${{ matrix.platform }}) + name: Unit Test Results (Subset ${{ matrix.subset }}) path: twister-out/twister.xml clang-build-results: diff --git a/.github/workflows/commit-tags.yml b/.github/workflows/commit-tags.yml new file mode 100644 index 00000000000..9e0323f9498 --- /dev/null +++ b/.github/workflows/commit-tags.yml @@ -0,0 +1,31 @@ +name: Commit tags + +on: pull_request + +jobs: + commit_tags: + runs-on: ubuntu-22.04 + name: Run commit tags checks on patch series (PR) + steps: + - name: Update PATH for west + run: | + echo "$HOME/.local/bin" >> $GITHUB_PATH + + - name: Checkout the code + uses: actions/checkout@v3 + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 + + - name: Install python dependencies + run: | + pip3 install setuptools + pip3 install wheel + pip3 install gitlint + + - name: Run the commit tags + uses: nrfconnect/action-commit-tags@main + with: + target: '.' + baserev: origin/${{ github.base_ref }} + revrange: 'none' diff --git a/.github/workflows/compliance.yml b/.github/workflows/compliance.yml index f84faff3e4a..f17780c38e3 100644 --- a/.github/workflows/compliance.yml +++ b/.github/workflows/compliance.yml @@ -38,8 +38,8 @@ jobs: git config --global user.name "Your Name" git remote -v # Ensure there's no merge commits in the PR - [[ "$(git rev-list --merges --count origin/${BASE_REF}..)" == "0" ]] || \ - (echo "::error ::Merge commits not allowed, rebase instead";false) + #[[ "$(git rev-list --merges --count origin/${BASE_REF}..)" == "0" ]] || \ + #(echo "::error ::Merge commits not allowed, rebase instead";false) git rebase origin/${BASE_REF} # debug git log --pretty=oneline | head -n 10 @@ -56,8 +56,8 @@ jobs: # debug ls -la git log --pretty=oneline | head -n 10 - ./scripts/ci/check_compliance.py --annotate -e KconfigBasic \ - -c origin/${BASE_REF}.. + ./scripts/ci/check_compliance.py --annotate -e KconfigBasic -e Kconfig \ + -e KconfigBasicNoModules -c origin/${BASE_REF}.. - name: upload-results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/doc-build.yml b/.github/workflows/doc-build.yml index 40e170ddc8a..a18507e028d 100644 --- a/.github/workflows/doc-build.yml +++ b/.github/workflows/doc-build.yml @@ -35,7 +35,7 @@ env: jobs: doc-build-html: name: "Documentation Build (HTML)" - runs-on: zephyr-runner-linux-x64-4xlarge + runs-on: ubuntu-22.04 timeout-minutes: 45 concurrency: group: doc-build-html-${{ github.ref }} diff --git a/.gitlint b/.gitlint index b8d25ce49b9..8a33f140b2a 100644 --- a/.gitlint +++ b/.gitlint @@ -16,7 +16,7 @@ debug = false extra-path=scripts/gitlint [title-max-length-no-revert] -line-length=75 +line-length=120 [body-min-line-count] min-line-count=1 @@ -42,7 +42,7 @@ words=wip [max-line-length-with-exceptions] # B1 = body-max-line-length -line-length=75 +line-length=120 [body-min-length] min-length=3 diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d761a97d8b..e23650f262f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -551,31 +551,9 @@ if(ZEPHYR_GIT_INDEX) set(git_dependency ${ZEPHYR_GIT_INDEX}) endif() -add_custom_command( - OUTPUT ${PROJECT_BINARY_DIR}/include/generated/version.h - COMMAND ${CMAKE_COMMAND} -DZEPHYR_BASE=${ZEPHYR_BASE} - -DOUT_FILE=${PROJECT_BINARY_DIR}/include/generated/version.h - -DVERSION_TYPE=KERNEL - -DVERSION_FILE=${ZEPHYR_BASE}/VERSION - -DKERNEL_VERSION_CUSTOMIZATION="${KERNEL_VERSION_CUSTOMIZATION}" - ${build_version_argument} - -P ${ZEPHYR_BASE}/cmake/gen_version_h.cmake - DEPENDS ${ZEPHYR_BASE}/VERSION ${git_dependency} -) add_custom_target(version_h DEPENDS ${PROJECT_BINARY_DIR}/include/generated/version.h) if(EXISTS ${APPLICATION_SOURCE_DIR}/VERSION) - add_custom_command( - OUTPUT ${PROJECT_BINARY_DIR}/include/generated/app_version.h - COMMAND ${CMAKE_COMMAND} -DZEPHYR_BASE=${ZEPHYR_BASE} - -DOUT_FILE=${PROJECT_BINARY_DIR}/include/generated/app_version.h - -DVERSION_TYPE=APP - -DVERSION_FILE=${APPLICATION_SOURCE_DIR}/VERSION - -DAPP_VERSION_CUSTOMIZATION="${APP_VERSION_CUSTOMIZATION}" - ${build_version_argument} - -P ${ZEPHYR_BASE}/cmake/gen_version_h.cmake - DEPENDS ${APPLICATION_SOURCE_DIR}/VERSION ${git_dependency} - ) add_custom_target(app_version_h DEPENDS ${PROJECT_BINARY_DIR}/include/generated/app_version.h) add_dependencies(zephyr_interface app_version_h) endif() @@ -626,6 +604,32 @@ endforeach() set(ZEPHYR_CURRENT_MODULE_DIR) set(ZEPHYR_CURRENT_CMAKE_DIR) +add_custom_command( + OUTPUT ${PROJECT_BINARY_DIR}/include/generated/version.h + COMMAND ${CMAKE_COMMAND} -DZEPHYR_BASE=${ZEPHYR_BASE} + -DOUT_FILE=${PROJECT_BINARY_DIR}/include/generated/version.h + -DVERSION_TYPE=KERNEL + -DVERSION_FILE=${ZEPHYR_BASE}/VERSION + -DKERNEL_VERSION_CUSTOMIZATION="${KERNEL_VERSION_CUSTOMIZATION}" + ${build_version_argument} + -P ${ZEPHYR_BASE}/cmake/gen_version_h.cmake + DEPENDS ${ZEPHYR_BASE}/VERSION ${git_dependency} +) + +if(EXISTS ${APPLICATION_SOURCE_DIR}/VERSION) + add_custom_command( + OUTPUT ${PROJECT_BINARY_DIR}/include/generated/app_version.h + COMMAND ${CMAKE_COMMAND} -DZEPHYR_BASE=${ZEPHYR_BASE} + -DOUT_FILE=${PROJECT_BINARY_DIR}/include/generated/app_version.h + -DVERSION_TYPE=APP + -DVERSION_FILE=${APPLICATION_SOURCE_DIR}/VERSION + -DAPP_VERSION_CUSTOMIZATION="${APP_VERSION_CUSTOMIZATION}" + ${build_version_argument} + -P ${ZEPHYR_BASE}/cmake/gen_version_h.cmake + DEPENDS ${APPLICATION_SOURCE_DIR}/VERSION ${git_dependency} + ) +endif() + get_property(LIBC_LINK_LIBRARIES TARGET zephyr_interface PROPERTY LIBC_LINK_LIBRARIES) zephyr_link_libraries(${LIBC_LINK_LIBRARIES}) @@ -1820,8 +1824,15 @@ endif() # Generate and use MCUboot related artifacts as needed. if(CONFIG_BOOTLOADER_MCUBOOT) get_target_property(signing_script zephyr_property_target SIGNING_SCRIPT) + if(NOT signing_script) - set_target_properties(zephyr_property_target PROPERTIES SIGNING_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/cmake/mcuboot.cmake) + zephyr_get(signing_script VAR SIGNING_SCRIPT SYSBUILD) + + if(signing_script) + set_target_properties(zephyr_property_target PROPERTIES SIGNING_SCRIPT ${signing_script}) + else() + set_target_properties(zephyr_property_target PROPERTIES SIGNING_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/cmake/mcuboot.cmake) + endif() endif() endif() diff --git a/CODEOWNERS b/CODEOWNERS index 25cf3791114..a670048c779 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -14,6 +14,7 @@ # * @galak @nashif /.github/ @nashif @stephanosio +/.github/test-spec.yml @nrfconnect/ncs-test-leads /.github/workflows/ @galak @nashif /MAINTAINERS.yml @MaureenHelm /arch/arc/ @abrodkin @ruuddw @evgeniy-paltsev diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 00000000000..3b9cf002239 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,5 @@ +@Library("CI_LIB") _ + +def pipeline = new ncs.sdk_zephyr.Main() + +pipeline.run(JOB_NAME) diff --git a/MAINTAINERS.yml b/MAINTAINERS.yml index 8d581406981..08ad5c8d61b 100644 --- a/MAINTAINERS.yml +++ b/MAINTAINERS.yml @@ -611,7 +611,6 @@ Documentation: - doc/images/Zephyr-Kite-in-tree.png - doc/index-tex.rst - doc/index.rst - - doc/kconfig.rst - doc/known-warnings.txt - doc/templates/sample.tmpl - doc/templates/board.tmpl diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index e17cf3f9b31..ef9a20d0cc9 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -53,6 +53,16 @@ config ARM_ON_ENTER_CPU_IDLE_HOOK If needed, this hook can be used to prevent the CPU from actually entering sleep by skipping the WFE/WFI instruction. +config ARM_ON_ENTER_CPU_IDLE_PREPARE_HOOK + bool + help + Enables a hook (z_arm_on_enter_cpu_idle_prepare()) that is called when + the CPU is made idle (by k_cpu_idle() or k_cpu_atomic_idle()). + If needed, this hook can prepare data to upcoming call to + z_arm_on_enter_cpu_idle(). The z_arm_on_enter_cpu_idle_prepare differs + from z_arm_on_enter_cpu_idle because it is called before interrupts are + disabled. + config ARM_ON_EXIT_CPU_IDLE bool help diff --git a/arch/arm/core/aarch32/Kconfig b/arch/arm/core/aarch32/Kconfig index a14bcd0cb62..c8a48cb7f11 100644 --- a/arch/arm/core/aarch32/Kconfig +++ b/arch/arm/core/aarch32/Kconfig @@ -264,17 +264,11 @@ choice config FP_HARDABI bool "Floating point Hard ABI" - # TF-M build system does not build the NS app and libraries correctly with Hard ABI. - # This limitation should be removed in the next TF-M synchronization. - depends on !TFM_BUILD_NS - depends on !(BUILD_WITH_TFM && !TFM_IPC) help This option selects the Floating point ABI in which hardware floating point instructions are generated and uses FPU-specific calling conventions. - Note: When building with TF-M enabled only the IPC mode is supported. - config FP_SOFTABI bool "Floating point Soft ABI" help diff --git a/arch/arm/core/aarch32/cpu_idle.S b/arch/arm/core/aarch32/cpu_idle.S index 8164959ab29..95e37917180 100644 --- a/arch/arm/core/aarch32/cpu_idle.S +++ b/arch/arm/core/aarch32/cpu_idle.S @@ -85,16 +85,24 @@ _skip_\@: .endm SECTION_FUNC(TEXT, arch_cpu_idle) -#ifdef CONFIG_TRACING +#if defined(CONFIG_TRACING) || \ + defined(CONFIG_ARM_ON_ENTER_CPU_IDLE_PREPARE_HOOK) push {r0, lr} + +#ifdef CONFIG_TRACING bl sys_trace_idle +#endif +#ifdef CONFIG_ARM_ON_ENTER_CPU_IDLE_PREPARE_HOOK + bl z_arm_on_enter_cpu_idle_prepare +#endif + #if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) pop {r0, r1} mov lr, r1 #else pop {r0, lr} #endif /* CONFIG_ARMV6_M_ARMV8_M_BASELINE */ -#endif /* CONFIG_TRACING */ +#endif #if defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) /* @@ -138,17 +146,24 @@ SECTION_FUNC(TEXT, arch_cpu_idle) bx lr SECTION_FUNC(TEXT, arch_cpu_atomic_idle) -#ifdef CONFIG_TRACING +#if defined(CONFIG_TRACING) || \ + defined(CONFIG_ARM_ON_ENTER_CPU_IDLE_PREPARE_HOOK) push {r0, lr} + +#ifdef CONFIG_TRACING bl sys_trace_idle +#endif +#ifdef CONFIG_ARM_ON_ENTER_CPU_IDLE_PREPARE_HOOK + bl z_arm_on_enter_cpu_idle_prepare +#endif + #if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) pop {r0, r1} mov lr, r1 #else pop {r0, lr} #endif /* CONFIG_ARMV6_M_ARMV8_M_BASELINE */ -#endif /* CONFIG_TRACING */ - +#endif /* * Lock PRIMASK while sleeping: wfe will still get interrupted by * incoming interrupts but the CPU will not service them right away. diff --git a/boards/arm/96b_nitrogen/96b_nitrogen.dts b/boards/arm/96b_nitrogen/96b_nitrogen.dts index d5bac1cd6d3..69148a032d7 100644 --- a/boards/arm/96b_nitrogen/96b_nitrogen.dts +++ b/boards/arm/96b_nitrogen/96b_nitrogen.dts @@ -58,6 +58,10 @@ }; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/96b_nitrogen/96b_nitrogen_defconfig b/boards/arm/96b_nitrogen/96b_nitrogen_defconfig index 500c469813b..e804b43da5c 100644 --- a/boards/arm/96b_nitrogen/96b_nitrogen_defconfig +++ b/boards/arm/96b_nitrogen/96b_nitrogen_defconfig @@ -16,7 +16,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/acn52832/acn52832.dts b/boards/arm/acn52832/acn52832.dts index 1e01221f652..ca9f5046fba 100644 --- a/boards/arm/acn52832/acn52832.dts +++ b/boards/arm/acn52832/acn52832.dts @@ -45,6 +45,10 @@ }; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/acn52832/acn52832_defconfig b/boards/arm/acn52832/acn52832_defconfig index 6b1ae1a7ba7..2bb13882f8c 100644 --- a/boards/arm/acn52832/acn52832_defconfig +++ b/boards/arm/acn52832/acn52832_defconfig @@ -17,7 +17,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# Enable P0_21 as RST -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/adafruit_feather_nrf52840/adafruit_feather_nrf52840.dts b/boards/arm/adafruit_feather_nrf52840/adafruit_feather_nrf52840.dts index 5024f43fd81..284a4d24542 100644 --- a/boards/arm/adafruit_feather_nrf52840/adafruit_feather_nrf52840.dts +++ b/boards/arm/adafruit_feather_nrf52840/adafruit_feather_nrf52840.dts @@ -61,6 +61,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/adafruit_feather_nrf52840/adafruit_feather_nrf52840_defconfig b/boards/arm/adafruit_feather_nrf52840/adafruit_feather_nrf52840_defconfig index 8d892db30f2..1f42d1a0aa4 100644 --- a/boards/arm/adafruit_feather_nrf52840/adafruit_feather_nrf52840_defconfig +++ b/boards/arm/adafruit_feather_nrf52840/adafruit_feather_nrf52840_defconfig @@ -17,7 +17,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/adafruit_itsybitsy_nrf52840/adafruit_itsybitsy_nrf52840.dts b/boards/arm/adafruit_itsybitsy_nrf52840/adafruit_itsybitsy_nrf52840.dts index 6e827388aef..103e18d8328 100644 --- a/boards/arm/adafruit_itsybitsy_nrf52840/adafruit_itsybitsy_nrf52840.dts +++ b/boards/arm/adafruit_itsybitsy_nrf52840/adafruit_itsybitsy_nrf52840.dts @@ -55,6 +55,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/adafruit_itsybitsy_nrf52840/adafruit_itsybitsy_nrf52840_defconfig b/boards/arm/adafruit_itsybitsy_nrf52840/adafruit_itsybitsy_nrf52840_defconfig index bc27efcdd29..e5a88c27fb3 100644 --- a/boards/arm/adafruit_itsybitsy_nrf52840/adafruit_itsybitsy_nrf52840_defconfig +++ b/boards/arm/adafruit_itsybitsy_nrf52840/adafruit_itsybitsy_nrf52840_defconfig @@ -17,8 +17,6 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y CONFIG_PINCTRL=y # Flashing diff --git a/boards/arm/arduino_nano_33_ble/arduino_nano_33_ble-common.dtsi b/boards/arm/arduino_nano_33_ble/arduino_nano_33_ble-common.dtsi index 60c0d63920c..200db7f94c7 100644 --- a/boards/arm/arduino_nano_33_ble/arduino_nano_33_ble-common.dtsi +++ b/boards/arm/arduino_nano_33_ble/arduino_nano_33_ble-common.dtsi @@ -177,6 +177,10 @@ arduino_spi: &spi2 { pinctrl-names = "default", "sleep"; }; +&uicr { + gpio-as-nreset; +}; + &gpio0 { status = "okay"; }; diff --git a/boards/arm/arduino_nano_33_ble/arduino_nano_33_ble_defconfig b/boards/arm/arduino_nano_33_ble/arduino_nano_33_ble_defconfig index 2d91725aaf9..dcdc0c8a836 100644 --- a/boards/arm/arduino_nano_33_ble/arduino_nano_33_ble_defconfig +++ b/boards/arm/arduino_nano_33_ble/arduino_nano_33_ble_defconfig @@ -20,7 +20,4 @@ CONFIG_UART_CONSOLE=y CONFIG_BOOTLOADER_BOSSA=y CONFIG_BOOTLOADER_BOSSA_LEGACY=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/arduino_nano_33_ble/arduino_nano_33_ble_sense_defconfig b/boards/arm/arduino_nano_33_ble/arduino_nano_33_ble_sense_defconfig index 8ef8ece861c..4604c6bf386 100644 --- a/boards/arm/arduino_nano_33_ble/arduino_nano_33_ble_sense_defconfig +++ b/boards/arm/arduino_nano_33_ble/arduino_nano_33_ble_sense_defconfig @@ -20,7 +20,4 @@ CONFIG_UART_CONSOLE=y CONFIG_BOOTLOADER_BOSSA=y CONFIG_BOOTLOADER_BOSSA_LEGACY=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/arduino_nicla_sense_me/arduino_nicla_sense_me.dts b/boards/arm/arduino_nicla_sense_me/arduino_nicla_sense_me.dts index ada4c76441c..2329853a1a3 100644 --- a/boards/arm/arduino_nicla_sense_me/arduino_nicla_sense_me.dts +++ b/boards/arm/arduino_nicla_sense_me/arduino_nicla_sense_me.dts @@ -45,6 +45,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/arduino_nicla_sense_me/arduino_nicla_sense_me_defconfig b/boards/arm/arduino_nicla_sense_me/arduino_nicla_sense_me_defconfig index 396d37d36e0..c2f2b95cc72 100644 --- a/boards/arm/arduino_nicla_sense_me/arduino_nicla_sense_me_defconfig +++ b/boards/arm/arduino_nicla_sense_me/arduino_nicla_sense_me_defconfig @@ -18,8 +18,5 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - # enable pin controller CONFIG_PINCTRL=y diff --git a/boards/arm/b_u585i_iot02a/Kconfig.defconfig b/boards/arm/b_u585i_iot02a/Kconfig.defconfig index 93748d0dcf2..84e7bdb320a 100644 --- a/boards/arm/b_u585i_iot02a/Kconfig.defconfig +++ b/boards/arm/b_u585i_iot02a/Kconfig.defconfig @@ -20,4 +20,15 @@ config USE_DT_CODE_PARTITION config SYS_CLOCK_TICKS_PER_SEC default 4096 if STM32_LPTIM_TIMER +if BUILD_WITH_TFM + +# Initial Attestation key provisioned by the BL1 bootloader +config TFM_INITIAL_ATTESTATION_KEY + default y + +config TFM_DUMMY_PROVISIONING + default n + +endif # BUILD_WITH_TFM + endif # BOARD_B_U585I_IOT02A diff --git a/boards/arm/bl5340_dvk/Kconfig.defconfig b/boards/arm/bl5340_dvk/Kconfig.defconfig index 38a06c8b91d..e033f718dba 100644 --- a/boards/arm/bl5340_dvk/Kconfig.defconfig +++ b/boards/arm/bl5340_dvk/Kconfig.defconfig @@ -13,21 +13,6 @@ config BOARD config I2C default GPIO || DAC -# By default, if we build for a Non-Secure version of the board, -# enable building with TF-M as the Secure Execution Environment. -config BUILD_WITH_TFM - default y if BOARD_BL5340_DVK_CPUAPP_NS - -if BUILD_WITH_TFM - -# By default, if we build with TF-M, instruct build system to -# flash the combined TF-M (Secure) & Zephyr (Non Secure) image -config TFM_FLASH_MERGED_BINARY - bool - default y - -endif # BUILD_WITH_TFM - # Code Partition: # # For the secure version of the board the firmware is linked at the beginning diff --git a/boards/arm/bl652_dvk/bl652_dvk.dts b/boards/arm/bl652_dvk/bl652_dvk.dts index 5569f1323c0..f2cffbd819b 100644 --- a/boards/arm/bl652_dvk/bl652_dvk.dts +++ b/boards/arm/bl652_dvk/bl652_dvk.dts @@ -68,6 +68,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/bl652_dvk/bl652_dvk_defconfig b/boards/arm/bl652_dvk/bl652_dvk_defconfig index 1ca971e2546..5302fa8d53f 100644 --- a/boards/arm/bl652_dvk/bl652_dvk_defconfig +++ b/boards/arm/bl652_dvk/bl652_dvk_defconfig @@ -21,9 +21,6 @@ CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y CONFIG_RTT_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - # 32kHz clock source CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC=y CONFIG_CLOCK_CONTROL_NRF_K32SRC_500PPM=y diff --git a/boards/arm/bl653_dvk/bl653_dvk.dts b/boards/arm/bl653_dvk/bl653_dvk.dts index ceb91aeb96f..ba8fa3658d2 100644 --- a/boards/arm/bl653_dvk/bl653_dvk.dts +++ b/boards/arm/bl653_dvk/bl653_dvk.dts @@ -91,6 +91,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/bl653_dvk/bl653_dvk_defconfig b/boards/arm/bl653_dvk/bl653_dvk_defconfig index 4933055c93c..83b73ef4435 100644 --- a/boards/arm/bl653_dvk/bl653_dvk_defconfig +++ b/boards/arm/bl653_dvk/bl653_dvk_defconfig @@ -20,9 +20,6 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - # 32kHz clock source CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC=y CONFIG_CLOCK_CONTROL_NRF_K32SRC_500PPM=y diff --git a/boards/arm/bl654_dvk/bl654_dvk.dts b/boards/arm/bl654_dvk/bl654_dvk.dts index b99435696d1..7ede69427de 100644 --- a/boards/arm/bl654_dvk/bl654_dvk.dts +++ b/boards/arm/bl654_dvk/bl654_dvk.dts @@ -91,6 +91,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/bl654_dvk/bl654_dvk_defconfig b/boards/arm/bl654_dvk/bl654_dvk_defconfig index aa2238cda87..976495f7e88 100644 --- a/boards/arm/bl654_dvk/bl654_dvk_defconfig +++ b/boards/arm/bl654_dvk/bl654_dvk_defconfig @@ -21,9 +21,6 @@ CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y CONFIG_RTT_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - # 32kHz clock source CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC=y CONFIG_CLOCK_CONTROL_NRF_K32SRC_500PPM=y diff --git a/boards/arm/bl654_sensor_board/bl654_sensor_board.dts b/boards/arm/bl654_sensor_board/bl654_sensor_board.dts index 1c2949ee2e8..c43354f2705 100644 --- a/boards/arm/bl654_sensor_board/bl654_sensor_board.dts +++ b/boards/arm/bl654_sensor_board/bl654_sensor_board.dts @@ -56,6 +56,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/bl654_sensor_board/bl654_sensor_board_defconfig b/boards/arm/bl654_sensor_board/bl654_sensor_board_defconfig index 259fac9d309..e92c2c4005f 100644 --- a/boards/arm/bl654_sensor_board/bl654_sensor_board_defconfig +++ b/boards/arm/bl654_sensor_board/bl654_sensor_board_defconfig @@ -17,9 +17,6 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# Additional board options -CONFIG_GPIO_AS_PINRESET=y - # 32KHz clock source CONFIG_CLOCK_CONTROL_NRF_K32SRC_XTAL=y CONFIG_CLOCK_CONTROL_NRF_K32SRC_150PPM=y diff --git a/boards/arm/bl654_usb/bl654_usb.dts b/boards/arm/bl654_usb/bl654_usb.dts index 2cc6850a468..80600290dbf 100644 --- a/boards/arm/bl654_usb/bl654_usb.dts +++ b/boards/arm/bl654_usb/bl654_usb.dts @@ -47,6 +47,10 @@ }; }; +&uicr { + gpio-as-nreset; +}; + &gpio0 { status = "okay"; }; diff --git a/boards/arm/bl654_usb/bl654_usb_defconfig b/boards/arm/bl654_usb/bl654_usb_defconfig index 59c26ddb4d7..983150c35d9 100644 --- a/boards/arm/bl654_usb/bl654_usb_defconfig +++ b/boards/arm/bl654_usb/bl654_usb_defconfig @@ -19,9 +19,6 @@ CONFIG_GPIO=y # Enable USB CONFIG_USB_DEVICE_STACK=y -# Additional board options -CONFIG_GPIO_AS_PINRESET=y - # 32KHz clock source CONFIG_CLOCK_CONTROL_NRF_K32SRC_XTAL=y CONFIG_CLOCK_CONTROL_NRF_K32SRC_150PPM=y diff --git a/boards/arm/blueclover_plt_demo_v2_nrf52832/blueclover_plt_demo_v2_nrf52832.dts b/boards/arm/blueclover_plt_demo_v2_nrf52832/blueclover_plt_demo_v2_nrf52832.dts index cf7dd7a52de..4c49979bb0a 100644 --- a/boards/arm/blueclover_plt_demo_v2_nrf52832/blueclover_plt_demo_v2_nrf52832.dts +++ b/boards/arm/blueclover_plt_demo_v2_nrf52832/blueclover_plt_demo_v2_nrf52832.dts @@ -48,6 +48,10 @@ }; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status ="okay"; }; diff --git a/boards/arm/blueclover_plt_demo_v2_nrf52832/blueclover_plt_demo_v2_nrf52832_defconfig b/boards/arm/blueclover_plt_demo_v2_nrf52832/blueclover_plt_demo_v2_nrf52832_defconfig index 52f089ba0d6..301a23af362 100644 --- a/boards/arm/blueclover_plt_demo_v2_nrf52832/blueclover_plt_demo_v2_nrf52832_defconfig +++ b/boards/arm/blueclover_plt_demo_v2_nrf52832/blueclover_plt_demo_v2_nrf52832_defconfig @@ -24,7 +24,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/bt510/bt510.dts b/boards/arm/bt510/bt510.dts index 74818be0d7c..984b098e5d5 100644 --- a/boards/arm/bt510/bt510.dts +++ b/boards/arm/bt510/bt510.dts @@ -72,6 +72,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/bt510/bt510_defconfig b/boards/arm/bt510/bt510_defconfig index fd86ea3f0f2..eb769cc9465 100644 --- a/boards/arm/bt510/bt510_defconfig +++ b/boards/arm/bt510/bt510_defconfig @@ -17,9 +17,6 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# Additional board options -CONFIG_GPIO_AS_PINRESET=y - # 32KHz clock source CONFIG_CLOCK_CONTROL_NRF_K32SRC_XTAL=y CONFIG_CLOCK_CONTROL_NRF_K32SRC_150PPM=y diff --git a/boards/arm/bt610/bt610.dts b/boards/arm/bt610/bt610.dts index 3c047a50fcd..489401a5fb3 100644 --- a/boards/arm/bt610/bt610.dts +++ b/boards/arm/bt610/bt610.dts @@ -143,6 +143,11 @@ status = "okay"; }; +&uicr { + nfct-pins-as-gpios; + gpio-as-nreset; +}; + &gpio0 { status = "okay"; }; diff --git a/boards/arm/bt610/bt610_defconfig b/boards/arm/bt610/bt610_defconfig index 64f0fcb85ed..d1d7226fade 100644 --- a/boards/arm/bt610/bt610_defconfig +++ b/boards/arm/bt610/bt610_defconfig @@ -9,7 +9,6 @@ CONFIG_ARM_MPU=y # Enable GPIO CONFIG_GPIO=y -CONFIG_NFCT_PINS_AS_GPIOS=y # Enable uart driver CONFIG_SERIAL=y @@ -18,9 +17,6 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# Additional board options -CONFIG_GPIO_AS_PINRESET=y - # Enable hardware stack protection CONFIG_HW_STACK_PROTECTION=y diff --git a/boards/arm/circuitdojo_feather_nrf9160/circuitdojo_feather_nrf9160_common.dtsi b/boards/arm/circuitdojo_feather_nrf9160/circuitdojo_feather_nrf9160_common.dtsi index 33fdba4d865..00ce5dcf6c0 100644 --- a/boards/arm/circuitdojo_feather_nrf9160/circuitdojo_feather_nrf9160_common.dtsi +++ b/boards/arm/circuitdojo_feather_nrf9160/circuitdojo_feather_nrf9160_common.dtsi @@ -187,22 +187,19 @@ slot0_partition: partition@10000 { label = "image-0"; }; - slot0_ns_partition: partition@40000 { + slot0_ns_partition: partition@50000 { label = "image-0-nonsecure"; }; slot1_partition: partition@80000 { label = "image-1"; }; - slot1_ns_partition: partition@b0000 { + slot1_ns_partition: partition@c0000 { label = "image-1-nonsecure"; }; - scratch_partition: partition@f0000 { - label = "image-scratch"; - reg = <0x000f0000 0xa000>; - }; - storage_partition: partition@fa000 { + /* 0xf0000 to 0xf7fff reserved for TF-M partitions */ + storage_partition: partition@f8000 { label = "storage"; - reg = <0x000fa000 0x00006000>; + reg = <0x000f8000 0x00008000>; }; }; }; diff --git a/boards/arm/circuitdojo_feather_nrf9160/circuitdojo_feather_nrf9160_ns.yaml b/boards/arm/circuitdojo_feather_nrf9160/circuitdojo_feather_nrf9160_ns.yaml index c358a1d87fe..56905972e9f 100644 --- a/boards/arm/circuitdojo_feather_nrf9160/circuitdojo_feather_nrf9160_ns.yaml +++ b/boards/arm/circuitdojo_feather_nrf9160/circuitdojo_feather_nrf9160_ns.yaml @@ -7,7 +7,7 @@ toolchain: - xtools - zephyr ram: 128 -flash: 256 +flash: 192 supported: - i2c - pwm diff --git a/boards/arm/circuitdojo_feather_nrf9160/circuitdojo_feather_nrf9160_partition_conf.dtsi b/boards/arm/circuitdojo_feather_nrf9160/circuitdojo_feather_nrf9160_partition_conf.dtsi index e8cb6fc586e..007975132d6 100644 --- a/boards/arm/circuitdojo_feather_nrf9160/circuitdojo_feather_nrf9160_partition_conf.dtsi +++ b/boards/arm/circuitdojo_feather_nrf9160/circuitdojo_feather_nrf9160_partition_conf.dtsi @@ -23,19 +23,19 @@ */ &slot0_partition { - reg = <0x00010000 0x30000>; + reg = <0x00010000 0x40000>; }; &slot0_ns_partition { - reg = <0x00040000 0x40000>; + reg = <0x00050000 0x30000>; }; &slot1_partition { - reg = <0x00080000 0x30000>; + reg = <0x00080000 0x40000>; }; &slot1_ns_partition { - reg = <0x000b0000 0x40000>; + reg = <0x000c0000 0x30000>; }; /* Default SRAM planning when building for nRF9160 with diff --git a/boards/arm/contextualelectronics_abc/contextualelectronics_abc.dts b/boards/arm/contextualelectronics_abc/contextualelectronics_abc.dts index ebc866afeff..6f60b1ddfed 100644 --- a/boards/arm/contextualelectronics_abc/contextualelectronics_abc.dts +++ b/boards/arm/contextualelectronics_abc/contextualelectronics_abc.dts @@ -25,6 +25,10 @@ }; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/contextualelectronics_abc/contextualelectronics_abc_defconfig b/boards/arm/contextualelectronics_abc/contextualelectronics_abc_defconfig index bb841469339..7a3055b9c82 100644 --- a/boards/arm/contextualelectronics_abc/contextualelectronics_abc_defconfig +++ b/boards/arm/contextualelectronics_abc/contextualelectronics_abc_defconfig @@ -20,7 +20,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# Additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/decawave_dwm1001_dev/decawave_dwm1001_dev.dts b/boards/arm/decawave_dwm1001_dev/decawave_dwm1001_dev.dts index 75510faf4a6..a1c415fc299 100644 --- a/boards/arm/decawave_dwm1001_dev/decawave_dwm1001_dev.dts +++ b/boards/arm/decawave_dwm1001_dev/decawave_dwm1001_dev.dts @@ -84,6 +84,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/decawave_dwm1001_dev/decawave_dwm1001_dev_defconfig b/boards/arm/decawave_dwm1001_dev/decawave_dwm1001_dev_defconfig index 89d31bfd45b..4cb6022e7ae 100644 --- a/boards/arm/decawave_dwm1001_dev/decawave_dwm1001_dev_defconfig +++ b/boards/arm/decawave_dwm1001_dev/decawave_dwm1001_dev_defconfig @@ -21,7 +21,4 @@ CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y CONFIG_RTT_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/degu_evk/degu_evk.dts b/boards/arm/degu_evk/degu_evk.dts index caa134752e0..eb3329726e9 100644 --- a/boards/arm/degu_evk/degu_evk.dts +++ b/boards/arm/degu_evk/degu_evk.dts @@ -92,6 +92,10 @@ status ="okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status ="okay"; }; diff --git a/boards/arm/degu_evk/degu_evk_defconfig b/boards/arm/degu_evk/degu_evk_defconfig index 7b9d7737481..e003438c3dc 100644 --- a/boards/arm/degu_evk/degu_evk_defconfig +++ b/boards/arm/degu_evk/degu_evk_defconfig @@ -17,7 +17,6 @@ CONFIG_USB_DEVICE_STACK=y # additional board options CONFIG_GPIO=y -CONFIG_GPIO_AS_PINRESET=y # required to enable 3V3 power rail and Vin1 monitor CONFIG_REGULATOR=y diff --git a/boards/arm/ebyte_e73_tbb_nrf52832/ebyte_e73_tbb_nrf52832.dts b/boards/arm/ebyte_e73_tbb_nrf52832/ebyte_e73_tbb_nrf52832.dts index 9fa240cb5ae..31eb0cc7524 100644 --- a/boards/arm/ebyte_e73_tbb_nrf52832/ebyte_e73_tbb_nrf52832.dts +++ b/boards/arm/ebyte_e73_tbb_nrf52832/ebyte_e73_tbb_nrf52832.dts @@ -75,6 +75,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/ebyte_e73_tbb_nrf52832/ebyte_e73_tbb_nrf52832_defconfig b/boards/arm/ebyte_e73_tbb_nrf52832/ebyte_e73_tbb_nrf52832_defconfig index 3e1778de525..05acf2ca945 100644 --- a/boards/arm/ebyte_e73_tbb_nrf52832/ebyte_e73_tbb_nrf52832_defconfig +++ b/boards/arm/ebyte_e73_tbb_nrf52832/ebyte_e73_tbb_nrf52832_defconfig @@ -23,7 +23,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# Additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/holyiot_yj16019/Kconfig.defconfig b/boards/arm/holyiot_yj16019/Kconfig.defconfig index 99373ad286a..de2d63ad7c8 100644 --- a/boards/arm/holyiot_yj16019/Kconfig.defconfig +++ b/boards/arm/holyiot_yj16019/Kconfig.defconfig @@ -8,9 +8,6 @@ if BOARD_HOLYIOT_YJ16019 config BOARD default "holyiot_yj16019" -config GPIO_AS_PINRESET - default n - config BT_CTLR default BT diff --git a/boards/arm/mg100/mg100.dts b/boards/arm/mg100/mg100.dts index 512ca901010..0d33fd32151 100644 --- a/boards/arm/mg100/mg100.dts +++ b/boards/arm/mg100/mg100.dts @@ -65,6 +65,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/mg100/mg100_defconfig b/boards/arm/mg100/mg100_defconfig index 93f55f3d0a4..bb7d09dabf2 100644 --- a/boards/arm/mg100/mg100_defconfig +++ b/boards/arm/mg100/mg100_defconfig @@ -18,9 +18,6 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# Additional board options -CONFIG_GPIO_AS_PINRESET=y - # 32KHz clock source CONFIG_CLOCK_CONTROL_NRF_K32SRC_XTAL=y CONFIG_CLOCK_CONTROL_NRF_K32SRC_150PPM=y diff --git a/boards/arm/mimxrt1170_evk/doc/index.rst b/boards/arm/mimxrt1170_evk/doc/index.rst index 90f192c3870..1582861ffc9 100644 --- a/boards/arm/mimxrt1170_evk/doc/index.rst +++ b/boards/arm/mimxrt1170_evk/doc/index.rst @@ -1,4 +1,4 @@ -.. _mimxrt1170_evk: +.. _mimxrt1170_evk: NXP MIMXRT1170-EVK/EVKB ####################### @@ -9,7 +9,7 @@ Overview The dual core i.MX RT1170 runs on the Cortex-M7 core at 1 GHz and on the Cortex-M4 at 400 MHz. The i.MX RT1170 MCU offers support over a wide temperature range and is qualified for consumer, industrial and automotive markets. Zephyr -supports the initial revision of this EVK, as well as EVK rev B. +supports the initial revision of this EVK, as well as rev EVKB. .. image:: mimxrt1170_evk.jpg :align: center @@ -59,7 +59,7 @@ Hardware - Debug - JTAG 20-pin connector - - OpenSDA with DAPLink + - on-board debugger - Sensor @@ -90,16 +90,17 @@ This platform has the following external memories: | Device | Controller | Status | +====================+============+=====================================+ | W9825G6KH | SEMC | Enabled via device configuration | -| | | data block, which sets up SEMC at | -| | | boot time | +| SDRAM | | data (DCD) block, which sets up | +| | | the SEMC at boot time | +--------------------+------------+-------------------------------------+ -| IS25WP128 | FLEXSPI | Enabled via flash configurationn | -| (RT1170 EVK) | | block, which sets up FLEXSPI at | -| | | boot time. | +| IS25WP128 | FLEXSPI | Enabled via flash configuration | +| QSPI flash | | block (FCB), which sets up the | +| (RT1170 EVK) | | FLEXSPI at boot time. | +--------------------+------------+-------------------------------------+ -| W25Q512NWEIQ | FLEXSPI | Enabled via flash configurationn | -| (RT1170 EVKB) | | block, which sets up FLEXSPI at | -| | | boot time. Supported for XIP only. | +| W25Q512NWEIQ | FLEXSPI | Enabled via flash configuration | +| QSPI flash | | block (FCB), which sets up the | +| (RT1170 EVKB) | | FLEXSPI at boot time. Supported for | +| | | XIP only. | +--------------------+------------+-------------------------------------+ Supported Features @@ -123,6 +124,8 @@ RT1170 EVKB (`mimxrt1170_evkb_cm7/cm4`) +-----------+------------+-------------------------------------+-----------------+-----------------+ | COUNTER | on-chip | gpt | Supported | Supported | +-----------+------------+-------------------------------------+-----------------+-----------------+ +| TIMER | on-chip | gpt | Supported | Supported | ++-----------+------------+-------------------------------------+-----------------+-----------------+ | CAN | on-chip | flexcan | Supported (M7) | Supported (M7) | +-----------+------------+-------------------------------------+-----------------+-----------------+ | SPI | on-chip | spi | Supported (M7) | Supported | @@ -139,11 +142,10 @@ RT1170 EVKB (`mimxrt1170_evkb_cm7/cm4`) +-----------+------------+-------------------------------------+-----------------+-----------------+ | DMA | on-chip | dma | Supported | Supported | +-----------+------------+-------------------------------------+-----------------+-----------------+ -| GPT | on-chip | gpt | Supported | Supported | -+-----------+------------+-------------------------------------+-----------------+-----------------+ | WATCHDOG | on-chip | watchdog | Supported (M7) | Supported (M7) | +-----------+------------+-------------------------------------+-----------------+-----------------+ -| ENET | on-chip | ethernet | Supported (M7) | No support | +| ENET | on-chip | ethernet - 10/100M (ENET_QOS or | Supported (M7) | No support | +| ENET1G | | GigE not supported yet) | | | +-----------+------------+-------------------------------------+-----------------+-----------------+ | SAI | on-chip | i2s | Supported | No support | +-----------+------------+-------------------------------------+-----------------+-----------------+ @@ -165,10 +167,9 @@ RT1170 EVKB (`mimxrt1170_evkb_cm7/cm4`) | SDHC | on-chip | SD host controller | Supported (M7) | Supported (M7) | +-----------+------------+-------------------------------------+-----------------+-----------------+ -The default configuration can be found in the defconfig file: +The default configuration can be found in the defconfig files: ``boards/arm/mimxrt1170_evk/mimxrt1170_evk_cm7_defconfig`` - -Other hardware features are not currently supported by the port. +``boards/arm/mimxrt1170_evk/mimxrt1170_evkb_cm7_defconfig`` Connections and I/Os ==================== @@ -310,33 +311,58 @@ secondary core should be placed into a loop, then a debugger can be attached Configuring a Debug Probe ========================= -A debug probe is used for both flashing and debugging the board. This board is -configured by default to use the :ref:`opensda-daplink-onboard-debug-probe`, -however the :ref:`pyocd-debug-host-tools` do not yet support programming the -external flashes on this board so you must reconfigure the board for one of the -following debug probes instead. - -.. _Using J-Link RT1170: +A debug probe is used for both flashing and debugging the board. The on-board +debugger listed below works with the LinkServer runner by default, or can be +reprogrammed with JLink firmware. +- MIMXRT1170-EVKB: :ref:`mcu-link-cmsis-onboard-debug-probe` +- MIMXRT1170-EVK: :ref:`opensda-daplink-onboard-debug-probe` Using J-Link ---------------------------------- +------------ -Install the :ref:`jlink-debug-host-tools` and make sure they are in your search -path. +JLink is the default runner for this board. Install the +:ref:`jlink-debug-host-tools` and make sure they are in your search path. There are two options: the onboard debug circuit can be updated with Segger J-Link firmware, or :ref:`jlink-external-debug-probe` can be attached to the -EVK. See `Using J-Link with MIMXRT1160-EVK or MIMXRT1170-EVK`_ for more details. +EVK. See `Using J-Link with MIMXRT1170-EVKB`_ or +`Using J-Link with MIMXRT1160-EVK or MIMXRT1170-EVK`_ for more details. + +Using LinkServer +---------------- + +Known limitations with LinkServer and these boards include: +- ``west debug`` does not yet work correctly, and the application image is not +properly written to the memory. `NXP MCUXpresso for Visual Studio Code`_ +can be used to debug Zephyr applications with LinkServer. +- ``west flash`` will not write images to non-flash locations. The flash +command only works when all data in the image is written to flash memory +regions. + +Install the :ref:`linkserver-debug-host-tools` and make sure they are in your +search path. LinkServer works with the default CMSIS-DAP firmware included in +the on-board debugger. + +Use the ``-r linkserver`` option with West to use the LinkServer runner. + +.. code-block:: console + + west flash -r linkserver + Configuring a Console ===================== -Regardless of your choice in debug probe, we will use the OpenSDA -microcontroller as a usb-to-serial adapter for the serial console. Check that -jumpers J5 and J8 are **on** (they are on by default when boards ship from -the factory) to connect UART signals to the OpenSDA microcontroller. +We will use the on-board debugger +microcontroller as a usb-to-serial adapter for the serial console. The following +jumper settings are default on these boards, and are required to connect the +UART signals to the USB bridge circuit: +- MIMXRT1170-EVKB: JP2 open (default) +- MIMXRT1170-EVK: J31 and J32 shorted (default) -Connect a USB cable from your PC to J11. +Connect a USB cable from your PC to the on-board debugger USB port: +- MIMXRT1170-EVKB: J86 +- MIMXRT1170-EVK: J11 Use the following settings with your serial terminal of choice (minicom, putty, etc.): @@ -351,7 +377,7 @@ Flashing Here is an example for the :ref:`hello_world` application. -Before power on the board, make sure SW1 is set to 0001b +Before powering the board, make sure SW1 is set to 0001b .. zephyr-app-commands:: :zephyr-app: samples/hello_world @@ -364,7 +390,7 @@ see the following message in the terminal: .. code-block:: console - ***** Booting Zephyr OS v2.4.0-xxxx-xxxxxxxxxxxxx ***** + ***** Booting Zephyr OS v3.4.0-xxxx-xxxxxxxxxxxxx ***** Hello World! mimxrt1170_evk_cm7 Debugging @@ -382,7 +408,7 @@ should see the following message in the terminal: .. code-block:: console - ***** Booting Zephyr OS v2.4.0-xxxx-xxxxxxxxxxxxx ***** + ***** Booting Zephyr OS v3.4.0-xxxx-xxxxxxxxxxxxx ***** Hello World! mimxrt1170_evk_cm7 .. _MIMXRT1170-EVK Website: @@ -403,5 +429,11 @@ should see the following message in the terminal: .. _Using J-Link with MIMXRT1160-EVK or MIMXRT1170-EVK: https://community.nxp.com/t5/i-MX-RT-Knowledge-Base/Using-J-Link-with-MIMXRT1160-EVK-or-MIMXRT1170-EVK/ta-p/1529760 +.. _Using J-Link with MIMXRT1170-EVKB: + https://community.nxp.com/t5/i-MX-RT-Knowledge-Base/Using-J-Link-with-MIMXRT1170-EVKB/ta-p/1715138 + .. _AN13264: https://www.nxp.com/docs/en/application-note/AN13264.pdf + +.. _NXP MCUXpresso for Visual Studio Code: + https://www.nxp.com/design/software/development-software/mcuxpresso-software-and-tools-/mcuxpresso-for-visual-studio-code:MCUXPRESSO-VSC diff --git a/boards/arm/nrf21540dk_nrf52840/nrf21540dk_nrf52840.dts b/boards/arm/nrf21540dk_nrf52840/nrf21540dk_nrf52840.dts index 077eb4a1657..af0ea59794c 100644 --- a/boards/arm/nrf21540dk_nrf52840/nrf21540dk_nrf52840.dts +++ b/boards/arm/nrf21540dk_nrf52840/nrf21540dk_nrf52840.dts @@ -150,6 +150,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/nrf21540dk_nrf52840/nrf21540dk_nrf52840_defconfig b/boards/arm/nrf21540dk_nrf52840/nrf21540dk_nrf52840_defconfig index 56d8497b43a..af230adb18a 100644 --- a/boards/arm/nrf21540dk_nrf52840/nrf21540dk_nrf52840_defconfig +++ b/boards/arm/nrf21540dk_nrf52840/nrf21540dk_nrf52840_defconfig @@ -23,7 +23,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/nrf52832_mdk/nrf52832_mdk.dts b/boards/arm/nrf52832_mdk/nrf52832_mdk.dts index 348a5b79745..c51002c6497 100644 --- a/boards/arm/nrf52832_mdk/nrf52832_mdk.dts +++ b/boards/arm/nrf52832_mdk/nrf52832_mdk.dts @@ -86,6 +86,10 @@ }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/nrf52832_mdk/nrf52832_mdk_defconfig b/boards/arm/nrf52832_mdk/nrf52832_mdk_defconfig index 2c283bda97e..8993b59f938 100644 --- a/boards/arm/nrf52832_mdk/nrf52832_mdk_defconfig +++ b/boards/arm/nrf52832_mdk/nrf52832_mdk_defconfig @@ -17,7 +17,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/nrf52833dk_nrf52820/nrf52833dk_nrf52820.dts b/boards/arm/nrf52833dk_nrf52820/nrf52833dk_nrf52820.dts index f70d6649aa8..4942dd6daa0 100644 --- a/boards/arm/nrf52833dk_nrf52820/nrf52833dk_nrf52820.dts +++ b/boards/arm/nrf52833dk_nrf52820/nrf52833dk_nrf52820.dts @@ -99,6 +99,10 @@ clock-prescaler = <8>; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/nrf52833dk_nrf52820/nrf52833dk_nrf52820_defconfig b/boards/arm/nrf52833dk_nrf52820/nrf52833dk_nrf52820_defconfig index 9bbc4da98cb..9ef66860ab3 100644 --- a/boards/arm/nrf52833dk_nrf52820/nrf52833dk_nrf52820_defconfig +++ b/boards/arm/nrf52833dk_nrf52820/nrf52833dk_nrf52820_defconfig @@ -23,7 +23,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/nrf52833dk_nrf52833/nrf52833dk_nrf52833.dts b/boards/arm/nrf52833dk_nrf52833/nrf52833dk_nrf52833.dts index 0d1725d5bbb..b488b5d1ff3 100644 --- a/boards/arm/nrf52833dk_nrf52833/nrf52833dk_nrf52833.dts +++ b/boards/arm/nrf52833dk_nrf52833/nrf52833dk_nrf52833.dts @@ -127,6 +127,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/nrf52833dk_nrf52833/nrf52833dk_nrf52833_defconfig b/boards/arm/nrf52833dk_nrf52833/nrf52833dk_nrf52833_defconfig index 7fe9f4e480c..9cbee72b2e7 100644 --- a/boards/arm/nrf52833dk_nrf52833/nrf52833dk_nrf52833_defconfig +++ b/boards/arm/nrf52833dk_nrf52833/nrf52833dk_nrf52833_defconfig @@ -23,7 +23,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/nrf52840_blip/nrf52840_blip.dts b/boards/arm/nrf52840_blip/nrf52840_blip.dts index 112d22799bc..e641c60bd1f 100644 --- a/boards/arm/nrf52840_blip/nrf52840_blip.dts +++ b/boards/arm/nrf52840_blip/nrf52840_blip.dts @@ -66,6 +66,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/nrf52840_blip/nrf52840_blip_defconfig b/boards/arm/nrf52840_blip/nrf52840_blip_defconfig index 82c0146ecf1..811a88de939 100644 --- a/boards/arm/nrf52840_blip/nrf52840_blip_defconfig +++ b/boards/arm/nrf52840_blip/nrf52840_blip_defconfig @@ -17,7 +17,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/nrf52840_mdk/nrf52840_mdk.dts b/boards/arm/nrf52840_mdk/nrf52840_mdk.dts index 0ed4addb069..2439b0c2aca 100644 --- a/boards/arm/nrf52840_mdk/nrf52840_mdk.dts +++ b/boards/arm/nrf52840_mdk/nrf52840_mdk.dts @@ -90,6 +90,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/nrf52840_mdk/nrf52840_mdk_defconfig b/boards/arm/nrf52840_mdk/nrf52840_mdk_defconfig index f0ed2a2fbad..e4c79a2bf88 100644 --- a/boards/arm/nrf52840_mdk/nrf52840_mdk_defconfig +++ b/boards/arm/nrf52840_mdk/nrf52840_mdk_defconfig @@ -17,7 +17,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/nrf52840_mdk_usb_dongle/nrf52840_mdk_usb_dongle.dts b/boards/arm/nrf52840_mdk_usb_dongle/nrf52840_mdk_usb_dongle.dts index 616af71443c..4e13783d656 100644 --- a/boards/arm/nrf52840_mdk_usb_dongle/nrf52840_mdk_usb_dongle.dts +++ b/boards/arm/nrf52840_mdk_usb_dongle/nrf52840_mdk_usb_dongle.dts @@ -87,6 +87,11 @@ status = "okay"; }; +&uicr { + nfct-pins-as-gpios; + gpio-as-nreset; +}; + &gpio0 { status = "okay"; }; diff --git a/boards/arm/nrf52840_mdk_usb_dongle/nrf52840_mdk_usb_dongle_defconfig b/boards/arm/nrf52840_mdk_usb_dongle/nrf52840_mdk_usb_dongle_defconfig index 92ec1dc7885..8701863e99a 100644 --- a/boards/arm/nrf52840_mdk_usb_dongle/nrf52840_mdk_usb_dongle_defconfig +++ b/boards/arm/nrf52840_mdk_usb_dongle/nrf52840_mdk_usb_dongle_defconfig @@ -13,8 +13,4 @@ CONFIG_HW_STACK_PROTECTION=y # enable GPIO CONFIG_GPIO=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y -CONFIG_NFCT_PINS_AS_GPIOS=y - CONFIG_PINCTRL=y diff --git a/boards/arm/nrf52840_papyr/nrf52840_papyr.dts b/boards/arm/nrf52840_papyr/nrf52840_papyr.dts index acbb09d2df2..0a9d8ff4724 100644 --- a/boards/arm/nrf52840_papyr/nrf52840_papyr.dts +++ b/boards/arm/nrf52840_papyr/nrf52840_papyr.dts @@ -85,6 +85,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/nrf52840_papyr/nrf52840_papyr_defconfig b/boards/arm/nrf52840_papyr/nrf52840_papyr_defconfig index 0ad48d7f940..4a5e737cd14 100644 --- a/boards/arm/nrf52840_papyr/nrf52840_papyr_defconfig +++ b/boards/arm/nrf52840_papyr/nrf52840_papyr_defconfig @@ -17,7 +17,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/nrf52840dk_nrf52811/nrf52840dk_nrf52811.dts b/boards/arm/nrf52840dk_nrf52811/nrf52840dk_nrf52811.dts index eb99beca22f..a53a174be15 100644 --- a/boards/arm/nrf52840dk_nrf52811/nrf52840dk_nrf52811.dts +++ b/boards/arm/nrf52840dk_nrf52811/nrf52840dk_nrf52811.dts @@ -97,6 +97,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/nrf52840dk_nrf52811/nrf52840dk_nrf52811_defconfig b/boards/arm/nrf52840dk_nrf52811/nrf52840dk_nrf52811_defconfig index 36553a757eb..2c6b7f2fc82 100644 --- a/boards/arm/nrf52840dk_nrf52811/nrf52840dk_nrf52811_defconfig +++ b/boards/arm/nrf52840dk_nrf52811/nrf52840dk_nrf52811_defconfig @@ -22,9 +22,6 @@ CONFIG_UART_CONSOLE=y # enable GPIO CONFIG_GPIO=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - # Bluetooth not enabled by default on nRF52811 due to RAM limitations when # running the default set of kernel tests. # Enable this on your prj.conf to include Bluetooth support diff --git a/boards/arm/nrf52840dk_nrf52840/nrf52840dk_nrf52840.dts b/boards/arm/nrf52840dk_nrf52840/nrf52840dk_nrf52840.dts index ba5d94738a6..96006ec4722 100644 --- a/boards/arm/nrf52840dk_nrf52840/nrf52840dk_nrf52840.dts +++ b/boards/arm/nrf52840dk_nrf52840/nrf52840dk_nrf52840.dts @@ -139,6 +139,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/nrf52840dk_nrf52840/nrf52840dk_nrf52840_defconfig b/boards/arm/nrf52840dk_nrf52840/nrf52840dk_nrf52840_defconfig index 100d91ed320..9f1232a8f0a 100644 --- a/boards/arm/nrf52840dk_nrf52840/nrf52840dk_nrf52840_defconfig +++ b/boards/arm/nrf52840dk_nrf52840/nrf52840dk_nrf52840_defconfig @@ -23,7 +23,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/nrf52840dongle_nrf52840/nrf52840dongle_nrf52840.dts b/boards/arm/nrf52840dongle_nrf52840/nrf52840dongle_nrf52840.dts index f17ecb8e18a..dcf9fb690d1 100644 --- a/boards/arm/nrf52840dongle_nrf52840/nrf52840dongle_nrf52840.dts +++ b/boards/arm/nrf52840dongle_nrf52840/nrf52840dongle_nrf52840.dts @@ -99,6 +99,11 @@ status = "okay"; }; +&uicr { + nfct-pins-as-gpios; + gpio-as-nreset; +}; + &gpio0 { status = "okay"; }; diff --git a/boards/arm/nrf52840dongle_nrf52840/nrf52840dongle_nrf52840_defconfig b/boards/arm/nrf52840dongle_nrf52840/nrf52840dongle_nrf52840_defconfig index f38880961d5..c367a90d5ef 100644 --- a/boards/arm/nrf52840dongle_nrf52840/nrf52840dongle_nrf52840_defconfig +++ b/boards/arm/nrf52840dongle_nrf52840/nrf52840dongle_nrf52840_defconfig @@ -13,10 +13,6 @@ CONFIG_HW_STACK_PROTECTION=y # enable GPIO CONFIG_GPIO=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y -CONFIG_NFCT_PINS_AS_GPIOS=y - CONFIG_PINCTRL=y # Board Kconfig.defconfig enables USB CDC ACM and should disable USB remote diff --git a/boards/arm/nrf52_adafruit_feather/nrf52_adafruit_feather.dts b/boards/arm/nrf52_adafruit_feather/nrf52_adafruit_feather.dts index 4412d3c492d..c29ca8000dc 100644 --- a/boards/arm/nrf52_adafruit_feather/nrf52_adafruit_feather.dts +++ b/boards/arm/nrf52_adafruit_feather/nrf52_adafruit_feather.dts @@ -70,6 +70,10 @@ }; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/nrf52_adafruit_feather/nrf52_adafruit_feather_defconfig b/boards/arm/nrf52_adafruit_feather/nrf52_adafruit_feather_defconfig index 42948633b2f..19aaa3825d1 100644 --- a/boards/arm/nrf52_adafruit_feather/nrf52_adafruit_feather_defconfig +++ b/boards/arm/nrf52_adafruit_feather/nrf52_adafruit_feather_defconfig @@ -17,7 +17,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/nrf52_sparkfun/nrf52_sparkfun.dts b/boards/arm/nrf52_sparkfun/nrf52_sparkfun.dts index 618bd670ad1..60de01f0bb0 100644 --- a/boards/arm/nrf52_sparkfun/nrf52_sparkfun.dts +++ b/boards/arm/nrf52_sparkfun/nrf52_sparkfun.dts @@ -50,6 +50,10 @@ }; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/nrf52_sparkfun/nrf52_sparkfun_defconfig b/boards/arm/nrf52_sparkfun/nrf52_sparkfun_defconfig index 944fc20b9c8..2a21dc07192 100644 --- a/boards/arm/nrf52_sparkfun/nrf52_sparkfun_defconfig +++ b/boards/arm/nrf52_sparkfun/nrf52_sparkfun_defconfig @@ -17,7 +17,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# Enable P0_21 as RST -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/nrf52_vbluno52/nrf52_vbluno52.dts b/boards/arm/nrf52_vbluno52/nrf52_vbluno52.dts index 8b276c5fed7..b622a1a23ea 100644 --- a/boards/arm/nrf52_vbluno52/nrf52_vbluno52.dts +++ b/boards/arm/nrf52_vbluno52/nrf52_vbluno52.dts @@ -49,6 +49,10 @@ }; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/nrf52_vbluno52/nrf52_vbluno52_defconfig b/boards/arm/nrf52_vbluno52/nrf52_vbluno52_defconfig index 97f4efbc236..fac5419e6f6 100644 --- a/boards/arm/nrf52_vbluno52/nrf52_vbluno52_defconfig +++ b/boards/arm/nrf52_vbluno52/nrf52_vbluno52_defconfig @@ -17,7 +17,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/nrf52dk_nrf52805/nrf52dk_nrf52805.dts b/boards/arm/nrf52dk_nrf52805/nrf52dk_nrf52805.dts index 29257e7a0b2..e0eaa911a06 100644 --- a/boards/arm/nrf52dk_nrf52805/nrf52dk_nrf52805.dts +++ b/boards/arm/nrf52dk_nrf52805/nrf52dk_nrf52805.dts @@ -89,6 +89,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpio0 { status = "okay"; }; diff --git a/boards/arm/nrf52dk_nrf52805/nrf52dk_nrf52805_defconfig b/boards/arm/nrf52dk_nrf52805/nrf52dk_nrf52805_defconfig index 31571f10fb4..6e275e63f35 100644 --- a/boards/arm/nrf52dk_nrf52805/nrf52dk_nrf52805_defconfig +++ b/boards/arm/nrf52dk_nrf52805/nrf52dk_nrf52805_defconfig @@ -20,9 +20,6 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# Additional board options -CONFIG_GPIO_AS_PINRESET=y - # Bluetooth not enabled by default on nRF52805 due to RAM limitations when # running the default set of kernel tests. diff --git a/boards/arm/nrf52dk_nrf52810/nrf52dk_nrf52810.dts b/boards/arm/nrf52dk_nrf52810/nrf52dk_nrf52810.dts index 823a3db7f7e..4d4600f1222 100644 --- a/boards/arm/nrf52dk_nrf52810/nrf52dk_nrf52810.dts +++ b/boards/arm/nrf52dk_nrf52810/nrf52dk_nrf52810.dts @@ -91,6 +91,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/nrf52dk_nrf52810/nrf52dk_nrf52810_defconfig b/boards/arm/nrf52dk_nrf52810/nrf52dk_nrf52810_defconfig index 5873f1d2e9a..93706e89d0a 100644 --- a/boards/arm/nrf52dk_nrf52810/nrf52dk_nrf52810_defconfig +++ b/boards/arm/nrf52dk_nrf52810/nrf52dk_nrf52810_defconfig @@ -20,9 +20,6 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - # Bluetooth not enabled by default on nRF52810 due to RAM limitations when # running the default set of kernel tests. # Enable this on your prj.conf to include Bluetooth support diff --git a/boards/arm/nrf52dk_nrf52832/nrf52dk_nrf52832.dts b/boards/arm/nrf52dk_nrf52832/nrf52dk_nrf52832.dts index 3002a1841c9..687bba9254a 100644 --- a/boards/arm/nrf52dk_nrf52832/nrf52dk_nrf52832.dts +++ b/boards/arm/nrf52dk_nrf52832/nrf52dk_nrf52832.dts @@ -138,6 +138,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/nrf52dk_nrf52832/nrf52dk_nrf52832_defconfig b/boards/arm/nrf52dk_nrf52832/nrf52dk_nrf52832_defconfig index 547ff67c348..23e1f4c249a 100644 --- a/boards/arm/nrf52dk_nrf52832/nrf52dk_nrf52832_defconfig +++ b/boards/arm/nrf52dk_nrf52832/nrf52dk_nrf52832_defconfig @@ -23,7 +23,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/nrf5340_audio_dk_nrf5340/doc/index.rst b/boards/arm/nrf5340_audio_dk_nrf5340/doc/index.rst index 7718258eec2..3d8d2413e91 100644 --- a/boards/arm/nrf5340_audio_dk_nrf5340/doc/index.rst +++ b/boards/arm/nrf5340_audio_dk_nrf5340/doc/index.rst @@ -53,9 +53,6 @@ The nrf5340_audio_dk_nrf5340_cpuapp build target provides support for the applic core on the nRF5340 SoC. The nrf5340_audio_dk_nrf5340_cpunet build target provides support for the network core on the nRF5340 SoC. -.. note:: - Trusted Firmware-M (TF-M) and building the ``ns`` target is not supported for this board. - The `Nordic Semiconductor Infocenter`_ contains the processor's information and the datasheet. diff --git a/boards/arm/nrf5340_audio_dk_nrf5340/nrf5340_audio_dk_nrf5340_cpuapp.dts b/boards/arm/nrf5340_audio_dk_nrf5340/nrf5340_audio_dk_nrf5340_cpuapp.dts index 8fe46bdd7a3..be871800332 100644 --- a/boards/arm/nrf5340_audio_dk_nrf5340/nrf5340_audio_dk_nrf5340_cpuapp.dts +++ b/boards/arm/nrf5340_audio_dk_nrf5340/nrf5340_audio_dk_nrf5340_cpuapp.dts @@ -20,3 +20,7 @@ zephyr,sram-non-secure-partition = &sram0_ns; }; }; + +&uicr { + nfct-pins-as-gpios; +}; diff --git a/boards/arm/nrf5340_audio_dk_nrf5340/nrf5340_audio_dk_nrf5340_cpuapp_common-pinctrl.dtsi b/boards/arm/nrf5340_audio_dk_nrf5340/nrf5340_audio_dk_nrf5340_cpuapp_common-pinctrl.dtsi index 46359082edd..5247c04429b 100644 --- a/boards/arm/nrf5340_audio_dk_nrf5340/nrf5340_audio_dk_nrf5340_cpuapp_common-pinctrl.dtsi +++ b/boards/arm/nrf5340_audio_dk_nrf5340/nrf5340_audio_dk_nrf5340_cpuapp_common-pinctrl.dtsi @@ -51,6 +51,28 @@ }; }; + uart1_default: uart1_default { + group1 { + psels = , + ; + }; + group2 { + psels = , + ; + bias-pull-up; + }; + }; + + uart1_sleep: uart1_sleep { + group1 { + psels = , + , + , + ; + low-power-enable; + }; + }; + i2c1_default: i2c1_default { group1 { psels = , diff --git a/boards/arm/nrf5340_audio_dk_nrf5340/nrf5340_audio_dk_nrf5340_cpuapp_common.dtsi b/boards/arm/nrf5340_audio_dk_nrf5340/nrf5340_audio_dk_nrf5340_cpuapp_common.dtsi index f529f16fbe8..df028fb75a2 100644 --- a/boards/arm/nrf5340_audio_dk_nrf5340/nrf5340_audio_dk_nrf5340_cpuapp_common.dtsi +++ b/boards/arm/nrf5340_audio_dk_nrf5340/nrf5340_audio_dk_nrf5340_cpuapp_common.dtsi @@ -141,6 +141,14 @@ pinctrl-names = "default", "sleep"; }; +arduino_serial: &uart1 { + compatible = "nordic,nrf-uarte"; + current-speed = <115200>; + pinctrl-0 = <&uart1_default>; + pinctrl-1 = <&uart1_sleep>; + pinctrl-names = "default", "sleep"; +}; + &i2c1 { compatible = "nordic,nrf-twim"; status = "okay"; diff --git a/boards/arm/nrf5340_audio_dk_nrf5340/nrf5340_audio_dk_nrf5340_cpuapp_defconfig b/boards/arm/nrf5340_audio_dk_nrf5340/nrf5340_audio_dk_nrf5340_cpuapp_defconfig index 0f5f1991b6d..d1b5f551c9b 100644 --- a/boards/arm/nrf5340_audio_dk_nrf5340/nrf5340_audio_dk_nrf5340_cpuapp_defconfig +++ b/boards/arm/nrf5340_audio_dk_nrf5340/nrf5340_audio_dk_nrf5340_cpuapp_defconfig @@ -12,7 +12,6 @@ CONFIG_HW_STACK_PROTECTION=y CONFIG_ARM_TRUSTZONE_M=y CONFIG_GPIO=y -CONFIG_NFCT_PINS_AS_GPIOS=y CONFIG_SERIAL=y diff --git a/boards/arm/nrf5340dk_nrf5340/Kconfig.defconfig b/boards/arm/nrf5340dk_nrf5340/Kconfig.defconfig index d337e2f1a1c..09ebfbf50ba 100644 --- a/boards/arm/nrf5340dk_nrf5340/Kconfig.defconfig +++ b/boards/arm/nrf5340dk_nrf5340/Kconfig.defconfig @@ -8,21 +8,6 @@ if BOARD_NRF5340DK_NRF5340_CPUAPP || BOARD_NRF5340DK_NRF5340_CPUAPP_NS config BOARD default "nrf5340dk_nrf5340_cpuapp" if BOARD_NRF5340DK_NRF5340_CPUAPP || BOARD_NRF5340DK_NRF5340_CPUAPP_NS -# By default, if we build for a Non-Secure version of the board, -# enable building with TF-M as the Secure Execution Environment. -config BUILD_WITH_TFM - default y if BOARD_NRF5340DK_NRF5340_CPUAPP_NS - -if BUILD_WITH_TFM - -# By default, if we build with TF-M, instruct build system to -# flash the combined TF-M (Secure) & Zephyr (Non Secure) image -config TFM_FLASH_MERGED_BINARY - bool - default y - -endif # BUILD_WITH_TFM - # Code Partition: # # For the secure version of the board the firmware is linked at the beginning diff --git a/boards/arm/nrf5340dk_nrf5340/nrf5340_cpuapp_common-pinctrl.dtsi b/boards/arm/nrf5340dk_nrf5340/nrf5340_cpuapp_common-pinctrl.dtsi index 6db9767c0dc..f93e3a69402 100644 --- a/boards/arm/nrf5340dk_nrf5340/nrf5340_cpuapp_common-pinctrl.dtsi +++ b/boards/arm/nrf5340dk_nrf5340/nrf5340_cpuapp_common-pinctrl.dtsi @@ -84,10 +84,12 @@ uart1_default: uart1_default { group1 { - psels = ; + psels = , + ; }; group2 { - psels = ; + psels = , + ; bias-pull-up; }; }; @@ -95,7 +97,9 @@ uart1_sleep: uart1_sleep { group1 { psels = , - ; + , + , + ; low-power-enable; }; }; diff --git a/boards/arm/nrf9131ek_nrf9131/Kconfig.board b/boards/arm/nrf9131ek_nrf9131/Kconfig.board new file mode 100644 index 00000000000..4a237e3fb61 --- /dev/null +++ b/boards/arm/nrf9131ek_nrf9131/Kconfig.board @@ -0,0 +1,14 @@ +# nRF9131-EK board configuration + +# Copyright (c) 2023 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +if SOC_NRF9131_LACA + +config BOARD_NRF9131EK_NRF9131 + bool "nRF9131 EK NRF9131" + +config BOARD_NRF9131EK_NRF9131_NS + bool "nRF9131 EK NRF9131 non-secure" + +endif # SOC_NRF9131_LACA diff --git a/boards/arm/nrf9131ek_nrf9131/Kconfig.defconfig b/boards/arm/nrf9131ek_nrf9131/Kconfig.defconfig new file mode 100644 index 00000000000..378a58fb6e3 --- /dev/null +++ b/boards/arm/nrf9131ek_nrf9131/Kconfig.defconfig @@ -0,0 +1,54 @@ +# nRF9131 EK NRF9131 board configuration + +# Copyright (c) 2023 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +if BOARD_NRF9131EK_NRF9131 || BOARD_NRF9131EK_NRF9131_NS + +config BOARD + default "nrf9131ek_nrf9131" + + +# By default, if we build for a Non-Secure version of the board, +# enable building with TF-M as the Secure Execution Environment. +config BUILD_WITH_TFM + default y if BOARD_NRF9131EK_NRF9131_NS + +if BUILD_WITH_TFM + +# By default, if we build with TF-M, instruct build system to +# flash the combined TF-M (Secure) & Zephyr (Non Secure) image +config TFM_FLASH_MERGED_BINARY + bool + default y + +endif # BUILD_WITH_TFM + +# For the secure version of the board the firmware is linked at the beginning +# of the flash, or into the code-partition defined in DT if it is intended to +# be loaded by MCUboot. If the secure firmware is to be combined with a non- +# secure image (TRUSTED_EXECUTION_SECURE=y), the secure FW image shall always +# be restricted to the size of its code partition. +# For the non-secure version of the board, the firmware +# must be linked into the code-partition (non-secure) defined in DT, regardless. +# Apply this configuration below by setting the Kconfig symbols used by +# the linker according to the information extracted from DT partitions. + +# Workaround for not being able to have commas in macro arguments +DT_CHOSEN_Z_CODE_PARTITION := zephyr,code-partition + +config FLASH_LOAD_SIZE + default $(dt_chosen_reg_size_hex,$(DT_CHOSEN_Z_CODE_PARTITION)) + depends on BOARD_NRF9131EK_NRF9131 && TRUSTED_EXECUTION_SECURE + +if BOARD_NRF9131EK_NRF9131_NS + +config FLASH_LOAD_OFFSET + default $(dt_chosen_reg_addr_hex,$(DT_CHOSEN_Z_CODE_PARTITION)) + +config FLASH_LOAD_SIZE + default $(dt_chosen_reg_size_hex,$(DT_CHOSEN_Z_CODE_PARTITION)) + +endif # BOARD_NRF9131EK_NRF9131_NS + +endif # BOARD_NRF9131EK_NRF9131 || BOARD_NRF9131EK_NRF9131_NS diff --git a/boards/arm/nrf9131ek_nrf9131/board.cmake b/boards/arm/nrf9131ek_nrf9131/board.cmake new file mode 100644 index 00000000000..8293a428b40 --- /dev/null +++ b/boards/arm/nrf9131ek_nrf9131/board.cmake @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: Apache-2.0 + +if(CONFIG_BOARD_NRF9131EK_NRF9131_NS) + set(TFM_PUBLIC_KEY_FORMAT "full") +endif() + +if(CONFIG_TFM_FLASH_MERGED_BINARY) + set_property(TARGET runners_yaml_props_target PROPERTY hex_file tfm_merged.hex) +endif() + +# TODO: change to nRF9131_xxAA when such device is available in JLink +board_runner_args(jlink "--device=nRF9160_xxAA" "--speed=4000") +include(${ZEPHYR_BASE}/boards/common/nrfjprog.board.cmake) +include(${ZEPHYR_BASE}/boards/common/jlink.board.cmake) diff --git a/boards/arm/nrf9131ek_nrf9131/doc/img/nrf9131ek_nrf9131.webp b/boards/arm/nrf9131ek_nrf9131/doc/img/nrf9131ek_nrf9131.webp new file mode 100644 index 00000000000..056296ebbbc Binary files /dev/null and b/boards/arm/nrf9131ek_nrf9131/doc/img/nrf9131ek_nrf9131.webp differ diff --git a/boards/arm/nrf9131ek_nrf9131/doc/index.rst b/boards/arm/nrf9131ek_nrf9131/doc/index.rst new file mode 100644 index 00000000000..ffb066d860d --- /dev/null +++ b/boards/arm/nrf9131ek_nrf9131/doc/index.rst @@ -0,0 +1,228 @@ +.. _nrf9131ek_nrf9131: + +nRF9131 EK +########## + +Overview +******** + +The nRF9131 EK (PCA10165) is a single-board evaluation kit for the nRF9131 SiP for LTE-M and NB-IoT. +The nrf9131ek_nrf9131 board configuration provides support for the Nordic Semiconductor nRF9131 ARM +Cortex-M33F CPU with ARMv8-M Security Extension and the following devices: + +* :abbr:`ADC (Analog to Digital Converter)` +* CLOCK +* FLASH +* :abbr:`GPIO (General Purpose Input Output)` +* :abbr:`I2C (Inter-Integrated Circuit)` +* :abbr:`MPU (Memory Protection Unit)` +* :abbr:`NVIC (Nested Vectored Interrupt Controller)` +* :abbr:`PWM (Pulse Width Modulation)` +* :abbr:`RTC (nRF RTC System Clock)` +* Segger RTT (RTT Console) +* :abbr:`SPI (Serial Peripheral Interface)` +* :abbr:`UARTE (Universal asynchronous receiver-transmitter with EasyDMA)` +* :abbr:`WDT (Watchdog Timer)` +* :abbr:`IDAU (Implementation Defined Attribution Unit)` + +.. figure:: img/nrf9131ek_nrf9131.webp + :align: center + :alt: nRF9131 EK + + nRF9131 EK (Credit: Nordic Semiconductor) + +The `Nordic Semiconductor Infocenter`_ +contains the processor's information and the datasheet. + + +Hardware +******** + +nRF9131 EK has two external oscillators. The frequency of +the slow clock is 32.768 kHz. The frequency of the main clock +is 32 MHz. + +Supported Features +================== + +The nrf9131ek_nrf9131 board configuration supports the following +hardware features: + ++-----------+------------+----------------------+ +| Interface | Controller | Driver/Component | ++===========+============+======================+ +| ADC | on-chip | adc | ++-----------+------------+----------------------+ +| CLOCK | on-chip | clock_control | ++-----------+------------+----------------------+ +| FLASH | on-chip | flash | ++-----------+------------+----------------------+ +| GPIO | on-chip | gpio | ++-----------+------------+----------------------+ +| I2C(M) | on-chip | i2c | ++-----------+------------+----------------------+ +| MPU | on-chip | arch/arm | ++-----------+------------+----------------------+ +| NVIC | on-chip | arch/arm | ++-----------+------------+----------------------+ +| PWM | on-chip | pwm | ++-----------+------------+----------------------+ +| RTC | on-chip | system clock | ++-----------+------------+----------------------+ +| RTT | Segger | console | ++-----------+------------+----------------------+ +| SPI(M/S) | on-chip | spi | ++-----------+------------+----------------------+ +| SPU | on-chip | system protection | ++-----------+------------+----------------------+ +| UARTE | on-chip | serial | ++-----------+------------+----------------------+ +| WDT | on-chip | watchdog | ++-----------+------------+----------------------+ + +Connections and IOs +=================== + +LED +--- + +* LED (red) = P0.29 +* LED (green) = P0.30 +* LED (blue) = P0.31 + +Push buttons and Switches +------------------------- + +* BUTTON = P0.28 +* RESET + +Security components +=================== + +- Implementation Defined Attribution Unit (`IDAU`_). The IDAU is implemented + with the System Protection Unit and is used to define secure and non-secure + memory maps. By default, all of the memory space (Flash, SRAM, and + peripheral address space) is defined to be secure accessible only. +- Secure boot. + + +Programming and Debugging +************************* + +nrf9131ek_nrf9131 supports the Armv8m Security Extension, and by default boots +in the Secure state. + +Building Secure/Non-Secure Zephyr applications with Arm |reg| TrustZone |reg| +============================================================================= + +Applications on the nRF9131 may contain a Secure and a Non-Secure firmware +image. The Secure image can be built using either Zephyr or +`Trusted Firmware M`_ (TF-M). Non-Secure firmware images are always built +using Zephyr. The two alternatives are described below. + +.. note:: + + By default the Secure image for nRF9131 is built using TF-M. + +Building the Secure firmware using Zephyr +----------------------------------------- + +The process requires the following steps: + +1. Build the Secure Zephyr application using ``-DBOARD=nrf9131ek_nrf9131`` and + ``CONFIG_TRUSTED_EXECUTION_SECURE=y`` in the application project configuration file. +2. Build the Non-Secure Zephyr application using ``-DBOARD=nrf9131ek_nrf9131_ns``. +3. Merge the two binaries together. + +Building the Secure firmware with TF-M +-------------------------------------- + +The process to build the Secure firmware image using TF-M and the Non-Secure +firmware image using Zephyr requires the following action: + +1. Build the Non-Secure Zephyr application + using ``-DBOARD=nrf9131ek_nrf9131_ns``. + To invoke the building of TF-M the Zephyr build system requires the + Kconfig option ``BUILD_WITH_TFM`` to be enabled, which is done by + default when building Zephyr as a Non-Secure application. + The Zephyr build system will perform the following steps automatically: + + * Build the Non-Secure firmware image as a regular Zephyr application + * Build a TF-M (secure) firmware image + * Merge the output binaries together + * Optionally build a bootloader image (MCUboot) + +.. note:: + + Depending on the TF-M configuration, an application DTS overlay may be + required, to adjust the Non-Secure image Flash and SRAM starting address + and sizes. + +When building a Secure/Non-Secure application, the Secure application will +have to set the IDAU (SPU) configuration to allow Non-Secure access to all +CPU resources utilized by the Non-Secure application firmware. SPU +configuration shall take place before jumping to the Non-Secure application. + +Building a Secure only application +================================== + +Build the Zephyr app in the usual way (see :ref:`build_an_application` +and :ref:`application_run`), using ``-DBOARD=nrf9131ek_nrf9131``. + + +Flashing +======== + +Follow the instructions in the :ref:`nordic_segger` page to install +and configure all the necessary software. Further information can be +found in :ref:`nordic_segger_flashing`. Then build and flash +applications as usual (see :ref:`build_an_application` and +:ref:`application_run` for more details). + +Here is an example for the :ref:`hello_world` application. + +First, run your favorite terminal program to listen for output. + +.. code-block:: console + + $ minicom -D -b 115200 + +Replace :code:`` with the port where the nRF9131 EK +can be found. For example, under Linux, :code:`/dev/ttyACM0`. + +Then build and flash the application in the usual way. + +.. zephyr-app-commands:: + :zephyr-app: samples/hello_world + :board: nrf9131ek_nrf9131 + :goals: build flash + +Debugging +========= + +Refer to the :ref:`nordic_segger` page to learn about debugging Nordic boards with a +Segger IC. + + +Testing the LEDs and buttons in the nRF9131 EK +********************************************** + +There are 2 samples that allow you to test that the button and LED on +the board are working properly with Zephyr: + +* :ref:`blinky-sample` +* :ref:`button-sample` + +You can build and flash the examples to make sure Zephyr is running correctly on +your board. The button and LED definitions can be found in +:zephyr_file:`boards/arm/nrf9131ek_nrf9131/nrf9131ek_nrf9131_common.dtsi`. + +References +********** + +.. target-notes:: + +.. _IDAU: + https://developer.arm.com/docs/100690/latest/attribution-units-sau-and-idau +.. _Nordic Semiconductor Infocenter: https://infocenter.nordicsemi.com +.. _Trusted Firmware M: https://www.trustedfirmware.org/projects/tf-m/ diff --git a/boards/arm/nrf9131ek_nrf9131/nrf9131ek_nrf9131.dts b/boards/arm/nrf9131ek_nrf9131/nrf9131ek_nrf9131.dts new file mode 100644 index 00000000000..4b66e5348a0 --- /dev/null +++ b/boards/arm/nrf9131ek_nrf9131/nrf9131ek_nrf9131.dts @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/dts-v1/; +#include +#include "nrf9131ek_nrf9131_common.dtsi" + +/ { + chosen { + zephyr,sram = &sram0_s; + zephyr,flash = &flash0; + zephyr,code-partition = &slot0_partition; + zephyr,sram-secure-partition = &sram0_s; + zephyr,sram-non-secure-partition = &sram0_ns; + }; +}; diff --git a/boards/arm/nrf9131ek_nrf9131/nrf9131ek_nrf9131.yaml b/boards/arm/nrf9131ek_nrf9131/nrf9131ek_nrf9131.yaml new file mode 100644 index 00000000000..d1b04054ce8 --- /dev/null +++ b/boards/arm/nrf9131ek_nrf9131/nrf9131ek_nrf9131.yaml @@ -0,0 +1,17 @@ +identifier: nrf9131ek_nrf9131 +name: nRF9131-EK-NRF9131 +type: mcu +arch: arm +toolchain: + - gnuarmemb + - xtools + - zephyr +ram: 88 +flash: 1024 +supported: + - gpio + - i2c + - pwm + - spi + - watchdog + - counter diff --git a/boards/arm/nrf9131ek_nrf9131/nrf9131ek_nrf9131_common-pinctrl.dtsi b/boards/arm/nrf9131ek_nrf9131/nrf9131ek_nrf9131_common-pinctrl.dtsi new file mode 100644 index 00000000000..419e7c8d70c --- /dev/null +++ b/boards/arm/nrf9131ek_nrf9131/nrf9131ek_nrf9131_common-pinctrl.dtsi @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor + * SPDX-License-Identifier: Apache-2.0 + */ + +&pinctrl { + uart0_default: uart0_default { + group1 { + psels = , + ; + }; + group2 { + psels = , + ; + bias-pull-up; + }; + }; + + uart0_sleep: uart0_sleep { + group1 { + psels = , + , + , + ; + low-power-enable; + }; + }; + + uart1_default: uart1_default { + group1 { + psels = , + ; + }; + group2 { + psels = , + ; + bias-pull-up; + }; + }; + + uart1_sleep: uart1_sleep { + group1 { + psels = , + , + , + ; + low-power-enable; + }; + }; + + i2c2_default: i2c2_default { + group1 { + psels = , + ; + }; + }; + + i2c2_sleep: i2c2_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; + + pwm0_default: pwm0_default { + group1 { + psels = , + , + ; + }; + }; + + pwm0_sleep: pwm0_sleep { + group1 { + psels = , + , + ; + low-power-enable; + }; + }; + + spi3_default: spi3_default { + group1 { + psels = , + , + ; + nordic,drive-mode = ; + }; + }; + + spi3_sleep: spi3_sleep { + group1 { + psels = , + , + ; + low-power-enable; + }; + }; +}; diff --git a/boards/arm/nrf9131ek_nrf9131/nrf9131ek_nrf9131_common.dtsi b/boards/arm/nrf9131ek_nrf9131/nrf9131ek_nrf9131_common.dtsi new file mode 100644 index 00000000000..2c3b8481d2a --- /dev/null +++ b/boards/arm/nrf9131ek_nrf9131/nrf9131ek_nrf9131_common.dtsi @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "nrf9131ek_nrf9131_common-pinctrl.dtsi" +#include + +/ { + model = "Nordic nRF9131 EK NRF9131"; + compatible = "nordic,nrf9131-ek-nrf9131"; + + chosen { + zephyr,console = &uart0; + zephyr,shell-uart = &uart0; + zephyr,uart-mcumgr = &uart0; + }; + + leds { + compatible = "gpio-leds"; + led0: led_0 { + gpios = <&gpio0 29 GPIO_ACTIVE_HIGH>; + label = "Red LED 1"; + }; + led1: led_1 { + gpios = <&gpio0 30 GPIO_ACTIVE_HIGH>; + label = "Green LED 2"; + }; + led2: led_2 { + gpios = <&gpio0 31 GPIO_ACTIVE_HIGH>; + label = "Blue LED 3"; + }; + }; + + pwmleds { + compatible = "pwm-leds"; + pwm_led0: pwm_led_0 { + pwms = <&pwm0 0 PWM_MSEC(8) PWM_POLARITY_NORMAL>; + }; + pwm_led1: pwm_led_1 { + pwms = <&pwm0 1 PWM_MSEC(8) PWM_POLARITY_NORMAL>; + }; + pwm_led2: pwm_led_2 { + pwms = <&pwm0 2 PWM_MSEC(8) PWM_POLARITY_NORMAL>; + }; + }; + + buttons { + compatible = "gpio-keys"; + button0: button_0 { + gpios = <&gpio0 28 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; + label = "Push button 1"; + zephyr,code = ; + }; + }; + + /* These aliases are provided for compatibility with samples */ + aliases { + led0 = &led0; + led1 = &led1; + led2 = &led2; + pwm-led0 = &pwm_led0; + pwm-led1 = &pwm_led1; + pwm-led2 = &pwm_led2; + sw0 = &button0; + bootloader-led0 = &led0; + mcuboot-button0 = &button0; + mcuboot-led0 = &led0; + watchdog0 = &wdt0; + spi-flash0 = &gd25wb256; + }; +}; + +&adc { + status = "okay"; +}; + +&gpiote { + status = "okay"; +}; + +&gpio0 { + status = "okay"; +}; + +&uart0 { + status = "okay"; + current-speed = <115200>; + pinctrl-0 = <&uart0_default>; + pinctrl-1 = <&uart0_sleep>; + pinctrl-names = "default", "sleep"; +}; + +&uart1 { + status = "okay"; + current-speed = <115200>; + pinctrl-0 = <&uart1_default>; + pinctrl-1 = <&uart1_sleep>; + pinctrl-names = "default", "sleep"; +}; + +&i2c2 { + compatible = "nordic,nrf-twim"; + status = "okay"; + pinctrl-0 = <&i2c2_default>; + pinctrl-1 = <&i2c2_sleep>; + pinctrl-names = "default", "sleep"; + clock-frequency = ; + + pmic_main: npm1300@6b { + compatible = "nordic,npm1300"; + reg = <0x6b>; + pmic_charger: charger { + compatible = "nordic,npm1300-charger"; + term-microvolt = <4150000>; + term-warm-microvolt = <4000000>; + current-microamp = <150000>; + dischg-limit-microamp = <1000000>; + vbus-limit-microamp = <500000>; + thermistor-ohms = <10000>; + thermistor-beta = <3380>; + charging-enable; + }; + regulators { + compatible = "nordic,npm1300-regulator"; + BUCK1 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + }; + BUCK2 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + }; + }; + }; +}; + +&pwm0 { + status = "okay"; + pinctrl-0 = <&pwm0_default>; + pinctrl-1 = <&pwm0_sleep>; + pinctrl-names = "default", "sleep"; +}; + +&spi3 { + compatible = "nordic,nrf-spim"; + status = "okay"; + cs-gpios = <&gpio0 26 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&spi3_default>; + pinctrl-1 = <&spi3_sleep>; + pinctrl-names = "default", "sleep"; + + gd25wb256: gd25wb256e3ir@0 { + compatible = "jedec,spi-nor"; + status = "disabled"; + reg = <0>; + spi-max-frequency = <8000000>; + size = <268435456>; + has-dpd; + t-enter-dpd = <3000>; + t-exit-dpd = <40000>; + sfdp-bfp = [ + e5 20 f3 ff ff ff ff 0f 44 eb 08 6b 08 3b 42 bb + ee ff ff ff ff ff 00 ff ff ff 00 ff 0c 20 0f 52 + 10 d8 00 ff 44 7a c9 fe 83 67 26 62 ec 82 18 44 + 7a 75 7a 75 04 c4 d5 5c 00 06 74 00 08 50 00 01 + ]; + jedec-id = [c8 65 19]; + }; +}; + +&flash0 { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x00000000 0x10000>; + }; + slot0_partition: partition@10000 { + label = "image-0"; + }; + slot0_ns_partition: partition@50000 { + label = "image-0-nonsecure"; + }; + slot1_partition: partition@85000 { + label = "image-1"; + }; + slot1_ns_partition: partition@c5000 { + label = "image-1-nonsecure"; + }; + storage_partition: partition@fa000 { + label = "storage"; + reg = <0x000fa000 0x00006000>; + }; + }; +}; + + + +/ { + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + sram0_s: image_s@20000000 { + /* Secure image memory */ + }; + + sram0_modem: image_modem@20016000 { + /* Modem (shared) memory */ + }; + + sram0_ns: image_ns@20020000 { + /* Non-Secure image memory */ + }; + }; +}; + +/* Include partition configuration file */ +#include "nrf9131ek_nrf9131_partition_conf.dtsi" diff --git a/boards/arm/nrf9131ek_nrf9131/nrf9131ek_nrf9131_defconfig b/boards/arm/nrf9131ek_nrf9131/nrf9131ek_nrf9131_defconfig new file mode 100644 index 00000000000..fc77ffe0d13 --- /dev/null +++ b/boards/arm/nrf9131ek_nrf9131/nrf9131ek_nrf9131_defconfig @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_SOC_SERIES_NRF91X=y +CONFIG_SOC_NRF9131_LACA=y +CONFIG_BOARD_NRF9131EK_NRF9131=y + +# Enable MPU +CONFIG_ARM_MPU=y + +# Enable hardware stack protection +CONFIG_HW_STACK_PROTECTION=y + +# Enable TrustZone-M +CONFIG_ARM_TRUSTZONE_M=y + +# enable GPIO +CONFIG_GPIO=y + +# Enable uart driver +CONFIG_SERIAL=y + +# enable console +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y + +CONFIG_PINCTRL=y diff --git a/boards/arm/nrf9131ek_nrf9131/nrf9131ek_nrf9131_ns.dts b/boards/arm/nrf9131ek_nrf9131/nrf9131ek_nrf9131_ns.dts new file mode 100644 index 00000000000..9a652cd0aed --- /dev/null +++ b/boards/arm/nrf9131ek_nrf9131/nrf9131ek_nrf9131_ns.dts @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/dts-v1/; +#include +#include "nrf9131ek_nrf9131_common.dtsi" + +/ { + chosen { + zephyr,flash = &flash0; + zephyr,sram = &sram0_ns; + zephyr,code-partition = &slot0_ns_partition; + }; +}; + +/* Disable UART1, because it is used by default in TF-M */ +&uart1 { + status = "disabled"; +}; diff --git a/boards/arm/nrf9131ek_nrf9131/nrf9131ek_nrf9131_ns.yaml b/boards/arm/nrf9131ek_nrf9131/nrf9131ek_nrf9131_ns.yaml new file mode 100644 index 00000000000..cf33abd55da --- /dev/null +++ b/boards/arm/nrf9131ek_nrf9131/nrf9131ek_nrf9131_ns.yaml @@ -0,0 +1,15 @@ +identifier: nrf9131ek_nrf9131_ns +name: nRF9131-EK-NRF9131-Non-Secure +type: mcu +arch: arm +toolchain: + - gnuarmemb + - xtools + - zephyr +ram: 128 +flash: 212 +supported: + - i2c + - pwm + - watchdog + - netif:modem diff --git a/boards/arm/nrf9131ek_nrf9131/nrf9131ek_nrf9131_ns_defconfig b/boards/arm/nrf9131ek_nrf9131/nrf9131ek_nrf9131_ns_defconfig new file mode 100644 index 00000000000..83af1cf6b74 --- /dev/null +++ b/boards/arm/nrf9131ek_nrf9131/nrf9131ek_nrf9131_ns_defconfig @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_SOC_SERIES_NRF91X=y +CONFIG_SOC_NRF9131_LACA=y +CONFIG_BOARD_NRF9131EK_NRF9131_NS=y + +# Enable MPU +CONFIG_ARM_MPU=y + +# Enable hardware stack protection +CONFIG_HW_STACK_PROTECTION=y + +# Enable TrustZone-M +CONFIG_ARM_TRUSTZONE_M=y + +# This Board implies building Non-Secure firmware +CONFIG_TRUSTED_EXECUTION_NONSECURE=y + +# enable GPIO +CONFIG_GPIO=y + +# Enable uart driver +CONFIG_SERIAL=y + +# enable console +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y + +CONFIG_PINCTRL=y + +# enable PMIC +CONFIG_I2C=y +CONFIG_REGULATOR=y +CONFIG_SENSOR=y +CONFIG_NPM1300_CHARGER=y diff --git a/boards/arm/nrf9131ek_nrf9131/nrf9131ek_nrf9131_partition_conf.dtsi b/boards/arm/nrf9131ek_nrf9131/nrf9131ek_nrf9131_partition_conf.dtsi new file mode 100644 index 00000000000..d14d8d95f75 --- /dev/null +++ b/boards/arm/nrf9131ek_nrf9131/nrf9131ek_nrf9131_partition_conf.dtsi @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * Default Flash planning for nRF9131ek_nrf9131. + * + * Zephyr build for nRF9131 with ARM TrustZone-M support, + * implies building Secure and Non-Secure Zephyr images. + * + * Secure image will be placed, by default, in flash0 + * (or in slot0, if MCUboot is present). + * Secure image will use sram0 for system memory. + * + * Non-Secure image will be placed in slot0_ns, and use + * sram0_ns for system memory. + * + * Note that the Secure image only requires knowledge of + * the beginning of the Non-Secure image (not its size). + */ + +&slot0_partition { + reg = <0x00010000 0x40000>; +}; + +&slot0_ns_partition { + reg = <0x00050000 0x35000>; +}; + +&slot1_partition { + reg = <0x00085000 0x40000>; +}; + +&slot1_ns_partition { + reg = <0x000c5000 0x35000>; +}; + +/* Default SRAM planning when building for nRF9131 with + * ARM TrustZone-M support + * - Lowest 88 kB SRAM allocated to Secure image (sram0_s). + * - 40 kB SRAM reserved for and used by the modem library + * (sram0_modem). This memory is Non-Secure. + * - Upper 128 kB allocated to Non-Secure image (sram0_ns). + * When building with TF-M, both sram0_modem and sram0_ns + * are allocated to the Non-Secure image. + */ + +&sram0_s { + reg = <0x20000000 DT_SIZE_K(88)>; +}; + +&sram0_modem { + reg = <0x20016000 DT_SIZE_K(40)>; +}; + +&sram0_ns { + reg = <0x20020000 DT_SIZE_K(128)>; +}; diff --git a/boards/arm/nrf9131ek_nrf9131/pre_dt_board.cmake b/boards/arm/nrf9131ek_nrf9131/pre_dt_board.cmake new file mode 100644 index 00000000000..c8267afd1b4 --- /dev/null +++ b/boards/arm/nrf9131ek_nrf9131/pre_dt_board.cmake @@ -0,0 +1,7 @@ +# Copyright (c) 2021 Linaro Limited +# SPDX-License-Identifier: Apache-2.0 + +# Suppress "unique_unit_address_if_enabled" to handle the following overlaps: +# - flash-controller@39000 & kmu@39000 +# - power@5000 & clock@5000 +list(APPEND EXTRA_DTC_FLAGS "-Wno-unique_unit_address_if_enabled") diff --git a/boards/arm/nrf9160_innblue21/doc/index.rst b/boards/arm/nrf9160_innblue21/doc/index.rst index 628166c409c..9ded085dc6f 100644 --- a/boards/arm/nrf9160_innblue21/doc/index.rst +++ b/boards/arm/nrf9160_innblue21/doc/index.rst @@ -104,9 +104,6 @@ have to set the IDAU (SPU) configuration to allow Non-Secure access to all CPU resources utilized by the Non-Secure application firmware. SPU configuration shall take place before jumping to the Non-Secure application. -.. note:: - Trusted Firmware-M (TF-M) and building the ``ns`` target is not supported for this board. - Building a Secure only application ================================== diff --git a/boards/arm/nrf9160_innblue21/nrf9160_innblue21_common.dtsi b/boards/arm/nrf9160_innblue21/nrf9160_innblue21_common.dtsi index 0abfd4d6b9c..0ec5de5dfb1 100644 --- a/boards/arm/nrf9160_innblue21/nrf9160_innblue21_common.dtsi +++ b/boards/arm/nrf9160_innblue21/nrf9160_innblue21_common.dtsi @@ -168,27 +168,23 @@ boot_partition: partition@0 { label = "mcuboot"; - reg = <0x00000000 0xc000>; + reg = <0x00000000 0x10000>; }; - slot0_partition: partition@c000 { + slot0_partition: partition@10000 { label = "image-0"; }; - slot0_ns_partition: partition@3e000 { + slot0_ns_partition: partition@50000 { label = "image-0-nonsecure"; }; - slot1_partition: partition@7e000 { + slot1_partition: partition@80000 { label = "image-1"; }; - slot1_ns_partition: partition@b0000 { + slot1_ns_partition: partition@c0000 { label = "image-1-nonsecure"; }; - scratch_partition: partition@f0000 { - label = "image-scratch"; - reg = <0x000f0000 0xa000>; - }; - storage_partition: partition@fa000 { + storage_partition: partition@f8000 { label = "storage"; - reg = <0x000fa000 0x00006000>; + reg = <0x000f8000 0x00008000>; }; }; }; diff --git a/boards/arm/nrf9160_innblue21/nrf9160_innblue21_ns.yaml b/boards/arm/nrf9160_innblue21/nrf9160_innblue21_ns.yaml index 096821d8364..4584ad0e6a4 100644 --- a/boards/arm/nrf9160_innblue21/nrf9160_innblue21_ns.yaml +++ b/boards/arm/nrf9160_innblue21/nrf9160_innblue21_ns.yaml @@ -7,7 +7,7 @@ toolchain: - xtools - zephyr ram: 128 -flash: 256 +flash: 192 supported: - i2c - pwm diff --git a/boards/arm/nrf9160_innblue21/nrf9160_innblue21_partition_conf.dtsi b/boards/arm/nrf9160_innblue21/nrf9160_innblue21_partition_conf.dtsi index d3a30abdc76..2c64ba67a8a 100644 --- a/boards/arm/nrf9160_innblue21/nrf9160_innblue21_partition_conf.dtsi +++ b/boards/arm/nrf9160_innblue21/nrf9160_innblue21_partition_conf.dtsi @@ -22,19 +22,19 @@ */ &slot0_partition { - reg = <0x0000c000 0x30000>; + reg = <0x00010000 0x40000>; }; &slot0_ns_partition { - reg = <0x0003e000 0x40000>; + reg = <0x00050000 0x30000>; }; &slot1_partition { - reg = <0x0007e000 0x30000>; + reg = <0x00080000 0x40000>; }; &slot1_ns_partition { - reg = <0x000b0000 0x40000>; + reg = <0x000c0000 0x30000>; }; /* Default SRAM planning when building for nRF9160 with diff --git a/boards/arm/nrf9160_innblue22/doc/index.rst b/boards/arm/nrf9160_innblue22/doc/index.rst index ce3eb35d60f..06f6b6326f8 100644 --- a/boards/arm/nrf9160_innblue22/doc/index.rst +++ b/boards/arm/nrf9160_innblue22/doc/index.rst @@ -104,9 +104,6 @@ have to set the IDAU (SPU) configuration to allow Non-Secure access to all CPU resources utilized by the Non-Secure application firmware. SPU configuration shall take place before jumping to the Non-Secure application. -.. note:: - Trusted Firmware-M (TF-M) and building the ``ns`` target is not supported for this board. - Building a Secure only application ================================== diff --git a/boards/arm/nrf9160_innblue22/nrf9160_innblue22_common.dtsi b/boards/arm/nrf9160_innblue22/nrf9160_innblue22_common.dtsi index 4582f975f5d..4738dd84a63 100644 --- a/boards/arm/nrf9160_innblue22/nrf9160_innblue22_common.dtsi +++ b/boards/arm/nrf9160_innblue22/nrf9160_innblue22_common.dtsi @@ -171,27 +171,24 @@ boot_partition: partition@0 { label = "mcuboot"; - reg = <0x00000000 0xc000>; + reg = <0x00000000 0x10000>; }; - slot0_partition: partition@c000 { + slot0_partition: partition@10000 { label = "image-0"; }; - slot0_ns_partition: partition@3e000 { + slot0_ns_partition: partition@50000 { label = "image-0-nonsecure"; }; - slot1_partition: partition@7e000 { + slot1_partition: partition@80000 { label = "image-1"; }; - slot1_ns_partition: partition@b0000 { + slot1_ns_partition: partition@c0000 { label = "image-1-nonsecure"; }; - scratch_partition: partition@f0000 { - label = "image-scratch"; - reg = <0x000f0000 0xa000>; - }; - storage_partition: partition@fa000 { + /* 0xf0000 to 0xf7fff reserved for TF-M partitions */ + storage_partition: partition@f8000 { label = "storage"; - reg = <0x000fa000 0x00006000>; + reg = <0x000f8000 0x00008000>; }; }; }; diff --git a/boards/arm/nrf9160_innblue22/nrf9160_innblue22_ns.yaml b/boards/arm/nrf9160_innblue22/nrf9160_innblue22_ns.yaml index 2ce0d6e3db2..0186c26a377 100644 --- a/boards/arm/nrf9160_innblue22/nrf9160_innblue22_ns.yaml +++ b/boards/arm/nrf9160_innblue22/nrf9160_innblue22_ns.yaml @@ -7,7 +7,7 @@ toolchain: - xtools - zephyr ram: 128 -flash: 256 +flash: 192 supported: - i2c - pwm diff --git a/boards/arm/nrf9160_innblue22/nrf9160_innblue22_partition_conf.dtsi b/boards/arm/nrf9160_innblue22/nrf9160_innblue22_partition_conf.dtsi index 3588a6a7ce1..b14640a02ec 100644 --- a/boards/arm/nrf9160_innblue22/nrf9160_innblue22_partition_conf.dtsi +++ b/boards/arm/nrf9160_innblue22/nrf9160_innblue22_partition_conf.dtsi @@ -22,19 +22,19 @@ */ &slot0_partition { - reg = <0x0000c000 0x30000>; + reg = <0x00010000 0x40000>; }; &slot0_ns_partition { - reg = <0x0003e000 0x40000>; + reg = <0x00050000 0x30000>; }; &slot1_partition { - reg = <0x0007e000 0x30000>; + reg = <0x00080000 0x40000>; }; &slot1_ns_partition { - reg = <0x000b0000 0x40000>; + reg = <0x000c0000 0x30000>; }; /* Default SRAM planning when building for nRF9160 with diff --git a/boards/arm/nrf9160dk_nrf52840/board.c b/boards/arm/nrf9160dk_nrf52840/board.c index e01b8eb64e7..cdf00f8f3dd 100644 --- a/boards/arm/nrf9160dk_nrf52840/board.c +++ b/boards/arm/nrf9160dk_nrf52840/board.c @@ -27,7 +27,7 @@ LOG_MODULE_REGISTER(board_control, CONFIG_BOARD_NRF9160DK_LOG_LEVEL); * exposes the nRESET function (P0.18 in nRF52840), there is no need to * provide any additional GPIO configuration for it. */ -#define RESET_INPUT_IS_PINRESET (IS_ENABLED(CONFIG_GPIO_AS_PINRESET) && \ +#define RESET_INPUT_IS_PINRESET (DT_PROP(DT_NODELABEL(uicr), gpio_as_nreset) && \ GET_PORT(reset_input, gpios, 0) == 0 && \ GET_PIN(reset_input, gpios, 0) == 18) #define USE_RESET_GPIO \ diff --git a/boards/arm/nrf9160dk_nrf52840/nrf9160dk_nrf52840.dts b/boards/arm/nrf9160dk_nrf52840/nrf9160dk_nrf52840.dts index c8cc706750c..07dc648b853 100644 --- a/boards/arm/nrf9160dk_nrf52840/nrf9160dk_nrf52840.dts +++ b/boards/arm/nrf9160dk_nrf52840/nrf9160dk_nrf52840.dts @@ -141,6 +141,10 @@ }; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/nrf9160dk_nrf52840/nrf9160dk_nrf52840_defconfig b/boards/arm/nrf9160dk_nrf52840/nrf9160dk_nrf52840_defconfig index c15e39669ea..073409dbba1 100644 --- a/boards/arm/nrf9160dk_nrf52840/nrf9160dk_nrf52840_defconfig +++ b/boards/arm/nrf9160dk_nrf52840/nrf9160dk_nrf52840_defconfig @@ -17,7 +17,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/nrf9160dk_nrf9160/Kconfig.defconfig b/boards/arm/nrf9160dk_nrf9160/Kconfig.defconfig index cd052dc7a20..7d9046f63b5 100644 --- a/boards/arm/nrf9160dk_nrf9160/Kconfig.defconfig +++ b/boards/arm/nrf9160dk_nrf9160/Kconfig.defconfig @@ -8,21 +8,6 @@ if BOARD_NRF9160DK_NRF9160 || BOARD_NRF9160DK_NRF9160_NS config BOARD default "nrf9160dk_nrf9160" -# By default, if we build for a Non-Secure version of the board, -# enable building with TF-M as the Secure Execution Environment. -config BUILD_WITH_TFM - default y if BOARD_NRF9160DK_NRF9160_NS - -if BUILD_WITH_TFM - -# By default, if we build with TF-M, instruct build system to -# flash the combined TF-M (Secure) & Zephyr (Non Secure) image -config TFM_FLASH_MERGED_BINARY - bool - default y - -endif # BUILD_WITH_TFM - # For the secure version of the board the firmware is linked at the beginning # of the flash, or into the code-partition defined in DT if it is intended to # be loaded by MCUboot. If the secure firmware is to be combined with a non- diff --git a/boards/arm/nrf9161dk_nrf9161/Kconfig.defconfig b/boards/arm/nrf9161dk_nrf9161/Kconfig.defconfig index 2674e87cad4..ea7150ede5f 100644 --- a/boards/arm/nrf9161dk_nrf9161/Kconfig.defconfig +++ b/boards/arm/nrf9161dk_nrf9161/Kconfig.defconfig @@ -8,21 +8,6 @@ if BOARD_NRF9161DK_NRF9161 || BOARD_NRF9161DK_NRF9161_NS config BOARD default "nrf9161dk_nrf9161" -# By default, if we build for a Non-Secure version of the board, -# enable building with TF-M as the Secure Execution Environment. -config BUILD_WITH_TFM - default y if BOARD_NRF9161DK_NRF9161_NS - -if BUILD_WITH_TFM - -# By default, if we build with TF-M, instruct build system to -# flash the combined TF-M (Secure) & Zephyr (Non Secure) image -config TFM_FLASH_MERGED_BINARY - bool - default y - -endif # BUILD_WITH_TFM - # For the secure version of the board the firmware is linked at the beginning # of the flash, or into the code-partition defined in DT if it is intended to # be loaded by MCUboot. If the secure firmware is to be combined with a non- diff --git a/boards/arm/nrf9161dk_nrf9161/nrf9161dk_nrf9161_0_7_0.overlay b/boards/arm/nrf9161dk_nrf9161/nrf9161dk_nrf9161_0_7_0.overlay new file mode 100644 index 00000000000..22e9f970dab --- /dev/null +++ b/boards/arm/nrf9161dk_nrf9161/nrf9161dk_nrf9161_0_7_0.overlay @@ -0,0 +1,7 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "nrf9161dk_nrf9161_common_0_7_0.dtsi" diff --git a/boards/arm/nrf9161dk_nrf9161/nrf9161dk_nrf9161_common.dtsi b/boards/arm/nrf9161dk_nrf9161/nrf9161dk_nrf9161_common.dtsi index 1dee5b1b1a3..a6aeccf34b9 100644 --- a/boards/arm/nrf9161dk_nrf9161/nrf9161dk_nrf9161_common.dtsi +++ b/boards/arm/nrf9161dk_nrf9161/nrf9161dk_nrf9161_common.dtsi @@ -128,7 +128,7 @@ mcuboot-button0 = &button0; mcuboot-led0 = &led0; watchdog0 = &wdt0; - spi-flash0 = &gd25lb256; + spi-flash0 = &gd25wb256; }; }; @@ -195,22 +195,22 @@ arduino_spi: &spi3 { pinctrl-1 = <&spi3_sleep>; pinctrl-names = "default", "sleep"; - gd25lb256: gd25lb256e3ir@1 { + gd25wb256: gd25wb256e3ir@1 { compatible = "jedec,spi-nor"; status = "disabled"; reg = <1>; - spi-max-frequency = <60000000>; - jedec-id = [c8 67 19]; - sfdp-bfp = [ - e5 20 ea ff ff ff ff 0f 44 eb 08 6b 00 3b 00 bb - fe ff ff ff ff ff 00 ff ff ff 44 eb 0c 20 0f 52 - 10 d8 00 ff d5 31 b1 fe 82 e4 14 4c ec 60 06 33 - 7a 75 7a 75 04 bd d5 5c 29 06 74 00 08 50 00 01 - ]; + spi-max-frequency = <8000000>; size = <268435456>; has-dpd; t-enter-dpd = <3000>; - t-exit-dpd = <30000>; + t-exit-dpd = <40000>; + sfdp-bfp = [ + e5 20 f3 ff ff ff ff 0f 44 eb 08 6b 08 3b 42 bb + ee ff ff ff ff ff 00 ff ff ff 00 ff 0c 20 0f 52 + 10 d8 00 ff 44 7a c9 fe 83 67 26 62 ec 82 18 44 + 7a 75 7a 75 04 c4 d5 5c 00 06 74 00 08 50 00 01 + ]; + jedec-id = [c8 65 19]; }; }; diff --git a/boards/arm/nrf9161dk_nrf9161/nrf9161dk_nrf9161_common_0_7_0.dtsi b/boards/arm/nrf9161dk_nrf9161/nrf9161dk_nrf9161_common_0_7_0.dtsi new file mode 100644 index 00000000000..ae67df77b22 --- /dev/null +++ b/boards/arm/nrf9161dk_nrf9161/nrf9161dk_nrf9161_common_0_7_0.dtsi @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&spi3 { + gd25lb256: gd25lb256e3ir@1 { + compatible = "jedec,spi-nor"; + status = "okay"; + reg = <1>; + spi-max-frequency = <60000000>; + jedec-id = [c8 67 19]; + sfdp-bfp = [ + e5 20 ea ff ff ff ff 0f 44 eb 08 6b 00 3b 00 bb + fe ff ff ff ff ff 00 ff ff ff 44 eb 0c 20 0f 52 + 10 d8 00 ff d5 31 b1 fe 82 e4 14 4c ec 60 06 33 + 7a 75 7a 75 04 bd d5 5c 29 06 74 00 08 50 00 01 + ]; + size = <268435456>; + has-dpd; + t-enter-dpd = <3000>; + t-exit-dpd = <30000>; + }; +}; + +/ { + aliases { + spi-flash0 = &gd25lb256; + }; +}; diff --git a/boards/arm/nrf9161dk_nrf9161/nrf9161dk_nrf9161_ns_0_7_0.overlay b/boards/arm/nrf9161dk_nrf9161/nrf9161dk_nrf9161_ns_0_7_0.overlay new file mode 100644 index 00000000000..22e9f970dab --- /dev/null +++ b/boards/arm/nrf9161dk_nrf9161/nrf9161dk_nrf9161_ns_0_7_0.overlay @@ -0,0 +1,7 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "nrf9161dk_nrf9161_common_0_7_0.dtsi" diff --git a/boards/arm/nrf9161dk_nrf9161/revision.cmake b/boards/arm/nrf9161dk_nrf9161/revision.cmake new file mode 100644 index 00000000000..2a899f61a83 --- /dev/null +++ b/boards/arm/nrf9161dk_nrf9161/revision.cmake @@ -0,0 +1,8 @@ +# Copyright (c) 2023 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +board_check_revision( + FORMAT MAJOR.MINOR.PATCH + DEFAULT_REVISION 0.9.0 + VALID_REVISIONS 0.7.0 0.9.0 +) diff --git a/boards/arm/pan1770_evb/pan1770_evb.dts b/boards/arm/pan1770_evb/pan1770_evb.dts index d8fbe8fe9df..2b2a9d561a7 100644 --- a/boards/arm/pan1770_evb/pan1770_evb.dts +++ b/boards/arm/pan1770_evb/pan1770_evb.dts @@ -137,6 +137,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/pan1770_evb/pan1770_evb_defconfig b/boards/arm/pan1770_evb/pan1770_evb_defconfig index 1192aca064e..1d90cb82c25 100644 --- a/boards/arm/pan1770_evb/pan1770_evb_defconfig +++ b/boards/arm/pan1770_evb/pan1770_evb_defconfig @@ -26,8 +26,5 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - # using pinctrl CONFIG_PINCTRL=y diff --git a/boards/arm/pan1780_evb/pan1780_evb.dts b/boards/arm/pan1780_evb/pan1780_evb.dts index 4f4dbca98ac..da9a7e7fdb8 100644 --- a/boards/arm/pan1780_evb/pan1780_evb.dts +++ b/boards/arm/pan1780_evb/pan1780_evb.dts @@ -137,6 +137,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/pan1780_evb/pan1780_evb_defconfig b/boards/arm/pan1780_evb/pan1780_evb_defconfig index 0c459f39aac..ca1a58c3b74 100644 --- a/boards/arm/pan1780_evb/pan1780_evb_defconfig +++ b/boards/arm/pan1780_evb/pan1780_evb_defconfig @@ -26,8 +26,5 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - # using pinctrl CONFIG_PINCTRL=y diff --git a/boards/arm/pan1781_evb/pan1781_evb.dts b/boards/arm/pan1781_evb/pan1781_evb.dts index 7d9b7028e35..8ab6af51ea9 100644 --- a/boards/arm/pan1781_evb/pan1781_evb.dts +++ b/boards/arm/pan1781_evb/pan1781_evb.dts @@ -98,6 +98,10 @@ clock-prescaler = <8>; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/pan1781_evb/pan1781_evb_defconfig b/boards/arm/pan1781_evb/pan1781_evb_defconfig index 76436dd2ccd..dacffd42175 100644 --- a/boards/arm/pan1781_evb/pan1781_evb_defconfig +++ b/boards/arm/pan1781_evb/pan1781_evb_defconfig @@ -26,8 +26,5 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - # using pinctrl CONFIG_PINCTRL=y diff --git a/boards/arm/pan1782_evb/pan1782_evb.dts b/boards/arm/pan1782_evb/pan1782_evb.dts index cf169e41d87..fdc86f792ec 100644 --- a/boards/arm/pan1782_evb/pan1782_evb.dts +++ b/boards/arm/pan1782_evb/pan1782_evb.dts @@ -98,6 +98,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/pan1782_evb/pan1782_evb_defconfig b/boards/arm/pan1782_evb/pan1782_evb_defconfig index 0bdeb9d9127..e6a6cc1d4a5 100644 --- a/boards/arm/pan1782_evb/pan1782_evb_defconfig +++ b/boards/arm/pan1782_evb/pan1782_evb_defconfig @@ -26,9 +26,6 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - # using pinctrl CONFIG_PINCTRL=y diff --git a/boards/arm/particle_argon/particle_argon.dts b/boards/arm/particle_argon/particle_argon.dts index fa2314b6fb2..a88199fe40b 100644 --- a/boards/arm/particle_argon/particle_argon.dts +++ b/boards/arm/particle_argon/particle_argon.dts @@ -32,6 +32,10 @@ }; }; +&uicr { + gpio-as-nreset; +}; + &uart1 { /* ESP32 */ compatible = "nordic,nrf-uarte"; current-speed = <921600>; diff --git a/boards/arm/particle_argon/particle_argon_defconfig b/boards/arm/particle_argon/particle_argon_defconfig index 784364e9edc..c29f62babd4 100644 --- a/boards/arm/particle_argon/particle_argon_defconfig +++ b/boards/arm/particle_argon/particle_argon_defconfig @@ -21,7 +21,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/particle_boron/particle_boron.dts b/boards/arm/particle_boron/particle_boron.dts index 3e20eddb94f..06e75a5a52e 100644 --- a/boards/arm/particle_boron/particle_boron.dts +++ b/boards/arm/particle_boron/particle_boron.dts @@ -36,6 +36,10 @@ }; }; +&uicr { + gpio-as-nreset; +}; + &i2c1 { /* power monitoring */ compatible = "nordic,nrf-twi"; status = "okay"; diff --git a/boards/arm/particle_boron/particle_boron_defconfig b/boards/arm/particle_boron/particle_boron_defconfig index 6963e9d9393..622a3169073 100644 --- a/boards/arm/particle_boron/particle_boron_defconfig +++ b/boards/arm/particle_boron/particle_boron_defconfig @@ -20,9 +20,6 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y # Fix the priority to enable the modem line's serial buffer diff --git a/boards/arm/particle_xenon/particle_xenon.dts b/boards/arm/particle_xenon/particle_xenon.dts index 22252c077f3..2d64ba119b2 100644 --- a/boards/arm/particle_xenon/particle_xenon.dts +++ b/boards/arm/particle_xenon/particle_xenon.dts @@ -33,6 +33,10 @@ }; }; +&uicr { + gpio-as-nreset; +}; + &uart1 { /* feather UART2 */ compatible = "nordic,nrf-uarte"; current-speed = <115200>; diff --git a/boards/arm/particle_xenon/particle_xenon_defconfig b/boards/arm/particle_xenon/particle_xenon_defconfig index 5f90beabba0..2495d886b41 100644 --- a/boards/arm/particle_xenon/particle_xenon_defconfig +++ b/boards/arm/particle_xenon/particle_xenon_defconfig @@ -20,7 +20,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/pinetime_devkit0/pinetime_devkit0.dts b/boards/arm/pinetime_devkit0/pinetime_devkit0.dts index baad177740c..207c8934880 100644 --- a/boards/arm/pinetime_devkit0/pinetime_devkit0.dts +++ b/boards/arm/pinetime_devkit0/pinetime_devkit0.dts @@ -83,10 +83,18 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; +&uicr { + nfct-pins-as-gpios; +}; + &gpio0 { status = "okay"; diff --git a/boards/arm/pinetime_devkit0/pinetime_devkit0_defconfig b/boards/arm/pinetime_devkit0/pinetime_devkit0_defconfig index 4e48c259730..fc8f45eceaa 100644 --- a/boards/arm/pinetime_devkit0/pinetime_devkit0_defconfig +++ b/boards/arm/pinetime_devkit0/pinetime_devkit0_defconfig @@ -13,10 +13,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# use P0.09 and P0.10 as GPIOs -CONFIG_NFCT_PINS_AS_GPIOS=y - -# use P0.21 as RST -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/pinnacle_100_dvk/pinnacle_100_dvk.dts b/boards/arm/pinnacle_100_dvk/pinnacle_100_dvk.dts index 62664e67b5e..19f9e6ff41e 100644 --- a/boards/arm/pinnacle_100_dvk/pinnacle_100_dvk.dts +++ b/boards/arm/pinnacle_100_dvk/pinnacle_100_dvk.dts @@ -88,6 +88,10 @@ status ="okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status ="okay"; }; diff --git a/boards/arm/pinnacle_100_dvk/pinnacle_100_dvk_defconfig b/boards/arm/pinnacle_100_dvk/pinnacle_100_dvk_defconfig index 7f11757ab5d..010c72e8c07 100644 --- a/boards/arm/pinnacle_100_dvk/pinnacle_100_dvk_defconfig +++ b/boards/arm/pinnacle_100_dvk/pinnacle_100_dvk_defconfig @@ -22,9 +22,6 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# Additional board options -CONFIG_GPIO_AS_PINRESET=y - # 32KHz clock source CONFIG_CLOCK_CONTROL_NRF_K32SRC_XTAL=y CONFIG_CLOCK_CONTROL_NRF_K32SRC_150PPM=y diff --git a/boards/arm/rak4631_nrf52840/rak4631_nrf52840.dts b/boards/arm/rak4631_nrf52840/rak4631_nrf52840.dts index 43b2dd0de28..454c4bb84b1 100644 --- a/boards/arm/rak4631_nrf52840/rak4631_nrf52840.dts +++ b/boards/arm/rak4631_nrf52840/rak4631_nrf52840.dts @@ -48,6 +48,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/rak4631_nrf52840/rak4631_nrf52840_defconfig b/boards/arm/rak4631_nrf52840/rak4631_nrf52840_defconfig index 80da4e05041..4cc2863efab 100644 --- a/boards/arm/rak4631_nrf52840/rak4631_nrf52840_defconfig +++ b/boards/arm/rak4631_nrf52840/rak4631_nrf52840_defconfig @@ -22,7 +22,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/raytac_mdbt50q_db_33_nrf52833/raytac_mdbt50q_db_33_nrf52833.dts b/boards/arm/raytac_mdbt50q_db_33_nrf52833/raytac_mdbt50q_db_33_nrf52833.dts index 6a530e687f6..a3ada26e811 100644 --- a/boards/arm/raytac_mdbt50q_db_33_nrf52833/raytac_mdbt50q_db_33_nrf52833.dts +++ b/boards/arm/raytac_mdbt50q_db_33_nrf52833/raytac_mdbt50q_db_33_nrf52833.dts @@ -93,6 +93,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/raytac_mdbt50q_db_33_nrf52833/raytac_mdbt50q_db_33_nrf52833_defconfig b/boards/arm/raytac_mdbt50q_db_33_nrf52833/raytac_mdbt50q_db_33_nrf52833_defconfig index 7ed9a1ffe8b..1212ba8928f 100644 --- a/boards/arm/raytac_mdbt50q_db_33_nrf52833/raytac_mdbt50q_db_33_nrf52833_defconfig +++ b/boards/arm/raytac_mdbt50q_db_33_nrf52833/raytac_mdbt50q_db_33_nrf52833_defconfig @@ -26,7 +26,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/raytac_mdbt50q_db_40_nrf52840/raytac_mdbt50q_db_40_nrf52840.dts b/boards/arm/raytac_mdbt50q_db_40_nrf52840/raytac_mdbt50q_db_40_nrf52840.dts index 2414b8ea517..bd1a2f06855 100644 --- a/boards/arm/raytac_mdbt50q_db_40_nrf52840/raytac_mdbt50q_db_40_nrf52840.dts +++ b/boards/arm/raytac_mdbt50q_db_40_nrf52840/raytac_mdbt50q_db_40_nrf52840.dts @@ -93,6 +93,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/raytac_mdbt50q_db_40_nrf52840/raytac_mdbt50q_db_40_nrf52840_defconfig b/boards/arm/raytac_mdbt50q_db_40_nrf52840/raytac_mdbt50q_db_40_nrf52840_defconfig index 973b83c9d62..438343c6d9b 100644 --- a/boards/arm/raytac_mdbt50q_db_40_nrf52840/raytac_mdbt50q_db_40_nrf52840_defconfig +++ b/boards/arm/raytac_mdbt50q_db_40_nrf52840/raytac_mdbt50q_db_40_nrf52840_defconfig @@ -26,7 +26,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/reel_board/dts/reel_board.dtsi b/boards/arm/reel_board/dts/reel_board.dtsi index 6289cf0ec6d..028a5d86a78 100644 --- a/boards/arm/reel_board/dts/reel_board.dtsi +++ b/boards/arm/reel_board/dts/reel_board.dtsi @@ -94,6 +94,10 @@ }; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/reel_board/reel_board_defconfig b/boards/arm/reel_board/reel_board_defconfig index 7ec7b36871b..850506692ba 100644 --- a/boards/arm/reel_board/reel_board_defconfig +++ b/boards/arm/reel_board/reel_board_defconfig @@ -17,7 +17,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/reel_board/reel_board_v2_defconfig b/boards/arm/reel_board/reel_board_v2_defconfig index a7f80f970e8..2ac0610f78c 100644 --- a/boards/arm/reel_board/reel_board_v2_defconfig +++ b/boards/arm/reel_board/reel_board_v2_defconfig @@ -17,7 +17,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/ruuvi_ruuvitag/ruuvi_ruuvitag_defconfig b/boards/arm/ruuvi_ruuvitag/ruuvi_ruuvitag_defconfig index bd247788458..7e28dde8423 100644 --- a/boards/arm/ruuvi_ruuvitag/ruuvi_ruuvitag_defconfig +++ b/boards/arm/ruuvi_ruuvitag/ruuvi_ruuvitag_defconfig @@ -14,8 +14,4 @@ CONFIG_USE_SEGGER_RTT=y # Enable GPIO CONFIG_GPIO=y -# additional board options -# set y to disable R button -CONFIG_GPIO_AS_PINRESET=n - CONFIG_PINCTRL=y diff --git a/boards/arm/sparkfun_thing_plus_nrf9160/doc/index.rst b/boards/arm/sparkfun_thing_plus_nrf9160/doc/index.rst index 44366c49a39..aa1a0e6279d 100644 --- a/boards/arm/sparkfun_thing_plus_nrf9160/doc/index.rst +++ b/boards/arm/sparkfun_thing_plus_nrf9160/doc/index.rst @@ -91,9 +91,6 @@ Building an application In most cases you'll want to use the ``ns`` target with any of the Zephyr or Nordic based examples. -.. note:: - Trusted Firmware-M (TF-M) and building the ``ns`` target is not supported for this board. - Some of the examples do not use secure mode, so they do not required the ``ns`` suffix. A great example of this is the `hello_world` below. diff --git a/boards/arm/sparkfun_thing_plus_nrf9160/sparkfun_thing_plus_nrf9160_common.dtsi b/boards/arm/sparkfun_thing_plus_nrf9160/sparkfun_thing_plus_nrf9160_common.dtsi index b85c00ea5b2..2658ee5eaa4 100644 --- a/boards/arm/sparkfun_thing_plus_nrf9160/sparkfun_thing_plus_nrf9160_common.dtsi +++ b/boards/arm/sparkfun_thing_plus_nrf9160/sparkfun_thing_plus_nrf9160_common.dtsi @@ -185,22 +185,19 @@ slot0_partition: partition@10000 { label = "image-0"; }; - slot0_ns_partition: partition@40000 { + slot0_ns_partition: partition@50000 { label = "image-0-nonsecure"; }; slot1_partition: partition@80000 { label = "image-1"; }; - slot1_ns_partition: partition@b0000 { + slot1_ns_partition: partition@c0000 { label = "image-1-nonsecure"; }; - scratch_partition: partition@f0000 { - label = "image-scratch"; - reg = <0x000f0000 0xa000>; - }; - storage_partition: partition@fa000 { + /* 0xf0000 to 0xf7fff reserved for TF-M partitions */ + storage_partition: partition@f8000 { label = "storage"; - reg = <0x000fa000 0x00006000>; + reg = <0x000f8000 0x00008000>; }; }; }; diff --git a/boards/arm/sparkfun_thing_plus_nrf9160/sparkfun_thing_plus_nrf9160_ns.yaml b/boards/arm/sparkfun_thing_plus_nrf9160/sparkfun_thing_plus_nrf9160_ns.yaml index e667a231774..adbef49e2b0 100644 --- a/boards/arm/sparkfun_thing_plus_nrf9160/sparkfun_thing_plus_nrf9160_ns.yaml +++ b/boards/arm/sparkfun_thing_plus_nrf9160/sparkfun_thing_plus_nrf9160_ns.yaml @@ -7,7 +7,7 @@ toolchain: - xtools - zephyr ram: 128 -flash: 256 +flash: 192 supported: - i2c - pwm diff --git a/boards/arm/sparkfun_thing_plus_nrf9160/sparkfun_thing_plus_nrf9160_partition_conf.dtsi b/boards/arm/sparkfun_thing_plus_nrf9160/sparkfun_thing_plus_nrf9160_partition_conf.dtsi index 2422b2fdc52..64591fbf900 100644 --- a/boards/arm/sparkfun_thing_plus_nrf9160/sparkfun_thing_plus_nrf9160_partition_conf.dtsi +++ b/boards/arm/sparkfun_thing_plus_nrf9160/sparkfun_thing_plus_nrf9160_partition_conf.dtsi @@ -23,19 +23,19 @@ */ &slot0_partition { - reg = <0x00010000 0x30000>; + reg = <0x00010000 0x40000>; }; &slot0_ns_partition { - reg = <0x00040000 0x40000>; + reg = <0x00050000 0x30000>; }; &slot1_partition { - reg = <0x00080000 0x30000>; + reg = <0x00080000 0x40000>; }; &slot1_ns_partition { - reg = <0x000b0000 0x40000>; + reg = <0x000c0000 0x30000>; }; /* Default SRAM planning when building for nRF9160 with diff --git a/boards/arm/thingy53_nrf5340/Kconfig.defconfig b/boards/arm/thingy53_nrf5340/Kconfig.defconfig index e4d9e63b7f1..b6080de9a80 100644 --- a/boards/arm/thingy53_nrf5340/Kconfig.defconfig +++ b/boards/arm/thingy53_nrf5340/Kconfig.defconfig @@ -8,6 +8,12 @@ if BOARD_THINGY53_NRF5340_CPUAPP || BOARD_THINGY53_NRF5340_CPUAPP_NS config BOARD default "thingy53_nrf5340_cpuapp" +config BOOTLOADER_MCUBOOT + default y if !MCUBOOT + +config BOARD_ENABLE_CPUNET + default y if !MCUBOOT + # Code Partition: # # For the secure version of the board the firmware is linked at the beginning @@ -135,6 +141,13 @@ endif # LOG endif # BOARD_SERIAL_BACKEND_CDC_ACM +# By default, a USB CDC ACM instance is already enabled in the board's DTS. +# It is not necessary for nRF Connect SDK to add another instance if MCUBoot +# bootloader is built as a child image. +config MCUBOOT_USB_SUPPORT + bool + default n + endif # BOARD_THINGY53_NRF5340_CPUAPP || BOARD_THINGY53_NRF5340_CPUAPP_NS if BOARD_THINGY53_NRF5340_CPUNET diff --git a/boards/arm/thingy53_nrf5340/doc/index.rst b/boards/arm/thingy53_nrf5340/doc/index.rst index 3a77deee968..4544ea7ed79 100644 --- a/boards/arm/thingy53_nrf5340/doc/index.rst +++ b/boards/arm/thingy53_nrf5340/doc/index.rst @@ -22,9 +22,6 @@ The nrf5340dk_nrf5340_cpuapp build target provides support for the application core on the nRF5340 SoC. The nrf5340dk_nrf5340_cpunet build target provides support for the network core on the nRF5340 SoC. -.. note:: - Trusted Firmware-M (TF-M) and building the ``ns`` target is not supported for this board. - The `Nordic Semiconductor Infocenter`_ contains the processor's information and the datasheet. diff --git a/boards/arm/thingy53_nrf5340/pm_static_thingy53_nrf5340_cpuapp.yml b/boards/arm/thingy53_nrf5340/pm_static_thingy53_nrf5340_cpuapp.yml new file mode 100644 index 00000000000..7a48d51ec33 --- /dev/null +++ b/boards/arm/thingy53_nrf5340/pm_static_thingy53_nrf5340_cpuapp.yml @@ -0,0 +1,55 @@ +app: + address: 0x10200 + region: flash_primary + size: 0xdfe00 +mcuboot: + address: 0x0 + region: flash_primary + size: 0x10000 +mcuboot_pad: + address: 0x10000 + region: flash_primary + size: 0x200 +mcuboot_primary: + address: 0x10000 + orig_span: &id001 + - mcuboot_pad + - app + region: flash_primary + size: 0xe0000 + span: *id001 +mcuboot_primary_app: + address: 0x10200 + orig_span: &id002 + - app + region: flash_primary + size: 0xdfe00 + span: *id002 +settings_storage: + address: 0xf0000 + region: flash_primary + size: 0x10000 +mcuboot_primary_1: + address: 0x0 + size: 0x40000 + device: flash_ctrl + region: ram_flash +mcuboot_secondary: + address: 0x00000 + size: 0xe0000 + device: MX25R64 + region: external_flash +mcuboot_secondary_1: + address: 0xe0000 + size: 0x40000 + device: MX25R64 + region: external_flash +external_flash: + address: 0x120000 + size: 0x6e0000 + device: MX25R64 + region: external_flash +pcd_sram: + address: 0x20000000 + size: 0x2000 + region: sram_primary diff --git a/boards/arm/thingy53_nrf5340/pm_static_thingy53_nrf5340_cpuapp_ns.yml b/boards/arm/thingy53_nrf5340/pm_static_thingy53_nrf5340_cpuapp_ns.yml new file mode 100644 index 00000000000..70ffe6d9c12 --- /dev/null +++ b/boards/arm/thingy53_nrf5340/pm_static_thingy53_nrf5340_cpuapp_ns.yml @@ -0,0 +1,73 @@ +mcuboot: + address: 0x0 + region: flash_primary + size: 0x10000 +mcuboot_pad: + address: 0x10000 + region: flash_primary + size: 0x200 +tfm_secure: + address: 0x10000 + size: 0xc000 + span: [mcuboot_pad, tfm] +tfm_nonsecure: + address: 0x1c000 + size: 0xd4000 + span: [app] +tfm: + address: 0x10200 + region: flash_primary + size: 0xbe00 +app: + address: 0x1c000 + region: flash_primary + size: 0xd4000 +mcuboot_primary: + address: 0x10000 + orig_span: &id001 + - mcuboot_pad + - tfm + - app + region: flash_primary + size: 0xe0000 + span: *id001 +mcuboot_primary_app: + address: 0x10200 + orig_span: &id002 + - tfm + - app + region: flash_primary + size: 0xdfe00 + span: *id002 +nonsecure_storage: + address: 0xf0000 + size: 0x10000 + span: [settings_storage] +settings_storage: + address: 0xf0000 + region: flash_primary + size: 0x10000 +mcuboot_primary_1: + address: 0x0 + size: 0x40000 + device: flash_ctrl + region: ram_flash +mcuboot_secondary: + address: 0x00000 + size: 0xe0000 + device: MX25R64 + region: external_flash +mcuboot_secondary_1: + address: 0xe0000 + size: 0x40000 + device: MX25R64 + region: external_flash +external_flash: + address: 0x120000 + size: 0x6e0000 + device: MX25R64 + region: external_flash +pcd_sram: + address: 0x20000000 + size: 0x2000 + region: sram_primary diff --git a/boards/arm/thingy53_nrf5340/thingy53_nrf5340_common.dtsi b/boards/arm/thingy53_nrf5340/thingy53_nrf5340_common.dtsi index 341bb1305e1..515fc3bea7b 100644 --- a/boards/arm/thingy53_nrf5340/thingy53_nrf5340_common.dtsi +++ b/boards/arm/thingy53_nrf5340/thingy53_nrf5340_common.dtsi @@ -16,6 +16,7 @@ zephyr,bt-hci-rpmsg-ipc = &ipc0; nordic,802154-spinel-ipc = &ipc0; zephyr,ieee802154 = &ieee802154; + nordic,pm-ext-flash = &mx25r64; }; buttons { diff --git a/boards/arm/ubx_bmd300eval_nrf52832/ubx_bmd300eval_nrf52832.dts b/boards/arm/ubx_bmd300eval_nrf52832/ubx_bmd300eval_nrf52832.dts index a248d8f650e..9594b760488 100644 --- a/boards/arm/ubx_bmd300eval_nrf52832/ubx_bmd300eval_nrf52832.dts +++ b/boards/arm/ubx_bmd300eval_nrf52832/ubx_bmd300eval_nrf52832.dts @@ -137,6 +137,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/ubx_bmd300eval_nrf52832/ubx_bmd300eval_nrf52832_defconfig b/boards/arm/ubx_bmd300eval_nrf52832/ubx_bmd300eval_nrf52832_defconfig index 769e28f5846..801393790fe 100644 --- a/boards/arm/ubx_bmd300eval_nrf52832/ubx_bmd300eval_nrf52832_defconfig +++ b/boards/arm/ubx_bmd300eval_nrf52832/ubx_bmd300eval_nrf52832_defconfig @@ -23,7 +23,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/ubx_bmd330eval_nrf52810/ubx_bmd330eval_nrf52810.dts b/boards/arm/ubx_bmd330eval_nrf52810/ubx_bmd330eval_nrf52810.dts index 5afd4960694..c65221eb38c 100644 --- a/boards/arm/ubx_bmd330eval_nrf52810/ubx_bmd330eval_nrf52810.dts +++ b/boards/arm/ubx_bmd330eval_nrf52810/ubx_bmd330eval_nrf52810.dts @@ -137,6 +137,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/ubx_bmd330eval_nrf52810/ubx_bmd330eval_nrf52810_defconfig b/boards/arm/ubx_bmd330eval_nrf52810/ubx_bmd330eval_nrf52810_defconfig index 6fd69454784..4f8a5c5a8da 100644 --- a/boards/arm/ubx_bmd330eval_nrf52810/ubx_bmd330eval_nrf52810_defconfig +++ b/boards/arm/ubx_bmd330eval_nrf52810/ubx_bmd330eval_nrf52810_defconfig @@ -26,7 +26,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/ubx_bmd340eval_nrf52840/ubx_bmd340eval_nrf52840.dts b/boards/arm/ubx_bmd340eval_nrf52840/ubx_bmd340eval_nrf52840.dts index f7fbee05909..070730c69d2 100644 --- a/boards/arm/ubx_bmd340eval_nrf52840/ubx_bmd340eval_nrf52840.dts +++ b/boards/arm/ubx_bmd340eval_nrf52840/ubx_bmd340eval_nrf52840.dts @@ -136,6 +136,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/ubx_bmd340eval_nrf52840/ubx_bmd340eval_nrf52840_defconfig b/boards/arm/ubx_bmd340eval_nrf52840/ubx_bmd340eval_nrf52840_defconfig index 4a1d3a6075c..37271c2ce81 100644 --- a/boards/arm/ubx_bmd340eval_nrf52840/ubx_bmd340eval_nrf52840_defconfig +++ b/boards/arm/ubx_bmd340eval_nrf52840/ubx_bmd340eval_nrf52840_defconfig @@ -26,7 +26,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/ubx_bmd345eval_nrf52840/ubx_bmd345eval_nrf52840.dts b/boards/arm/ubx_bmd345eval_nrf52840/ubx_bmd345eval_nrf52840.dts index 2db4e005b04..68b3755123b 100644 --- a/boards/arm/ubx_bmd345eval_nrf52840/ubx_bmd345eval_nrf52840.dts +++ b/boards/arm/ubx_bmd345eval_nrf52840/ubx_bmd345eval_nrf52840.dts @@ -152,6 +152,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/ubx_bmd345eval_nrf52840/ubx_bmd345eval_nrf52840_defconfig b/boards/arm/ubx_bmd345eval_nrf52840/ubx_bmd345eval_nrf52840_defconfig index 6a6d80f1b2b..4a35f6e612f 100644 --- a/boards/arm/ubx_bmd345eval_nrf52840/ubx_bmd345eval_nrf52840_defconfig +++ b/boards/arm/ubx_bmd345eval_nrf52840/ubx_bmd345eval_nrf52840_defconfig @@ -26,7 +26,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/ubx_bmd360eval_nrf52811/ubx_bmd360eval_nrf52811.dts b/boards/arm/ubx_bmd360eval_nrf52811/ubx_bmd360eval_nrf52811.dts index 842c882ac7f..5b8679cc065 100644 --- a/boards/arm/ubx_bmd360eval_nrf52811/ubx_bmd360eval_nrf52811.dts +++ b/boards/arm/ubx_bmd360eval_nrf52811/ubx_bmd360eval_nrf52811.dts @@ -137,6 +137,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/ubx_bmd360eval_nrf52811/ubx_bmd360eval_nrf52811_defconfig b/boards/arm/ubx_bmd360eval_nrf52811/ubx_bmd360eval_nrf52811_defconfig index b2996dba82d..7a63bc69bf2 100644 --- a/boards/arm/ubx_bmd360eval_nrf52811/ubx_bmd360eval_nrf52811_defconfig +++ b/boards/arm/ubx_bmd360eval_nrf52811/ubx_bmd360eval_nrf52811_defconfig @@ -26,7 +26,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/ubx_bmd380eval_nrf52840/ubx_bmd380eval_nrf52840.dts b/boards/arm/ubx_bmd380eval_nrf52840/ubx_bmd380eval_nrf52840.dts index 1c4623b52fe..d7a21c98b44 100644 --- a/boards/arm/ubx_bmd380eval_nrf52840/ubx_bmd380eval_nrf52840.dts +++ b/boards/arm/ubx_bmd380eval_nrf52840/ubx_bmd380eval_nrf52840.dts @@ -97,6 +97,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/ubx_bmd380eval_nrf52840/ubx_bmd380eval_nrf52840_defconfig b/boards/arm/ubx_bmd380eval_nrf52840/ubx_bmd380eval_nrf52840_defconfig index d0a583edbe8..c20dc6fe9f3 100644 --- a/boards/arm/ubx_bmd380eval_nrf52840/ubx_bmd380eval_nrf52840_defconfig +++ b/boards/arm/ubx_bmd380eval_nrf52840/ubx_bmd380eval_nrf52840_defconfig @@ -27,7 +27,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/ubx_evkannab1_nrf52832/ubx_evkannab1_nrf52832.dts b/boards/arm/ubx_evkannab1_nrf52832/ubx_evkannab1_nrf52832.dts index b183b17bc41..2c0f051d25f 100644 --- a/boards/arm/ubx_evkannab1_nrf52832/ubx_evkannab1_nrf52832.dts +++ b/boards/arm/ubx_evkannab1_nrf52832/ubx_evkannab1_nrf52832.dts @@ -133,6 +133,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/ubx_evkannab1_nrf52832/ubx_evkannab1_nrf52832_defconfig b/boards/arm/ubx_evkannab1_nrf52832/ubx_evkannab1_nrf52832_defconfig index 4a289cc2597..93127beefef 100644 --- a/boards/arm/ubx_evkannab1_nrf52832/ubx_evkannab1_nrf52832_defconfig +++ b/boards/arm/ubx_evkannab1_nrf52832/ubx_evkannab1_nrf52832_defconfig @@ -26,7 +26,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/ubx_evkninab1_nrf52832/ubx_evkninab1_nrf52832.dts b/boards/arm/ubx_evkninab1_nrf52832/ubx_evkninab1_nrf52832.dts index 691705b3a91..849c96c5e6f 100644 --- a/boards/arm/ubx_evkninab1_nrf52832/ubx_evkninab1_nrf52832.dts +++ b/boards/arm/ubx_evkninab1_nrf52832/ubx_evkninab1_nrf52832.dts @@ -133,6 +133,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/ubx_evkninab1_nrf52832/ubx_evkninab1_nrf52832_defconfig b/boards/arm/ubx_evkninab1_nrf52832/ubx_evkninab1_nrf52832_defconfig index b7064633b25..b44e54e3d18 100644 --- a/boards/arm/ubx_evkninab1_nrf52832/ubx_evkninab1_nrf52832_defconfig +++ b/boards/arm/ubx_evkninab1_nrf52832/ubx_evkninab1_nrf52832_defconfig @@ -26,7 +26,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/ubx_evkninab3_nrf52840/ubx_evkninab3_nrf52840.dts b/boards/arm/ubx_evkninab3_nrf52840/ubx_evkninab3_nrf52840.dts index 19b4e25a5af..5e15c5cc232 100644 --- a/boards/arm/ubx_evkninab3_nrf52840/ubx_evkninab3_nrf52840.dts +++ b/boards/arm/ubx_evkninab3_nrf52840/ubx_evkninab3_nrf52840.dts @@ -129,6 +129,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/ubx_evkninab3_nrf52840/ubx_evkninab3_nrf52840_defconfig b/boards/arm/ubx_evkninab3_nrf52840/ubx_evkninab3_nrf52840_defconfig index ca03d00a8e7..f9cabe94b4a 100644 --- a/boards/arm/ubx_evkninab3_nrf52840/ubx_evkninab3_nrf52840_defconfig +++ b/boards/arm/ubx_evkninab3_nrf52840/ubx_evkninab3_nrf52840_defconfig @@ -26,7 +26,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/ubx_evkninab4_nrf52833/ubx_evkninab4_nrf52833.dts b/boards/arm/ubx_evkninab4_nrf52833/ubx_evkninab4_nrf52833.dts index 783d69b4788..521f9f6aa9e 100644 --- a/boards/arm/ubx_evkninab4_nrf52833/ubx_evkninab4_nrf52833.dts +++ b/boards/arm/ubx_evkninab4_nrf52833/ubx_evkninab4_nrf52833.dts @@ -134,6 +134,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/ubx_evkninab4_nrf52833/ubx_evkninab4_nrf52833_defconfig b/boards/arm/ubx_evkninab4_nrf52833/ubx_evkninab4_nrf52833_defconfig index 96d99738526..c06ad795868 100644 --- a/boards/arm/ubx_evkninab4_nrf52833/ubx_evkninab4_nrf52833_defconfig +++ b/boards/arm/ubx_evkninab4_nrf52833/ubx_evkninab4_nrf52833_defconfig @@ -26,7 +26,4 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/vmu_rt1170/doc/index.rst b/boards/arm/vmu_rt1170/doc/index.rst index 3514bf15893..28d57464b4f 100644 --- a/boards/arm/vmu_rt1170/doc/index.rst +++ b/boards/arm/vmu_rt1170/doc/index.rst @@ -216,10 +216,8 @@ Configuring a Debug Probe A debug probe is used for both flashing and debugging the board. -.. _Using J-Link RT1170: - Using J-Link ---------------------------------- +------------ Install the :ref:`jlink-debug-host-tools` and make sure they are in your search path. diff --git a/boards/arm/we_ophelia1ev_nrf52805/we_ophelia1ev_nrf52805.dts b/boards/arm/we_ophelia1ev_nrf52805/we_ophelia1ev_nrf52805.dts index b9b9bfcedb2..4d7c3d8cc0b 100644 --- a/boards/arm/we_ophelia1ev_nrf52805/we_ophelia1ev_nrf52805.dts +++ b/boards/arm/we_ophelia1ev_nrf52805/we_ophelia1ev_nrf52805.dts @@ -61,6 +61,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpio0 { status = "okay"; }; diff --git a/boards/arm/we_ophelia1ev_nrf52805/we_ophelia1ev_nrf52805_defconfig b/boards/arm/we_ophelia1ev_nrf52805/we_ophelia1ev_nrf52805_defconfig index d1a44910ced..88c4ddb2023 100644 --- a/boards/arm/we_ophelia1ev_nrf52805/we_ophelia1ev_nrf52805_defconfig +++ b/boards/arm/we_ophelia1ev_nrf52805/we_ophelia1ev_nrf52805_defconfig @@ -21,9 +21,6 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# Additional board options -CONFIG_GPIO_AS_PINRESET=y - # Use internal oscillator CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC=y diff --git a/boards/arm/we_proteus2ev_nrf52832/we_proteus2ev_nrf52832.dts b/boards/arm/we_proteus2ev_nrf52832/we_proteus2ev_nrf52832.dts index 4cd193c4162..ef7eede1f75 100644 --- a/boards/arm/we_proteus2ev_nrf52832/we_proteus2ev_nrf52832.dts +++ b/boards/arm/we_proteus2ev_nrf52832/we_proteus2ev_nrf52832.dts @@ -56,6 +56,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/we_proteus2ev_nrf52832/we_proteus2ev_nrf52832_defconfig b/boards/arm/we_proteus2ev_nrf52832/we_proteus2ev_nrf52832_defconfig index ab2356cf412..249c4a8e394 100644 --- a/boards/arm/we_proteus2ev_nrf52832/we_proteus2ev_nrf52832_defconfig +++ b/boards/arm/we_proteus2ev_nrf52832/we_proteus2ev_nrf52832_defconfig @@ -24,9 +24,6 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - # Use internal oscillator CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC=y diff --git a/boards/arm/we_proteus3ev_nrf52840/we_proteus3ev_nrf52840.dts b/boards/arm/we_proteus3ev_nrf52840/we_proteus3ev_nrf52840.dts index 4c64bcaa4d0..62cecf7066e 100644 --- a/boards/arm/we_proteus3ev_nrf52840/we_proteus3ev_nrf52840.dts +++ b/boards/arm/we_proteus3ev_nrf52840/we_proteus3ev_nrf52840.dts @@ -57,6 +57,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/we_proteus3ev_nrf52840/we_proteus3ev_nrf52840_defconfig b/boards/arm/we_proteus3ev_nrf52840/we_proteus3ev_nrf52840_defconfig index b232a355e40..cca27094e6a 100644 --- a/boards/arm/we_proteus3ev_nrf52840/we_proteus3ev_nrf52840_defconfig +++ b/boards/arm/we_proteus3ev_nrf52840/we_proteus3ev_nrf52840_defconfig @@ -24,9 +24,6 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - # Use internal oscillator CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC=y diff --git a/boards/arm/xiao_ble/xiao_ble_common.dtsi b/boards/arm/xiao_ble/xiao_ble_common.dtsi index 588f381868a..d031ce4b5b3 100644 --- a/boards/arm/xiao_ble/xiao_ble_common.dtsi +++ b/boards/arm/xiao_ble/xiao_ble_common.dtsi @@ -62,6 +62,10 @@ status = "okay"; }; +&uicr { + gpio-as-nreset; +}; + &gpiote { status = "okay"; }; diff --git a/boards/arm/xiao_ble/xiao_ble_defconfig b/boards/arm/xiao_ble/xiao_ble_defconfig index faef5befa1f..6b6aa3168ec 100644 --- a/boards/arm/xiao_ble/xiao_ble_defconfig +++ b/boards/arm/xiao_ble/xiao_ble_defconfig @@ -29,7 +29,4 @@ CONFIG_USB_DEVICE_STACK=y CONFIG_BUILD_OUTPUT_UF2=y CONFIG_USE_DT_CODE_PARTITION=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y diff --git a/boards/arm/xiao_ble/xiao_ble_sense_defconfig b/boards/arm/xiao_ble/xiao_ble_sense_defconfig index f549e4173dc..269f6f7915b 100644 --- a/boards/arm/xiao_ble/xiao_ble_sense_defconfig +++ b/boards/arm/xiao_ble/xiao_ble_sense_defconfig @@ -29,9 +29,6 @@ CONFIG_USB_DEVICE_STACK=y CONFIG_BUILD_OUTPUT_UF2=y CONFIG_USE_DT_CODE_PARTITION=y -# additional board options -CONFIG_GPIO_AS_PINRESET=y - CONFIG_PINCTRL=y # required to enable LSM6DS3TR-C power diff --git a/boards/posix/nrf52_bsim/Kconfig b/boards/posix/nrf52_bsim/Kconfig index ea1b49bea0a..566520fafa0 100644 --- a/boards/posix/nrf52_bsim/Kconfig +++ b/boards/posix/nrf52_bsim/Kconfig @@ -26,3 +26,9 @@ config SOC_SERIES_BSIM_NRF52X depends on SOC_SERIES_BSIM_NRFXX help Any NRF52 simulated SOC with BabbleSim, based on the POSIX arch + +config SOC_SERIES_BSIM_NRF53X + bool + depends on SOC_SERIES_BSIM_NRFXX + help + Any NRF53 simulated SOC with BabbleSim, based on the POSIX arch diff --git a/cmake/linker_script/common/common-ram.cmake b/cmake/linker_script/common/common-ram.cmake index e6ef59eaf29..9c051477d10 100644 --- a/cmake/linker_script/common/common-ram.cmake +++ b/cmake/linker_script/common/common-ram.cmake @@ -111,9 +111,6 @@ if(CONFIG_UVB) zephyr_iterable_section(NAME uvb_node GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT} SUBALIGN 4) endif() -if(CONFIG_BT_MESH_ADV_EXT) - zephyr_iterable_section(NAME bt_mesh_ext_adv GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT} SUBALIGN 4) -endif() if(CONFIG_LOG) zephyr_iterable_section(NAME log_mpsc_pbuf GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT} SUBALIGN 4) diff --git a/cmake/modules/kernel.cmake b/cmake/modules/kernel.cmake index 7e65c9cd186..28ee922d40b 100644 --- a/cmake/modules/kernel.cmake +++ b/cmake/modules/kernel.cmake @@ -242,3 +242,7 @@ if("${CMAKE_EXTRA_GENERATOR}" STREQUAL "Eclipse CDT4") include(${ZEPHYR_BASE}/cmake/ide/eclipse_cdt4_generator_amendment.cmake) eclipse_cdt4_generator_amendment(1) endif() + +if(ZEPHYR_NRF_MODULE_DIR) + include(${ZEPHYR_NRF_MODULE_DIR}/cmake/partition_manager.cmake) +endif() diff --git a/cmake/modules/snippets.cmake b/cmake/modules/snippets.cmake index 550d236a2f4..6f8950c5f64 100644 --- a/cmake/modules/snippets.cmake +++ b/cmake/modules/snippets.cmake @@ -59,6 +59,7 @@ function(zephyr_process_snippets) # Set SNIPPET_ROOT. list(APPEND SNIPPET_ROOT ${APPLICATION_SOURCE_DIR}) list(APPEND SNIPPET_ROOT ${ZEPHYR_BASE}) + list(APPEND SNIPPET_ROOT ${ZEPHYR_NRF_MODULE_DIR}) unset(real_snippet_root) foreach(snippet_dir ${SNIPPET_ROOT}) # The user might have put a symbolic link in here, for example. diff --git a/doc/build/kconfig/setting.rst b/doc/build/kconfig/setting.rst index 2fd78ff463b..5f37e831e9e 100644 --- a/doc/build/kconfig/setting.rst +++ b/doc/build/kconfig/setting.rst @@ -7,8 +7,7 @@ The :ref:`menuconfig and guiconfig interfaces ` can be used to test out configurations during application development. This page explains how to make settings permanent. -All Kconfig options can be searched in the :ref:`Kconfig search page -`. +All Kconfig options can be searched in the Kconfig search page. .. note:: @@ -115,8 +114,7 @@ Assignments in configuration files are only respected if the dependencies for the symbol are satisfied. A warning is printed otherwise. To figure out what the dependencies of a symbol are, use one of the :ref:`interactive configuration interfaces ` (you can jump directly to a symbol with -:kbd:`/`), or look up the symbol in the :ref:`Kconfig search page -`. +:kbd:`/`), or look up the symbol in the Kconfig search page. .. _initial-conf: diff --git a/doc/build/sysbuild/index.rst b/doc/build/sysbuild/index.rst index 2c53cb0e88a..e09ee3e5617 100644 --- a/doc/build/sysbuild/index.rst +++ b/doc/build/sysbuild/index.rst @@ -569,13 +569,19 @@ You can mark ``my_sample`` as a build-only application in this manner: ) As a result, ``my_sample`` will be built as part of the sysbuild build invocation, -but neither ``west flash`` nor ``west debug`` will be aware of this application. +but it will be excluded from the default image sequence used by ``west flash``. Instead, you may use the outputs of this domain for other purposes - for example, to produce a secondary image for DFU, or to merge multiple images together. You can also replace ``TRUE`` with another boolean constant in CMake, such as a Kconfig option, which would make ``my_sample`` conditionally build-only. +.. note:: + + Applications marked as build-only can still be flashed manually, using + ``west flash --domain my_sample``. As such, the ``BUILD_ONLY`` option only + controls the default behavior of ``west flash``. + Zephyr application configuration ================================ diff --git a/doc/connectivity/bluetooth/api/mesh/shell.rst b/doc/connectivity/bluetooth/api/mesh/shell.rst index f9acf24d277..6f102b5490f 100644 --- a/doc/connectivity/bluetooth/api/mesh/shell.rst +++ b/doc/connectivity/bluetooth/api/mesh/shell.rst @@ -297,7 +297,7 @@ To allow a device to provision devices over GATT, the :kconfig:option:`CONFIG_BT * ``String``: Unquoted alphanumeric authentication string. -``mesh prov static-oob [Val(1-16 hex)]`` +``mesh prov static-oob [Val(1-32 hex)]`` ---------------------------------------- Set or clear the static OOB authentication value. The static OOB authentication value must be set before provisioning starts to have any effect. The static OOB value must be same on both participants in the provisioning. To enable this command, the :kconfig:option:`BT_MESH_SHELL_PROV_CTX_INSTANCE` option must be enabled. @@ -358,7 +358,7 @@ To allow a device to provision devices over GATT, the :kconfig:option:`CONFIG_BT ------------------------------------------------ From the provisioner device, instruct the unprovisioned device to use static OOB authentication, and use the given static authentication value when provisioning. - * ``Val`` - Static OOB value. Providing a hex-string shorter than 16 bytes will populate the N most significant bytes of the array and zero-pad the rest. + * ``Val`` - Static OOB value. Providing a hex-string shorter than 32 bytes will populate the N most significant bytes of the array and zero-pad the rest. ``mesh prov auth-method none`` ------------------------------ @@ -1034,15 +1034,14 @@ Firmware Update Client model The Firmware Update Client model can be added to the mesh shell by enabling configuration options :kconfig:option:`CONFIG_BT_MESH_BLOB_CLI` and :kconfig:option:`CONFIG_BT_MESH_DFU_CLI`. The Firmware Update Client demonstrates the firmware update Distributor role by transferring a dummy firmware update to a set of Target nodes. -``mesh models dfu slot add [ [ []]]`` +``mesh models dfu slot add []`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Add a virtual DFU image slot that can be transferred as a DFU image. The image slot will be assigned an image slot index, which is printed as a response, and can be used to reference the slot in other commands. To update the image slot, remove it using the ``mesh models dfu slot del`` shell command and then add it again. * ``Size``: DFU image slot size in bytes. - * ``FwID``: Optional firmware ID, formatted as a hexstring. + * ``FwID``: Firmware ID, formatted as a hexstring. * ``Metadata``: Optional firmware metadata, formatted as a hexstring. - * ``URI``: Optional URI for the firmware. ``mesh models dfu slot del `` diff --git a/doc/connectivity/networking/api/images/lwm2m_lifetime_both.png b/doc/connectivity/networking/api/images/lwm2m_lifetime_both.png new file mode 100644 index 00000000000..1cdf0bbb662 Binary files /dev/null and b/doc/connectivity/networking/api/images/lwm2m_lifetime_both.png differ diff --git a/doc/connectivity/networking/api/images/lwm2m_lifetime_seconds_early.png b/doc/connectivity/networking/api/images/lwm2m_lifetime_seconds_early.png new file mode 100644 index 00000000000..119005ce5c7 Binary files /dev/null and b/doc/connectivity/networking/api/images/lwm2m_lifetime_seconds_early.png differ diff --git a/doc/connectivity/networking/api/lwm2m.rst b/doc/connectivity/networking/api/lwm2m.rst index 6d26205511e..d3843e4c3d1 100644 --- a/doc/connectivity/networking/api/lwm2m.rst +++ b/doc/connectivity/networking/api/lwm2m.rst @@ -318,6 +318,14 @@ events, setup a callback function: LOG_DBG("Disconnected"); break; + case LWM2M_RD_CLIENT_EVENT_REG_UPDATE: + LOG_DBG("Registration update"); + break; + + case LWM2M_RD_CLIENT_EVENT_DEREGISTER: + LOG_DBG("Deregistration client"); + break; + } } @@ -617,6 +625,53 @@ The events are prefixed with ``LWM2M_RD_CLIENT_EVENT_``. If the retry counter reaches its limits, this event will be triggered. - No actions needed, client will do a re-registrate automatically. + +Configuring lifetime and activity period +**************************************** + +In LwM2M engine, there are three Kconfig options and one runtime value that configures how often the +client will send LwM2M Update message. + +.. list-table:: Update period variables + :widths: auto + :header-rows: 1 + + * - Variable + - Effect + * - LwM2M registration lifetime + - The lifetime parameter in LwM2M specifies how long a device's registration with an LwM2M server remains valid. + Device is expected to send LwM2M Update message before the lifetime exprires. + * - :kconfig:option:`CONFIG_LWM2M_ENGINE_DEFAULT_LIFETIME` + - Default lifetime value, unless set by the bootstrap server. + Also defines lower limit that client accepts as a lifetime. + * - :kconfig:option:`CONFIG_LWM2M_UPDATE_PERIOD` + - How long the client can stay idle before sending a next update. + * - :kconfig:option:`CONFIG_LWM2M_SECONDS_TO_UPDATE_EARLY` + - Minimum time margin to send the update message before the registration lifetime expires. + +.. figure:: images/lwm2m_lifetime_seconds_early.png + :alt: LwM2M seconds to update early + + Default way of calculating when to update registration. + +By default, the client uses :kconfig:option:`CONFIG_LWM2M_SECONDS_TO_UPDATE_EARLY` to calculate how +many seconds before the expiration of lifetime it is going to send the registration update. +The problem with default mode is when the server changes the lifetime of the registration. +This is then affecting the period of updates the client is doing. +If this is used with the QUEUE mode, which is typical in IPv4 networks, it is also affecting the +period of when the device is reachable from the server. + +.. figure:: images/lwm2m_lifetime_both.png + :alt: LwM2M update time when both values are set + + Update time is controlled by UPDATE_PERIOD. + +When also the :kconfig:option:`CONFIG_LWM2M_UPDATE_PERIOD` is set, time to send the update message +is the earliest when any of these values expire. This allows setting long lifetime for the +registration and configure the period accurately, even if server changes the lifetime parameter. + +In runtime, the update frequency is limited to once in 15 seconds to avoid flooding. + .. _lwm2m_shell: LwM2M shell @@ -633,40 +688,58 @@ required actions from the server side. .. code-block:: console - uart:~$ lwm2m - lwm2m - LwM2M commands - Subcommands: - exec :Execute a resource - exec PATH + uart:~$ lwm2m + lwm2m - LwM2M commands + Subcommands: + send :send PATHS + LwM2M SEND operation - read :Read value from LwM2M resource - read PATH [OPTIONS] - -s Read value as string (default) + exec :exec PATH [PARAM] + Execute a resource + + read :read PATH [OPTIONS] + Read value from LwM2M resource + -x Read value as hex stream (default) + -s Read value as string -b Read value as bool (1/0) -uX Read value as uintX_t -sX Read value as intX_t -f Read value as float - - write :Write into LwM2M resource - write PATH [OPTIONS] VALUE - -s Value as string (default) - -b Value as bool - -uX Value as uintX_t - -sX Value as intX_t - -f Value as float - - start :Start the LwM2M RD (Registration / Discovery) Client - start EP_NAME [BOOTSTRAP FLAG] + -t Read value as time_t + + write :write PATH [OPTIONS] VALUE + Write into LwM2M resource + -s Write value as string (default) + -b Write value as bool + -uX Write value as uintX_t + -sX Write value as intX_t + -f Write value as float + -t Write value as time_t + + create :create PATH + Create object instance + + cache :cache PATH NUM + Enable data cache for resource + PATH is LwM2M path + NUM how many elements to cache + + start :start EP_NAME [BOOTSTRAP FLAG] + Start the LwM2M RD (Registration / Discovery) Client -b Set the bootstrap flag (default 0) - stop :Stop the LwM2M RD (De-register) Client - stop [OPTIONS] + stop :stop [OPTIONS] + Stop the LwM2M RD (De-register) Client -f Force close the connection - update :Trigger Registration Update of the LwM2M RD Client + update :Trigger Registration Update of the LwM2M RD Client + + pause :LwM2M engine thread pause + resume :LwM2M engine thread resume + lock :Lock the LwM2M registry + unlock :Unlock the LwM2M registry + - pause :LwM2M engine thread pause - resume :LwM2M engine thread resume .. _lwm2m_api_reference: diff --git a/doc/connectivity/networking/api/mqtt.rst b/doc/connectivity/networking/api/mqtt.rst index b1090c8d394..dcc71e3320b 100644 --- a/doc/connectivity/networking/api/mqtt.rst +++ b/doc/connectivity/networking/api/mqtt.rst @@ -149,6 +149,7 @@ additional configuration information: tls_config->sec_tag_list = m_sec_tags; tls_config->sec_tag_count = ARRAY_SIZE(m_sec_tags); tls_config->hostname = MQTT_BROKER_HOSTNAME; + tls_config->set_native_tls = true; In this sample code, the ``m_sec_tags`` array holds a list of tags, referencing TLS credentials that the MQTT library should use for authentication. We do not specify @@ -161,6 +162,8 @@ Note, that TLS credentials referenced by the ``m_sec_tags`` array must be registered in the system first. For more information on how to do that, refer to :ref:`secure sockets documentation `. +Finally, ``set_native_tls`` can be optionally set to enable native TLS support instead of offloading TLS operations to the modem. + An example of how to use TLS with MQTT is also present in :ref:`mqtt-publisher-sample`. diff --git a/doc/develop/application/index.rst b/doc/develop/application/index.rst index 7307d6c572c..3fd971ad945 100644 --- a/doc/develop/application/index.rst +++ b/doc/develop/application/index.rst @@ -640,9 +640,8 @@ started. See :ref:`setting_configuration_values` for detailed documentation on setting Kconfig configuration values. The :ref:`initial-conf` section on the same page -explains how the initial configuration is derived. See :ref:`kconfig-search` -for a complete list of configuration options. -See :ref:`hardening` for security information related with Kconfig options. +explains how the initial configuration is derived. See :ref:`hardening` for +security information related with Kconfig options. The other pages in the :ref:`Kconfig section of the manual ` are also worth going through, especially if you planning to add new configuration diff --git a/doc/develop/flash_debug/host-tools.rst b/doc/develop/flash_debug/host-tools.rst index e93ee618f55..981c89ce686 100644 --- a/doc/develop/flash_debug/host-tools.rst +++ b/doc/develop/flash_debug/host-tools.rst @@ -206,17 +206,20 @@ LinkServer Debug Host Tools Linkserver is a utility for launching and managing GDB servers for NXP debug probes, which also provides a command-line target flash programming capabilities. -Linkserver can be used with NXP MCUXpresso for Visual Studio Code implementation, +Linkserver can be used with the `NXP MCUXpresso for Visual Studio Code`_ implementation, with custom debug configurations based on GNU tools or as part of a headless solution -for continuous integration and test. Linkserver can be used with MCU-Link, LPC-Link2, +for continuous integration and test. LinkServer can be used with MCU-Link, LPC-Link2, LPC11U35-based and OpenSDA based standalone or on-board debug probes from NXP. -The Linkserver installer also includes the firmware update utilities for MCU-Link and -the LPCScrypt utility for use with LPC-Link2. Linkserver can also be installed using -the MCUXpresso Installer. + +NXP recommends installing LinkServer by using NXP's `MCUXpresso Installer`_. +This method will also install the tools supporting the debug probes below, +including NXP's MCU-Link and LPCScrypt tools. LinkServer is compatible with the following debug probes: - :ref:`lpclink2-cmsis-onboard-debug-probe` +- :ref:`mcu-link-cmsis-onboard-debug-probe` +- :ref:`opensda-daplink-onboard-debug-probe` Supported west commands: @@ -234,13 +237,14 @@ Notes: LinkServer probes -2. Use the LinkServer west runner ``--probe`` option to pass the probe index. +2. With multiple debug probes attached to the host, use the +LinkServer west runner ``--probe`` option to pass the probe index. .. code-block:: console west flash --runner=linkserver --probe=3 -3. device specific settings can be overridden with the west runner for LinkServer with +3. Device-specific settings can be overridden with the west runner for LinkServer with the option '--override'. May be used multiple times. The format is dictated by LinkServer, e.g.: @@ -265,6 +269,7 @@ These debug host tools are compatible with the following debug probes: - :ref:`lpclink2-jlink-onboard-debug-probe` - :ref:`opensda-jlink-onboard-debug-probe` +- :ref:`mcu-link-jlink-onboard-debug-probe` - :ref:`jlink-external-debug-probe` - :ref:`stlink-v21-onboard-debug-probe` @@ -405,3 +410,9 @@ To enable Zephyr RTOS awareness follow the steps described in .. _BOSSA official releases: https://github.com/shumatech/BOSSA/releases + +.. _NXP MCUXpresso for Visual Studio Code: + https://www.nxp.com/design/software/development-software/mcuxpresso-software-and-tools-/mcuxpresso-for-visual-studio-code:MCUXPRESSO-VSC + +.. _MCUXpresso Installer: + https://www.nxp.com/lgfiles/updates/mcuxpresso/MCUXpressoInstaller.exe diff --git a/doc/develop/flash_debug/probes.rst b/doc/develop/flash_debug/probes.rst index 78a068d6ac4..83f355aadba 100644 --- a/doc/develop/flash_debug/probes.rst +++ b/doc/develop/flash_debug/probes.rst @@ -58,6 +58,64 @@ onboard debug probe may have limitations, such as lack of support for advanced debuggers or high-speed tracing. You may need to adjust jumpers to prevent the onboard debug probe from interfering with the external debug probe. +.. _mcu-link-cmsis-onboard-debug-probe: + +MCU-Link CMSIS-DAP Onboard Debug Probe +*************************************** + +The CMSIS-DAP debug probes allow debugging from any compatible toolchain, +including IAR EWARM, Keil MDK, NXP’s MCUXpresso IDE and +MCUXpresso extension for VS Code. In addition to debug probe functionality, the +MCU-Link probes may also provide: + +1. SWO trace end point: this virtual device is used by MCUXpresso to retrieve + SWO trace data. See the MCUXpresso IDE documentation for more information. +#. Virtual COM (VCOM) port / UART bridge connected to the target processor +#. USB to UART, SPI and/or I2C interfaces (depending on MCU-Link + type/implementation) +#. Energy measurements of the target MCU + +This debug probe is compatible with the following debug host tools: + +- :ref:`linkserver-debug-host-tools` + +This probe is realized by programming the MCU-Link microcontroller with the +CMSIS-DAP MCU-Link firmware, which is already installed by default. NXP +recommends using NXP's `MCUXpresso Installer`_, which installs both the MCU-Link +host tools plus the :ref:`linkserver-debug-host-tools`. + +1. Put the MCU-Link microcontroller into DFU boot mode by attaching the DFU + jumper, then powering up the board. + +#. Run the ``program_CMSIS`` script, found in the installed MCU-Link ``scripts`` + folder. + +#. Remove the DFU jumper and power cycle the board. + +.. _mcu-link-jlink-onboard-debug-probe: + +MCU-Link JLink Onboard Debug Probe +************************************ + +The MCU-Link J-Link is an onboard debug probe and usb-to-serial adapter +supported on many NXP development boards. + +This debug probe is compatible with the following debug host tools: + +- :ref:`jlink-debug-host-tools` + +These probes do not have JLink firmware installed by default, and must be +updated. NXP recommends using NXP's `MCUXpresso Installer`_, which installs both +the :ref:`jlink-debug-host-tools` plus the MCU-Link host tools. + +1. Put the MCU-Link microcontroller into DFU boot mode by attaching the DFU + jumper, then powering up the board. + +#. Run the ``program_JLINK`` script, found in the installed MCU-Link ``scripts`` + folder. + +#. Remove the DFU jumper and power cycle the board. + .. _lpclink2-cmsis-onboard-debug-probe: LPC-LINK2 CMSIS DAP Onboard Debug Probe @@ -341,3 +399,6 @@ option. For more information about twister and available options, see .. _STM32CubeProgrammer Tool: https://www.st.com/en/development-tools/stm32cubeprog.html + +.. _MCUXpresso Installer: + https://www.nxp.com/lgfiles/updates/mcuxpresso/MCUXpressoInstaller.exe diff --git a/doc/develop/getting_started/index.rst b/doc/develop/getting_started/index.rst index 4a1b345ee62..f7a76a71585 100644 --- a/doc/develop/getting_started/index.rst +++ b/doc/develop/getting_started/index.rst @@ -118,12 +118,32 @@ The current minimum required version for the main dependencies are: /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + #. After the Homebrew installation script completes, follow the on-screen + instructions to add the Homebrew installation to the path. + + * On macOS running on Apple Silicon, this is achieved with: + + .. code-block:: bash + + (echo; echo 'eval "$(/opt/homebrew/bin/brew shellenv)"') >> ~/.zprofile + source ~/.zprofile + + * On macOS running on Intel, use the command for Apple Silicon, but replace ``/opt/homebrew/`` with ``/usr/local/``. + #. Use ``brew`` to install the required dependencies: .. code-block:: bash brew install cmake ninja gperf python3 ccache qemu dtc wget libmagic + #. Add the Homebrew Python folder to the path, in order to be able to + execute ``python`` and ``pip`` as well ``python3`` and ``pip3``. + + .. code-block:: bash + + (echo; echo 'export PATH="'$(brew --prefix)'/opt/python/libexec/bin:$PATH"') >> ~/.zprofile + source ~/.zprofile + .. group-tab:: Windows .. note:: diff --git a/doc/develop/test/pytest.rst b/doc/develop/test/pytest.rst index 97737ce0ae2..957ff8c773b 100644 --- a/doc/develop/test/pytest.rst +++ b/doc/develop/test/pytest.rst @@ -49,29 +49,65 @@ How to create a pytest test An example of a pytest test is given at :zephyr_file:`samples/subsys/testsuite/pytest/shell/pytest/test_shell.py`. Twister calls pytest for each configuration from the .yaml file which uses ``harness: pytest``. By default, it points to ``pytest`` directory, located next to a directory with binary sources. -A keyword ``pytest_root`` placed under ``harness_config`` section can be used to point to another -location. +A keyword ``pytest_root`` placed under ``harness_config`` section can be used to point to other +files, directories or subtests. -Pytest scans the given folder looking for tests, following its default +Pytest scans the given locations looking for tests, following its default `discovery rules `_ One can also pass some extra arguments to the pytest from yaml file using ``pytest_args`` keyword under ``harness_config``, e.g.: ``pytest_args: [‘-k=test_method’, ‘--log-level=DEBUG’]``. +There is also an option to pass ``--pytest-args`` through Twister command line parameters. +This can be particularly useful when one wants to select a specific testcase from a test suite. +For instance, one can use a command: -Following import is required to include in .py sources: +.. code-block:: console -.. code-block:: python + $ ./scripts/twister --platform native_sim -T samples/subsys/testsuite/pytest/shell \ + -s samples/subsys/testsuite/pytest/shell/sample.pytest.shell \ + --pytest-args='-k test_shell_print_version' - from twister_harness import Device -It is important for type checking and enabling IDE hints for ``dut`` s (objects representing -Devices Under Test). The ``dut`` fixture is the core of pytest harness plugin. When used as an -argument of a test function it gives access to a DeviceAbstract type object. The fixture yields a -device prepared according to the requested type (native posix, qemu, hardware, etc.). All types of -devices share the same API. This allows for writing tests which are device-type-agnostic. +Note that ``--pytest-args`` can be passed multiple times to pass several arguments to the pytest. Helpers & fixtures ================== +dut +--- + +Give access to a DeviceAdapter type object, that represents Device Under Test. +This fixture is the core of pytest harness plugin. It is required to launch +DUT (initialize logging, flash device, connect serial etc). +This fixture yields a device prepared according to the requested type +(native posix, qemu, hardware, etc.). All types of devices share the same API. +This allows for writing tests which are device-type-agnostic. +Scope of this fixture is determined by the ``pytest_dut_scope`` +keyword placed under ``harness_config`` section. + + +.. code-block:: python + + from twister_harness import DeviceAdapter + + def test_sample(dut: DeviceAdapter): + dut.readlines_until('Hello world') + +shell +----- + +Provide an object with methods used to interact with shell application. +It calls ``wait_for_promt`` method, to not start scenario until DUT is ready. +Note that it uses ``dut`` fixture, so ``dut`` can be skipped when ``shell`` is used. +Scope of this fixture is determined by the ``pytest_dut_scope`` +keyword placed under ``harness_config`` section. + +.. code-block:: python + + from twister_harness import Shell + + def test_shell(shell: Shell): + shell.exec_command('help') + mcumgr ------ @@ -82,16 +118,15 @@ More information about MCUmgr can be found here :ref:`mcu_mgr`. This fixture requires the ``mcumgr`` available in the system PATH Only selected functionality of MCUmgr is wrapped by this fixture. - For example, here is a test with a fixture ``mcumgr`` .. code-block:: python - from twister_harness import Device, McuMgr + from twister_harness import DeviceAdapter, Shell, McuMgr - def test_upgrade(dut: Device, mcumgr: McuMgr): - # wait for dut is up - time.sleep(2) + def test_upgrade(dut: DeviceAdapter, shell: Shell, mcumgr: McuMgr): + # free the serial port for mcumgr + dut.disconnect() # upload the signed image mcumgr.image_upload('path/to/zephyr.signed.bin') # obtain the hash of uploaded image from the device @@ -105,7 +140,4 @@ For example, here is a test with a fixture ``mcumgr`` Limitations *********** -* Device adapters in pytest plugin provide `iter_stdout` method to read from devices. In some - cases, it is not the most convenient way, and it will be considered how to improve this - (for example replace it with a simple read function with a given byte size and timeout arguments). * Not every platform type is supported in the plugin (yet). diff --git a/doc/develop/test/twister.rst b/doc/develop/test/twister.rst index a29890c7899..a61744eec6d 100644 --- a/doc/develop/test/twister.rst +++ b/doc/develop/test/twister.rst @@ -492,14 +492,20 @@ harness_config: Only one fixture can be defined per testcase and the fixture name has to be unique across all tests in the test suite. - pytest_root: (default pytest) - Specify a pytest directory which need to execute when test case begin to running, - default pytest directory name is pytest, after pytest finished, twister will - check if this case pass or fail according the pytest report. + pytest_root: (default pytest) + Specify a list of pytest directories, files or subtests that need to be executed + when test case begin to running, default pytest directory is pytest. + After pytest finished, twister will check if this case pass or fail according + to the pytest report. pytest_args: (default empty) Specify a list of additional arguments to pass to ``pytest``. + pytest_dut_scope: (default function) + The scope for which ``dut`` and ``shell`` pytest fixtures are shared. + If the scope is set to ``function``, DUT is launched for every test case + in python script. For ``session`` scope, DUT is launched only once. + robot_test_path: (default empty) Specify a path to a file containing a Robot Framework test suite to be run. @@ -526,15 +532,24 @@ harness_config: The following is an example yaml file with pytest harness_config options, default pytest_root name "pytest" will be used if pytest_root not specified. - please refer the example in samples/subsys/testsuite/pytest/. + please refer the examples in samples/subsys/testsuite/pytest/. :: + common: + harness: pytest tests: - pytest.example: - harness: pytest + pytest.example.directories: + harness_config: + pytest_root: + - pytest_dir1 + - $ENV_VAR/samples/test/pytest_dir2 + pytest.example.files_and_subtests: harness_config: - pytest_root: [pytest directory name] + pytest_root: + - pytest/test_file_1.py + - test_file_2.py::test_A + - test_file_2.py::test_B[param_a] The following is an example yaml file with robot harness_config options. diff --git a/doc/kconfig.rst b/doc/kconfig.rst deleted file mode 100644 index 1123de2adbd..00000000000 --- a/doc/kconfig.rst +++ /dev/null @@ -1,8 +0,0 @@ -:orphan: - -.. _kconfig-search: - -Kconfig Search -============== - -.. kconfig:search:: diff --git a/doc/releases/migration-guide-3.5.rst b/doc/releases/migration-guide-3.5.rst index 7ffb16319d1..8c059b630c6 100644 --- a/doc/releases/migration-guide-3.5.rst +++ b/doc/releases/migration-guide-3.5.rst @@ -42,3 +42,25 @@ Recommended Changes :kconfig:option:`CONFIG_GIC_V3` directly in Kconfig has been deprecated. The GIC version should now be specified by adding the appropriate compatible, for example :dtcompatible:`arm,gic-v2`, to the GIC node in the device tree. + +* Nordic nRF based boards using :kconfig:option:`CONFIG_NFCT_PINS_AS_GPIOS` + to configure NFCT pins as GPIOs, should instead set the new UICR + ``nfct-pins-as-gpios`` property in devicetree. It can be set like this in the + board devicetree files: + + .. code-block:: devicetree + + &uicr { + nfct-pins-as-gpios; + }; + +* Nordic nRF based boards using :kconfig:option:`CONFIG_GPIO_AS_PINRESET` + to configure reset GPIO as nRESET, should instead set the new UICR + ``gpio-as-nreset`` property in devicetree. It can be set like this in the + board devicetree files: + + .. code-block:: devicetree + + &uicr { + gpio-as-nreset; + }; diff --git a/doc/releases/release-notes-3.5.rst b/doc/releases/release-notes-3.5.rst index 1c2a38edcf2..82d2698849b 100644 --- a/doc/releases/release-notes-3.5.rst +++ b/doc/releases/release-notes-3.5.rst @@ -255,6 +255,7 @@ Networking * Added support for tickless mode. This removes the 500 ms timeout from the socket loop so the engine does not constantly wake up the CPU. This can be enabled by :kconfig:option:`CONFIG_LWM2M_TICKLESS`. + * Added new :c:macro:`LWM2M_RD_CLIENT_EVENT_DEREGISTER` event. * Wi-Fi * Added Passive scan support. @@ -338,6 +339,16 @@ HALs MCUboot ******* + * Added :kconfig:option:`CONFIG_MCUBOOT_BOOTLOADER_NO_DOWNGRADE` + that allows to inform application that the on-board MCUboot has been configured + with downgrade prevention enabled. This option is automatically selected for + DirectXIP mode and is available for both swap modes. + + * Added :kconfig:option:`CONFIG_MCUBOOT_BOOTLOADER_MODE_OVERWRITE_ONLY` + that allows to inform application that the on-board MCUboot will overwrite + the primary slot with secondary slot contents, without saving the original + image in primary slot. + Storage ******* diff --git a/doc/services/device_mgmt/smp_groups/smp_group_0.rst b/doc/services/device_mgmt/smp_groups/smp_group_0.rst index 839b0e06be3..53907f33bcc 100644 --- a/doc/services/device_mgmt/smp_groups/smp_group_0.rst +++ b/doc/services/device_mgmt/smp_groups/smp_group_0.rst @@ -28,6 +28,8 @@ OS management group defines following commands: +-------------------+-----------------------------------------------+ | ``7`` | OS/Application info | +-------------------+-----------------------------------------------+ + | ``8`` | Bootloader information | + +-------------------+-----------------------------------------------+ Echo command ************ @@ -660,12 +662,215 @@ CBOR data of response: where: +.. table:: + :align: center + + +------------------+-------------------------------------------------------------------------+ + | "output" | Text response including requested parameters. | + +------------------+-------------------------------------------------------------------------+ + | "err" -> "group" | :c:enum:`mcumgr_group_t` group of the group-based error code. Only | + | | appears if an error is returned when using SMP version 2. | + +------------------+-------------------------------------------------------------------------+ + | "err" -> "rc" | contains the index of the group-based error code. Only appears if | + | | non-zero (error condition) when using SMP version 2. | + +------------------+-------------------------------------------------------------------------+ + | "rc" | :c:enum:`mcumgr_err_t` only appears if non-zero (error condition) when | + | | using SMP version 1 or for SMP errors when using SMP version 2. | + +------------------+-------------------------------------------------------------------------+ + +Bootloader Information +********************** + +Allows retrieving information about the on-board bootloader and its parameters. + +Bootloader Information Request +============================== + +Bootloader information request header: + +.. table:: + :align: center + + +--------+--------------+----------------+ + | ``OP`` | ``Group ID`` | ``Command ID`` | + +========+==============+================+ + | ``0`` | ``0`` | ``8`` | + +--------+--------------+----------------+ + +CBOR data of request: + +.. code-block:: none + + { + (str,opt)"query" : (str) + } + +where: + .. table:: :align: center +--------------+-----------------------------------------------+ - | "output" | Text response including requested parameters. | + | "query" | Is string representing query for parameters, | + | | with no restrictions how the query looks like | + | | as processing of query is left for bootloader | + | | backend. | + | | If there is no query, then response will | + | | return string identifying the bootloader. | +--------------+-----------------------------------------------+ - | "rc" | :c:enum:`mcumgr_err_t` | - | | only appears if non-zero (error condition). | + +Bootloader Information Response +=============================== + +Bootloader information response header: + +.. table:: + :align: center + + +--------+--------------+----------------+ + | ``OP`` | ``Group ID`` | ``Command ID`` | + +========+==============+================+ + | ``1`` | ``0`` | ``8`` | + +--------+--------------+----------------+ + +In case when no "query" has been provided in request, +CBOR data of response: + +.. code-block:: none + + { + (str)"bootloader" : (str) + } + +where: + +.. table:: + :align: center + + +--------------+-----------------------------------------------+ + | "bootloader" | String representing bootloader name | +--------------+-----------------------------------------------+ + +In case when "query" is provided: + +.. code-block:: none + + { + (str,opt) : () + ... + } + +where: + +.. table:: + :align: center + + +------------------+-------------------------------------------------------------------------+ + | | Response to "query". This is optional and may be left out in case when | + | | query yields no response, SMP version 2 error code of | + | | `OS_MGMT_ERR_QUERY_YIELDS_NO_ANSWER` is expected. | + | | Response may have more than one parameter reported back or it may be | + | | a map, that is dependent on bootloader backednd and query. | + +------------------+-------------------------------------------------------------------------+ + | ... | Parameter characteristic information. | + +------------------+-------------------------------------------------------------------------+ + +Parameter may be accompanied by additional, parameter specific, information keywords with +assigned values. + +In case of error the CBOR data takes the form: + +.. tabs:: + + .. group-tab:: SMP version 2 + + .. code-block:: none + + { + (str)"err" : { + (str)"group" : (uint) + (str)"rc" : (uint) + } + } + + .. group-tab:: SMP version 1 (and non-group SMP version 2) + + .. code-block:: none + + { + (str)"rc" : (int) + } + +where: + +.. table:: + :align: center + + +------------------+-------------------------------------------------------------------------+ + | "err" -> "group" | :c:enum:`mcumgr_group_t` group of the group-based error code. Only | + | | appears if an error is returned when using SMP version 2. | + +------------------+-------------------------------------------------------------------------+ + | "err" -> "rc" | contains the index of the group-based error code. Only appears if | + | | non-zero (error condition) when using SMP version 2. | + +------------------+-------------------------------------------------------------------------+ + | "rc" | :c:enum:`mcumgr_err_t` only appears if non-zero (error condition) when | + | | using SMP version 1 or for SMP errors when using SMP version 2. | + +------------------+-------------------------------------------------------------------------+ + +Bootloader Information: MCUboot +=============================== + +In case when MCUboot is application bootloader empty request will +be responded with: + +.. code-block:: none + + { + (str)"bootloader" : (str)"MCUboot" + } + +Currently "MCUboot" supports querying for mode of operation: + +.. code-block:: none + + { + (str)"query" : (str)"mode" + } + +Response to "mode" is: + +.. code-block:: none + + { + (str)"mode" : (int) + (str,opt)"no-downgrade" : (bool) + } + +where "mode" is one of: + +.. table:: + :align: center + + +-----+-----------------------------------------------------+ + | -1 | Unknown mode of MCUboot. | + +-----+-----------------------------------------------------+ + | 0 | MCUboot is in single application mode. | + +-----+-----------------------------------------------------+ + | 1 | MCUboot is in swap using scratch partition mode. | + +-----+-----------------------------------------------------+ + | 2 | MCUboot is in overwrite (upgrade-only) mode. | + +-----+-----------------------------------------------------+ + | 3 | MCUboot is in swap without scratch mode. | + +-----+-----------------------------------------------------+ + | 4 | MCUboot is in DirectXIP without revert mode. | + +-----+-----------------------------------------------------+ + | 5 | MCUboot is in DirectXIP with revert mode. | + +-----+-----------------------------------------------------+ + | 6 | MCUboot is in RAM loader mode. | + +-----+-----------------------------------------------------+ + +The "no-downgrade" is a flag, which is always sent when true, indicating that +mode has downgrade prevention enabled; downgrade prevention means that +if uploaded image has lower version than running, it will notbe taken +for exectuion by MCUboot. +MCUmgr may reject image with lower version in that MCUboot configuration. diff --git a/drivers/disk/flashdisk.c b/drivers/disk/flashdisk.c index 6336ad4dfd0..3e43716c2b3 100644 --- a/drivers/disk/flashdisk.c +++ b/drivers/disk/flashdisk.c @@ -420,6 +420,8 @@ static const struct disk_operations flash_disk_ops = { .ioctl = disk_flash_access_ioctl, }; +#ifndef USE_PARTITION_MANAGER +/* The non-Partition manager, DTS based generators below */ #define DT_DRV_COMPAT zephyr_flash_disk #define PARTITION_PHANDLE(n) DT_PHANDLE_BY_IDX(DT_DRV_INST(n), partition, 0) @@ -461,6 +463,82 @@ DT_INST_FOREACH_STATUS_OKAY(VERIFY_CACHE_SIZE_IS_NOT_ZERO_IF_NOT_READ_ONLY) "Devicetree node " DT_NODE_PATH(DT_DRV_INST(n)) \ " has cache size which is not a multiple of its sector size"); DT_INST_FOREACH_STATUS_OKAY(VERIFY_CACHE_SIZE_IS_MULTIPLY_OF_SECTOR_SIZE) +#else /* ifndef USE_PARTITION_MANAGER */ +/* Partition Manager based generators below */ + +/* Gets the PM_..._EXTRA_PARAM_##param value */ +#define PM_FLASH_DISK_ENTRY_EXTRA_PARAM(name, param) PM_##name##_EXTRA_PARAM_disk_##param + +/* Gets the PM_..._NAME value which is originally cased, as in yaml, partition name */ +#define PM_FLASH_DISK_ENTRY_PARTITION_NAME(name) PM_##name##_NAME + +/* Generates flashdiskN_cache variable name, where N is partition ID */ +#define PM_FLASH_DISK_CACHE_VARIABLE(n) UTIL_CAT(flashdisk, UTIL_CAT(FIXED_PARTITION_ID(n), _cache)) + +/* Generate cache buffers */ +#define CACHE_SIZE(n) (COND_CODE_1(PM_FLASH_DISK_ENTRY_EXTRA_PARAM(n, read_only), (0), (1)) * \ + PM_FLASH_DISK_ENTRY_EXTRA_PARAM(n, cache_size)) +#define DEFINE_FLASHDISKS_CACHE(n) \ + static uint8_t __aligned(4) PM_FLASH_DISK_CACHE_VARIABLE(n)[CACHE_SIZE(n)]; + +PM_FOREACH_AFFILIATED_TO_disk(DEFINE_FLASHDISKS_CACHE) + +/* Generated single Flash Disk device data from Partition Manager partition. + * Partition is required to have type set to disk in partition definitions: + * type: disk + * and following extra params can be provided: + * extra_params: { + * name = "", + * cache_size = , + * sector_size = , + * read_only = + * } + * where: + * is mandatory device name that will be used by Disk Access and FAT FS to mount device; + * is cache r/w cache size, which is mandatory if read_only = 0 or not present, + * and should be multiple of ; + * is mandatory device sector size information, usually should be erase page size, + * for flash devices, for example 4096 bytes; + * read_only is optional, if not present then assumed false; can be 0(false) or 1(true). + */ +#define DEFINE_FLASHDISKS_DEVICE(n) \ +{ \ + .info = { \ + .ops = &flash_disk_ops, \ + .name = STRINGIFY(PM_FLASH_DISK_ENTRY_EXTRA_PARAM(n, name)), \ + }, \ + .area_id = FIXED_PARTITION_ID(n), \ + .offset = FIXED_PARTITION_OFFSET(n), \ + .cache = PM_FLASH_DISK_CACHE_VARIABLE(n), \ + .cache_size = sizeof(PM_FLASH_DISK_CACHE_VARIABLE(n)), \ + .size = FIXED_PARTITION_SIZE(n), \ + .sector_size = PM_FLASH_DISK_ENTRY_EXTRA_PARAM(n, sector_size), \ +}, + +/* The bellow used PM_FOREACH_TYPE_disk is generated by Partition Manager foreach + * loop macro. The lower case _disk is type name for which the macro has been generated; + * partition entry can have multiple types set and foreach macro will be generated + * for every type found across partition definitions. + */ +static struct flashdisk_data flash_disks[] = { + PM_FOREACH_AFFILIATED_TO_disk(DEFINE_FLASHDISKS_DEVICE) +}; + +#define VERIFY_CACHE_SIZE_IS_NOT_ZERO_IF_NOT_READ_ONLY(n) \ + COND_CODE_1(PM_FLASH_DISK_ENTRY_EXTRA_PARAM(n, read_only), \ + (/* cache-size is not used for read-only disks */), \ + (BUILD_ASSERT(PM_FLASH_DISK_ENTRY_EXTRA_PARAM(n, cache_size) != 0, \ + "Flash disk partition " STRINGIFY(PM_FLASH_DISK_ENTRY_PARTITION_NAME(n))\ + " must have non-zero cache-size");)) +PM_FOREACH_AFFILIATED_TO_disk(VERIFY_CACHE_SIZE_IS_NOT_ZERO_IF_NOT_READ_ONLY) + +#define VERIFY_CACHE_SIZE_IS_MULTIPLY_OF_SECTOR_SIZE(n) \ + BUILD_ASSERT(PM_FLASH_DISK_ENTRY_EXTRA_PARAM(n, cache_size) % \ + PM_FLASH_DISK_ENTRY_EXTRA_PARAM(n, sector_size) == 0, \ + "Devicetree node " STRINGIFY(PM_FLASH_DISK_ENTRY_PARTITION_NAME(n)) \ + " has cache size which is not a multiple of its sector size"); +PM_FOREACH_AFFILIATED_TO_disk(VERIFY_CACHE_SIZE_IS_MULTIPLY_OF_SECTOR_SIZE) +#endif /* USE_PARTITION_MANAGER */ static int disk_flash_init(void) { diff --git a/drivers/entropy/Kconfig.psa_crypto b/drivers/entropy/Kconfig.psa_crypto index 50f31509004..455a2c9b55b 100644 --- a/drivers/entropy/Kconfig.psa_crypto +++ b/drivers/entropy/Kconfig.psa_crypto @@ -8,6 +8,7 @@ config ENTROPY_PSA_CRYPTO_RNG depends on BUILD_WITH_TFM depends on DT_HAS_ZEPHYR_PSA_CRYPTO_RNG_ENABLED select ENTROPY_HAS_DRIVER + select PSA_WANT_GENERATE_RANDOM default y help Enable the PSA Crypto source Entropy driver. diff --git a/drivers/entropy/entropy_nrf5.c b/drivers/entropy/entropy_nrf5.c index 8febcfacb15..2f04bc06a37 100644 --- a/drivers/entropy/entropy_nrf5.c +++ b/drivers/entropy/entropy_nrf5.c @@ -116,10 +116,6 @@ static int random_byte_get(void) return retval; } -#pragma GCC push_options -#if defined(CONFIG_BT_CTLR_FAST_ENC) -#pragma GCC optimize ("Ofast") -#endif static uint16_t rng_pool_get(struct rng_pool *rngp, uint8_t *buf, uint16_t len) { uint32_t last = rngp->last; @@ -175,7 +171,6 @@ static uint16_t rng_pool_get(struct rng_pool *rngp, uint8_t *buf, uint16_t len) return len; } -#pragma GCC pop_options static int rng_pool_put(struct rng_pool *rngp, uint8_t byte) { diff --git a/drivers/entropy/entropy_smartbond.c b/drivers/entropy/entropy_smartbond.c index 0058baafce5..28b6f08e100 100644 --- a/drivers/entropy/entropy_smartbond.c +++ b/drivers/entropy/entropy_smartbond.c @@ -100,10 +100,6 @@ static int random_word_get(uint8_t buf[4]) return retval; } -#pragma GCC push_options -#if defined(CONFIG_BT_CTLR_FAST_ENC) -#pragma GCC optimize("Ofast") -#endif static uint16_t rng_pool_get(struct rng_pool *rngp, uint8_t *buf, uint16_t len) { uint32_t last = rngp->last; @@ -159,7 +155,6 @@ static uint16_t rng_pool_get(struct rng_pool *rngp, uint8_t *buf, uint16_t len) return len; } -#pragma GCC pop_options static int rng_pool_put(struct rng_pool *rngp, uint8_t byte) { diff --git a/drivers/entropy/entropy_stm32.c b/drivers/entropy/entropy_stm32.c index 9f2ad136203..7c342a09c23 100644 --- a/drivers/entropy/entropy_stm32.c +++ b/drivers/entropy/entropy_stm32.c @@ -377,13 +377,7 @@ static void pool_filling_work_handler(struct k_work *work) } } -#if defined(CONFIG_BT_CTLR_FAST_ENC) -#define __fast __attribute__((optimize("Ofast"))) -#else -#define __fast -#endif - -__fast static uint16_t rng_pool_get(struct rng_pool *rngp, uint8_t *buf, +static uint16_t rng_pool_get(struct rng_pool *rngp, uint8_t *buf, uint16_t len) { uint32_t last = rngp->last; @@ -448,7 +442,6 @@ __fast static uint16_t rng_pool_get(struct rng_pool *rngp, uint8_t *buf, return len; } -#undef __fast static int rng_pool_put(struct rng_pool *rngp, uint8_t byte) { diff --git a/drivers/flash/Kconfig.nordic_qspi_nor b/drivers/flash/Kconfig.nordic_qspi_nor index 16252e0812c..7b9fa05eb7f 100644 --- a/drivers/flash/Kconfig.nordic_qspi_nor +++ b/drivers/flash/Kconfig.nordic_qspi_nor @@ -14,8 +14,8 @@ menuconfig NORDIC_QSPI_NOR if NORDIC_QSPI_NOR config NORDIC_QSPI_NOR_INIT_PRIORITY - int - default 80 + int "Init priority" + default 41 help Device driver initialization priority. @@ -39,4 +39,14 @@ config NORDIC_QSPI_NOR_STACK_WRITE_BUFFER_SIZE if the data is larger than the configured size. Must be a multiple of 4. When set to 0, the feature is disabled. +config NORDIC_QSPI_NOR_XIP + bool "XIP (eXecute in place)" + depends on SOC_NRF5340_CPUAPP + help + Enable setting up the QSPI NOR driver to allow for execution of code + stored in QSPI XIP region. Note that for this functionality to work, + the QSPI NOR init priority must be set so that no XIP code in the + QSPI NOR flash chip is executed until the driver has been setup. + This will also disable power management for the QSPI NOR flash chip. + endif # NORDIC_QSPI_NOR diff --git a/drivers/flash/nrf_qspi_nor.c b/drivers/flash/nrf_qspi_nor.c index b2455ba3c6f..ac055229710 100644 --- a/drivers/flash/nrf_qspi_nor.c +++ b/drivers/flash/nrf_qspi_nor.c @@ -26,15 +26,15 @@ LOG_MODULE_REGISTER(qspi_nor, CONFIG_FLASH_LOG_LEVEL); #include struct qspi_nor_data { +#if !defined(CONFIG_PM_DEVICE_RUNTIME) && defined(CONFIG_MULTITHREADING) + /* A semaphore to control QSPI deactivation. */ + struct k_sem count; +#endif #ifdef CONFIG_MULTITHREADING - /* The semaphore to control exclusive access on write/erase. */ - struct k_sem trans; /* The semaphore to control exclusive access to the device. */ struct k_sem sem; /* The semaphore to indicate that transfer has completed. */ struct k_sem sync; - /* The semaphore to control driver init/uninit. */ - struct k_sem count; #else /* CONFIG_MULTITHREADING */ /* A flag that signals completed transfer when threads are * not enabled. @@ -173,12 +173,8 @@ BUILD_ASSERT(DT_INST_PROP(0, address_size_32), "After entering 4 byte addressing mode, 4 byte addressing is expected"); #endif -#ifndef CONFIG_PM_DEVICE_RUNTIME -static bool qspi_initialized; -#endif - -static int qspi_device_init(const struct device *dev); -static void qspi_device_uninit(const struct device *dev); +void z_impl_nrf_qspi_nor_xip_enable(const struct device *dev, bool enable); +void z_vrfy_nrf_qspi_nor_xip_enable(const struct device *dev, bool enable); #define WORD_SIZE 4 @@ -243,72 +239,99 @@ static inline int qspi_get_zephyr_ret_code(nrfx_err_t res) static inline void qspi_lock(const struct device *dev) { +#ifdef CONFIG_MULTITHREADING struct qspi_nor_data *dev_data = dev->data; - pm_device_busy_set(dev); - -#ifdef CONFIG_MULTITHREADING k_sem_take(&dev_data->sem, K_FOREVER); -#else /* CONFIG_MULTITHREADING */ - ARG_UNUSED(dev_data); -#endif /* CONFIG_MULTITHREADING */ - - /* - * Change the base clock divider only for the time the driver is locked - * to perform a QSPI operation, otherwise the power consumption would be - * increased also when the QSPI peripheral is idle. - * When XIP is enabled, there is nothing to do here as the changed - * divider is kept all the time. - */ -#if defined(CONFIG_SOC_SERIES_NRF53X) - if (!dev_data->xip_enabled) { - nrf_clock_hfclk192m_div_set(NRF_CLOCK, BASE_CLOCK_DIV); - } #endif } static inline void qspi_unlock(const struct device *dev) { +#ifdef CONFIG_MULTITHREADING struct qspi_nor_data *dev_data = dev->data; -#if defined(CONFIG_SOC_SERIES_NRF53X) - /* Restore the default base clock divider to reduce power consumption. - * Unless XIP is enabled, then the changed divider needs to be kept. - */ - if (!dev_data->xip_enabled) { - nrf_clock_hfclk192m_div_set(NRF_CLOCK, NRF_CLOCK_HFCLK_DIV_4); - } + k_sem_give(&dev_data->sem); #endif +} -#ifdef CONFIG_MULTITHREADING - k_sem_give(&dev_data->sem); -#else - ARG_UNUSED(dev_data); +static inline void qspi_clock_div_change(void) +{ +#ifdef CONFIG_SOC_SERIES_NRF53X + /* Make sure the base clock divider is changed accordingly + * before a QSPI transfer is performed. + */ + nrf_clock_hfclk192m_div_set(NRF_CLOCK, BASE_CLOCK_DIV); #endif +} - pm_device_busy_clear(dev); +static inline void qspi_clock_div_restore(void) +{ +#ifdef CONFIG_SOC_SERIES_NRF53X + /* Restore the default base clock divider to reduce power + * consumption when the QSPI peripheral is idle. + */ + nrf_clock_hfclk192m_div_set(NRF_CLOCK, NRF_CLOCK_HFCLK_DIV_4); +#endif } -static inline void qspi_trans_lock(const struct device *dev) +static void qspi_acquire(const struct device *dev) { -#ifdef CONFIG_MULTITHREADING struct qspi_nor_data *dev_data = dev->data; - k_sem_take(&dev_data->trans, K_FOREVER); -#else /* CONFIG_MULTITHREADING */ - ARG_UNUSED(dev); -#endif /* CONFIG_MULTITHREADING */ +#if defined(CONFIG_PM_DEVICE_RUNTIME) + int rc = pm_device_runtime_get(dev); + + if (rc < 0) { + LOG_ERR("pm_device_runtime_get failed: %d", rc); + } +#elif defined(CONFIG_MULTITHREADING) + /* In multithreading, the driver can call qspi_acquire more than once + * before calling qspi_release. Keeping count, so QSPI is deactivated + * only at the last call (count == 0). + */ + k_sem_give(&dev_data->count); +#endif + + qspi_lock(dev); + + if (!dev_data->xip_enabled) { + qspi_clock_div_change(); + + pm_device_busy_set(dev); + } } -static inline void qspi_trans_unlock(const struct device *dev) +static void qspi_release(const struct device *dev) { -#ifdef CONFIG_MULTITHREADING struct qspi_nor_data *dev_data = dev->data; + bool deactivate = true; - k_sem_give(&dev_data->trans); -#else /* CONFIG_MULTITHREADING */ - ARG_UNUSED(dev); -#endif /* CONFIG_MULTITHREADING */ +#if !defined(CONFIG_PM_DEVICE_RUNTIME) && defined(CONFIG_MULTITHREADING) + /* The last thread to finish using the driver deactivates the QSPI */ + (void) k_sem_take(&dev_data->count, K_NO_WAIT); + deactivate = (k_sem_count_get(&dev_data->count) == 0); +#endif + + if (!dev_data->xip_enabled) { + qspi_clock_div_restore(); + + if (deactivate && !IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) { + (void) nrfx_qspi_deactivate(); + } + + pm_device_busy_clear(dev); + } + + qspi_unlock(dev); + +#if defined(CONFIG_PM_DEVICE_RUNTIME) + int rc = pm_device_runtime_put(dev); + + if (rc < 0) { + LOG_ERR("pm_device_runtime_put failed: %d", rc); + } +#endif } static inline void qspi_wait_for_completion(const struct device *dev, @@ -357,89 +380,6 @@ static void qspi_handler(nrfx_qspi_evt_t event, void *p_context) } } -static int qspi_device_init(const struct device *dev) -{ - struct qspi_nor_data *dev_data = dev->data; - - if (dev_data->xip_enabled) { - return 0; - } - -#ifdef CONFIG_PM_DEVICE_RUNTIME - return pm_device_runtime_get(dev); -#else - nrfx_err_t res; - int ret = 0; - - qspi_lock(dev); - - /* In multithreading, driver can call qspi_device_init more than once - * before calling qspi_device_uninit. Keepping count, so QSPI is - * uninitialized only at the last call (count == 0). - */ -#ifdef CONFIG_MULTITHREADING - k_sem_give(&dev_data->count); -#endif - - if (!qspi_initialized) { - const struct qspi_nor_config *dev_config = dev->config; - - res = nrfx_qspi_init(&dev_config->nrfx_cfg, - qspi_handler, - dev_data); - ret = qspi_get_zephyr_ret_code(res); - qspi_initialized = (ret == 0); - } - - qspi_unlock(dev); - - return ret; -#endif -} - -static void qspi_device_uninit(const struct device *dev) -{ - struct qspi_nor_data *dev_data = dev->data; - - if (dev_data->xip_enabled) { - return; - } - -#ifdef CONFIG_PM_DEVICE_RUNTIME - int ret = pm_device_runtime_put(dev); - - if (ret < 0) { - LOG_ERR("Failed to schedule device sleep: %d", ret); - } -#else - bool last = true; - - qspi_lock(dev); - -#ifdef CONFIG_MULTITHREADING - /* The last thread to finish using the driver uninit the QSPI */ - (void) k_sem_take(&dev_data->count, K_NO_WAIT); - last = (k_sem_count_get(&dev_data->count) == 0); -#endif - - if (last) { - while (nrfx_qspi_mem_busy_check() != NRFX_SUCCESS) { - if (IS_ENABLED(CONFIG_MULTITHREADING)) { - k_msleep(50); - } else { - k_busy_wait(50000); - } - } - - nrfx_qspi_uninit(); - - qspi_initialized = false; - } - - qspi_unlock(dev); -#endif -} - /* QSPI send custom command. * * If this is used for both send and receive the buffer sizes must be @@ -495,11 +435,8 @@ static int qspi_send_cmd(const struct device *dev, const struct qspi_cmd *cmd, .wren = wren, }; - qspi_lock(dev); - int res = nrfx_qspi_cinstr_xfer(&cinstr_cfg, tx_buf, rx_buf); - qspi_unlock(dev); return qspi_get_zephyr_ret_code(res); } @@ -524,27 +461,27 @@ static int qspi_rdsr(const struct device *dev, uint8_t sr_num) .op_code = opcode, .rx_buf = &sr_buf, }; - int ret = qspi_send_cmd(dev, &cmd, false); + int rc = qspi_send_cmd(dev, &cmd, false); - return (ret < 0) ? ret : sr; + return (rc < 0) ? rc : sr; } /* Wait until RDSR confirms write is not in progress. */ static int qspi_wait_while_writing(const struct device *dev) { - int ret; + int rc; do { - ret = qspi_rdsr(dev, 1); - } while ((ret >= 0) - && ((ret & SPI_NOR_WIP_BIT) != 0U)); + rc = qspi_rdsr(dev, 1); + } while ((rc >= 0) + && ((rc & SPI_NOR_WIP_BIT) != 0U)); - return (ret < 0) ? ret : 0; + return (rc < 0) ? rc : 0; } static int qspi_wrsr(const struct device *dev, uint8_t sr_val, uint8_t sr_num) { - int ret = 0; + int rc = 0; uint8_t opcode = SPI_NOR_CMD_WRSR; uint8_t length = 1; uint8_t sr_array[2] = {0}; @@ -557,12 +494,12 @@ static int qspi_wrsr(const struct device *dev, uint8_t sr_val, uint8_t sr_num) sr_array[0] = sr_val; #if SR1_WRITE_CLEARS_SR2 /* Writing sr1 clears sr2. need to read/modify/write both. */ - ret = qspi_rdsr(dev, 2); - if (ret < 0) { - LOG_ERR("RDSR for WRSR failed: %d", ret); - return ret; + rc = qspi_rdsr(dev, 2); + if (rc < 0) { + LOG_ERR("RDSR for WRSR failed: %d", rc); + return rc; } - sr_array[1] = ret; + sr_array[1] = rc; length = 2; #endif } else { /* sr_num == 2 */ @@ -572,12 +509,12 @@ static int qspi_wrsr(const struct device *dev, uint8_t sr_val, uint8_t sr_num) * Uses standard WRSR opcode */ sr_array[1] = sr_val; - ret = qspi_rdsr(dev, 1); - if (ret < 0) { - LOG_ERR("RDSR for WRSR failed: %d", ret); - return ret; + rc = qspi_rdsr(dev, 1); + if (rc < 0) { + LOG_ERR("RDSR for WRSR failed: %d", rc); + return rc; } - sr_array[0] = ret; + sr_array[0] = rc; length = 2; #elif IS_EQUAL(INST_0_QER, JESD216_DW15_QER_VAL_S2B1v6) /* Writing sr2 uses a dedicated WRSR2 command */ @@ -598,46 +535,30 @@ static int qspi_wrsr(const struct device *dev, uint8_t sr_val, uint8_t sr_num) .tx_buf = &sr_buf, }; - ret = qspi_send_cmd(dev, &cmd, true); + rc = qspi_send_cmd(dev, &cmd, true); /* Writing SR can take some time, and further * commands sent while it's happening can be * corrupted. Wait. */ - if (ret == 0) { - ret = qspi_wait_while_writing(dev); + if (rc == 0) { + rc = qspi_wait_while_writing(dev); } - return ret; + return rc; } #endif /* !IS_EQUAL(INST_0_QER, JESD216_DW15_QER_VAL_NONE) */ /* QSPI erase */ static int qspi_erase(const struct device *dev, uint32_t addr, uint32_t size) { - /* address must be sector-aligned */ - if ((addr % QSPI_SECTOR_SIZE) != 0) { - return -EINVAL; - } - - /* size must be a non-zero multiple of sectors */ - if ((size == 0) || (size % QSPI_SECTOR_SIZE) != 0) { - return -EINVAL; - } - - int rv = 0; const struct qspi_nor_config *params = dev->config; + int rc, rc2; - rv = qspi_device_init(dev); - if (rv != 0) { - goto out; - } - qspi_trans_lock(dev); - rv = qspi_nor_write_protection_set(dev, false); - if (rv != 0) { - goto out_trans_unlock; + rc = qspi_nor_write_protection_set(dev, false); + if (rc != 0) { + return rc; } - qspi_lock(dev); while (size > 0) { nrfx_err_t res = !NRFX_SUCCESS; uint32_t adj = 0; @@ -668,76 +589,20 @@ static int qspi_erase(const struct device *dev, uint32_t addr, uint32_t size) size -= adj; } else { LOG_ERR("erase error at 0x%lx size %zu", (long)addr, size); - rv = qspi_get_zephyr_ret_code(res); + rc = qspi_get_zephyr_ret_code(res); break; } } - qspi_unlock(dev); - - int rv2 = qspi_nor_write_protection_set(dev, true); - - if (!rv) { - rv = rv2; - } -out_trans_unlock: - qspi_trans_unlock(dev); + rc2 = qspi_nor_write_protection_set(dev, true); -out: - qspi_device_uninit(dev); - return rv; + return rc != 0 ? rc : rc2; } -/* Configures QSPI memory for the transfer */ -static int qspi_nrfx_configure(const struct device *dev) +static int configure_chip(const struct device *dev) { - struct qspi_nor_data *dev_data = dev->data; const struct qspi_nor_config *dev_config = dev->config; - -#if defined(CONFIG_SOC_SERIES_NRF53X) - /* When the QSPI peripheral is activated, during the nrfx_qspi driver - * initialization, it reads the status of the connected flash chip. - * Make sure this transaction is performed with a valid base clock - * divider. - */ - nrf_clock_hfclk192m_div_set(NRF_CLOCK, BASE_CLOCK_DIV); -#endif - - nrfx_err_t res = nrfx_qspi_init(&dev_config->nrfx_cfg, - qspi_handler, - dev_data); - -#if defined(CONFIG_SOC_SERIES_NRF53X) - /* Restore the default /4 divider after the QSPI initialization. */ - nrf_clock_hfclk192m_div_set(NRF_CLOCK, NRF_CLOCK_HFCLK_DIV_4); -#endif - - int ret = qspi_get_zephyr_ret_code(res); - if (ret < 0) { - return ret; - } - -#if DT_INST_NODE_HAS_PROP(0, rx_delay) - if (!nrf53_errata_121()) { - nrf_qspi_iftiming_set(NRF_QSPI, DT_INST_PROP(0, rx_delay)); - } -#endif - - /* It may happen that after the flash chip was previously put into - * the DPD mode, the system was reset but the flash chip was not. - * Consequently, the flash chip can be in the DPD mode at this point. - * Some flash chips will just exit the DPD mode on the first CS pulse, - * but some need to receive the dedicated command to do it, so send it. - * This can be the case even if the current image does not have - * CONFIG_PM_DEVICE set to enter DPD mode, as a previously executing image - * (for example the main image if the currently executing image is the - * bootloader) might have set DPD mode before reboot. As a result, - * attempt to exit DPD mode regardless of whether CONFIG_PM_DEVICE is set. - */ - ret = exit_dpd(dev); - if (ret < 0) { - return ret; - } + int rc = 0; /* Set QE to match transfer mode. If not using quad * it's OK to leave QE set, but doing so prevents use @@ -767,28 +632,28 @@ static int qspi_nrfx_configure(const struct device *dev) return -EINVAL; #endif - ret = qspi_rdsr(dev, sr_num); - if (ret < 0) { - LOG_ERR("RDSR failed: %d", ret); - return ret; + rc = qspi_rdsr(dev, sr_num); + if (rc < 0) { + LOG_ERR("RDSR failed: %d", rc); + return rc; } - uint8_t sr = (uint8_t)ret; + uint8_t sr = (uint8_t)rc; bool qe_state = ((sr & qe_mask) != 0U); LOG_DBG("RDSR %02x QE %d need %d: %s", sr, qe_state, qe_value, (qe_state != qe_value) ? "updating" : "no-change"); - ret = 0; + rc = 0; if (qe_state != qe_value) { sr ^= qe_mask; - ret = qspi_wrsr(dev, sr, sr_num); + rc = qspi_wrsr(dev, sr, sr_num); } - if (ret < 0) { + if (rc < 0) { LOG_ERR("QE %s failed: %d", qe_value ? "set" : "clear", - ret); - return ret; + rc); + return rc; } #endif @@ -800,20 +665,19 @@ static int qspi_nrfx_configure(const struct device *dev) /* Call will send write enable before instruction if that * requirement is encoded in INST_0_4BA. */ - ret = qspi_send_cmd(dev, &cmd, (INST_0_4BA & 0x02)); + rc = qspi_send_cmd(dev, &cmd, (INST_0_4BA & 0x02)); - if (ret < 0) { - LOG_ERR("E4BA cmd issue failed: %d.", ret); + if (rc < 0) { + LOG_ERR("E4BA cmd issue failed: %d.", rc); } else { LOG_DBG("E4BA cmd issued."); } } - return ret; + return rc; } -static int qspi_read_jedec_id(const struct device *dev, - uint8_t *id) +static int qspi_rdid(const struct device *dev, uint8_t *id) { const struct qspi_buf rx_buf = { .buf = id, @@ -824,18 +688,24 @@ static int qspi_read_jedec_id(const struct device *dev, .rx_buf = &rx_buf, }; - int ret = qspi_device_init(dev); - - if (ret == 0) { - ret = qspi_send_cmd(dev, &cmd, false); - } - qspi_device_uninit(dev); - - return ret; + return qspi_send_cmd(dev, &cmd, false); } #if defined(CONFIG_FLASH_JESD216_API) +static int qspi_read_jedec_id(const struct device *dev, uint8_t *id) +{ + int rc; + + qspi_acquire(dev); + + rc = qspi_rdid(dev, id); + + qspi_release(dev); + + return rc; +} + static int qspi_sfdp_read(const struct device *dev, off_t offset, void *data, size_t len) { @@ -853,17 +723,10 @@ static int qspi_sfdp_read(const struct device *dev, off_t offset, .io2_level = true, .io3_level = true, }; + nrfx_err_t res; - int ret = qspi_device_init(dev); - nrfx_err_t res = NRFX_SUCCESS; - - if (ret != 0) { - LOG_DBG("qspi_device_init: %d", ret); - qspi_device_uninit(dev); - return ret; - } + qspi_acquire(dev); - qspi_lock(dev); res = nrfx_qspi_lfm_start(&cinstr_cfg); if (res != NRFX_SUCCESS) { LOG_DBG("lfm_start: %x", res); @@ -883,40 +746,13 @@ static int qspi_sfdp_read(const struct device *dev, off_t offset, } out: - qspi_unlock(dev); - qspi_device_uninit(dev); + qspi_release(dev); + return qspi_get_zephyr_ret_code(res); } #endif /* CONFIG_FLASH_JESD216_API */ -/** - * @brief Retrieve the Flash JEDEC ID and compare it with the one expected - * - * @param dev The device structure - * @return 0 on success, negative errno code otherwise - */ -static inline int qspi_nor_read_id(const struct device *dev) -{ - uint8_t id[SPI_NOR_MAX_ID_LEN]; - int ret = qspi_read_jedec_id(dev, id); - - if (ret != 0) { - return -EIO; - } - - const struct qspi_nor_config *qnc = dev->config; - - if (memcmp(qnc->id, id, SPI_NOR_MAX_ID_LEN) != 0) { - LOG_ERR("JEDEC id [%02x %02x %02x] expect [%02x %02x %02x]", - id[0], id[1], id[2], - qnc->id[0], qnc->id[1], qnc->id[2]); - return -ENODEV; - } - - return 0; -} - static inline nrfx_err_t read_non_aligned(const struct device *dev, off_t addr, void *dest, size_t size) @@ -991,6 +827,9 @@ static inline nrfx_err_t read_non_aligned(const struct device *dev, static int qspi_nor_read(const struct device *dev, off_t addr, void *dest, size_t size) { + const struct qspi_nor_config *params = dev->config; + nrfx_err_t res; + if (!dest) { return -EINVAL; } @@ -1000,8 +839,6 @@ static int qspi_nor_read(const struct device *dev, off_t addr, void *dest, return 0; } - const struct qspi_nor_config *params = dev->config; - /* affected region should be within device */ if (addr < 0 || (addr + size) > params->size) { @@ -1011,23 +848,13 @@ static int qspi_nor_read(const struct device *dev, off_t addr, void *dest, return -EINVAL; } - int rc = qspi_device_init(dev); + qspi_acquire(dev); - if (rc != 0) { - goto out; - } + res = read_non_aligned(dev, addr, dest, size); - qspi_lock(dev); - - nrfx_err_t res = read_non_aligned(dev, addr, dest, size); + qspi_release(dev); - qspi_unlock(dev); - - rc = qspi_get_zephyr_ret_code(res); - -out: - qspi_device_uninit(dev); - return rc; + return qspi_get_zephyr_ret_code(res); } /* addr aligned, sptr not null, slen less than 4 */ @@ -1092,6 +919,9 @@ static int qspi_nor_write(const struct device *dev, off_t addr, const void *src, size_t size) { + const struct qspi_nor_config *params = dev->config; + int rc, rc2; + if (!src) { return -EINVAL; } @@ -1106,8 +936,6 @@ static int qspi_nor_write(const struct device *dev, off_t addr, return -EINVAL; } - const struct qspi_nor_config *params = dev->config; - /* affected region should be within device */ if (addr < 0 || (addr + size) > params->size) { @@ -1117,18 +945,12 @@ static int qspi_nor_write(const struct device *dev, off_t addr, return -EINVAL; } - nrfx_err_t res = NRFX_SUCCESS; - - int rc = qspi_device_init(dev); + qspi_acquire(dev); - if (rc != 0) { - goto out; - } + rc = qspi_nor_write_protection_set(dev, false); + if (rc == 0) { + nrfx_err_t res; - qspi_trans_lock(dev); - res = qspi_nor_write_protection_set(dev, false); - qspi_lock(dev); - if (!res) { if (size < 4U) { res = write_sub_word(dev, addr, src, size); } else if (!nrfx_is_in_ram(src) || @@ -1138,25 +960,31 @@ static int qspi_nor_write(const struct device *dev, off_t addr, res = nrfx_qspi_write(src, size, addr); qspi_wait_for_completion(dev, res); } + + rc = qspi_get_zephyr_ret_code(res); } - qspi_unlock(dev); - int res2 = qspi_nor_write_protection_set(dev, true); + rc2 = qspi_nor_write_protection_set(dev, true); - qspi_trans_unlock(dev); - if (!res) { - res = res2; - } + qspi_release(dev); - rc = qspi_get_zephyr_ret_code(res); -out: - qspi_device_uninit(dev); - return rc; + return rc != 0 ? rc : rc2; } static int qspi_nor_erase(const struct device *dev, off_t addr, size_t size) { const struct qspi_nor_config *params = dev->config; + int rc; + + /* address must be sector-aligned */ + if ((addr % QSPI_SECTOR_SIZE) != 0) { + return -EINVAL; + } + + /* size must be a non-zero multiple of sectors */ + if ((size == 0) || (size % QSPI_SECTOR_SIZE) != 0) { + return -EINVAL; + } /* affected region should be within device */ if (addr < 0 || @@ -1167,78 +995,121 @@ static int qspi_nor_erase(const struct device *dev, off_t addr, size_t size) return -EINVAL; } - int ret = qspi_erase(dev, addr, size); + qspi_acquire(dev); + + rc = qspi_erase(dev, addr, size); + + qspi_release(dev); - return ret; + return rc; } static int qspi_nor_write_protection_set(const struct device *dev, bool write_protect) { - int ret = 0; + int rc = 0; struct qspi_cmd cmd = { .op_code = ((write_protect) ? SPI_NOR_CMD_WRDI : SPI_NOR_CMD_WREN), }; if (qspi_send_cmd(dev, &cmd, false) != 0) { - ret = -EIO; + rc = -EIO; } - return ret; + return rc; } -/** - * @brief Configure the flash - * - * @param dev The flash device structure - * @param info The flash info structure - * @return 0 on success, negative errno code otherwise - */ -static int qspi_nor_configure(const struct device *dev) +static int qspi_init(const struct device *dev) { - int ret = qspi_nrfx_configure(dev); + const struct qspi_nor_config *dev_config = dev->config; + uint8_t id[SPI_NOR_MAX_ID_LEN]; + nrfx_err_t res; + int rc; - if (ret != 0) { - return ret; + res = nrfx_qspi_init(&dev_config->nrfx_cfg, qspi_handler, dev->data); + rc = qspi_get_zephyr_ret_code(res); + if (rc < 0) { + return rc; } -#ifdef CONFIG_PM_DEVICE_RUNTIME - ret = pm_device_runtime_enable(dev); - if (ret < 0) { - LOG_ERR("Failed to enable runtime power management: %d", ret); - } else { - LOG_DBG("Runtime power management enabled"); +#if DT_INST_NODE_HAS_PROP(0, rx_delay) + if (!nrf53_errata_121()) { + nrf_qspi_iftiming_set(NRF_QSPI, DT_INST_PROP(0, rx_delay)); } -#else - qspi_device_uninit(dev); #endif - /* now the spi bus is configured, we can verify the flash id */ - if (qspi_nor_read_id(dev) != 0) { + /* It may happen that after the flash chip was previously put into + * the DPD mode, the system was reset but the flash chip was not. + * Consequently, the flash chip can be in the DPD mode at this point. + * Some flash chips will just exit the DPD mode on the first CS pulse, + * but some need to receive the dedicated command to do it, so send it. + * This can be the case even if the current image does not have + * CONFIG_PM_DEVICE set to enter DPD mode, as a previously executing image + * (for example the main image if the currently executing image is the + * bootloader) might have set DPD mode before reboot. As a result, + * attempt to exit DPD mode regardless of whether CONFIG_PM_DEVICE is set. + */ + rc = exit_dpd(dev); + if (rc < 0) { + return rc; + } + + /* Retrieve the Flash JEDEC ID and compare it with the one expected. */ + rc = qspi_rdid(dev, id); + if (rc < 0) { + return rc; + } + + if (memcmp(dev_config->id, id, SPI_NOR_MAX_ID_LEN) != 0) { + LOG_ERR("JEDEC id [%02x %02x %02x] expect [%02x %02x %02x]", + id[0], id[1], id[2], dev_config->id[0], + dev_config->id[1], dev_config->id[2]); return -ENODEV; } - return 0; + /* The chip is correct, it can be configured now. */ + return configure_chip(dev); } -/** - * @brief Initialize and configure the flash - * - * @param name The flash name - * @return 0 on success, negative errno code otherwise - */ static int qspi_nor_init(const struct device *dev) { const struct qspi_nor_config *dev_config = dev->config; - int ret = pinctrl_apply_state(dev_config->pcfg, PINCTRL_STATE_DEFAULT); + int rc; - if (ret < 0) { - return ret; + rc = pinctrl_apply_state(dev_config->pcfg, PINCTRL_STATE_DEFAULT); + if (rc < 0) { + return rc; } IRQ_CONNECT(DT_IRQN(QSPI_NODE), DT_IRQ(QSPI_NODE, priority), nrfx_isr, nrfx_qspi_irq_handler, 0); - return qspi_nor_configure(dev); + + qspi_clock_div_change(); + + rc = qspi_init(dev); + + qspi_clock_div_restore(); + +#ifdef CONFIG_PM_DEVICE_RUNTIME + int rc2 = pm_device_runtime_enable(dev); + + if (rc2 < 0) { + LOG_ERR("Failed to enable runtime power management: %d", rc2); + } else { + LOG_DBG("Runtime power management enabled"); + } +#endif + +#ifdef CONFIG_NORDIC_QSPI_NOR_XIP + if (rc == 0) { + /* Enable XIP mode for QSPI NOR flash, this will prevent the + * flash from being powered down + */ + z_impl_nrf_qspi_nor_xip_enable(dev, true); + } +#endif + + return rc; } #if defined(CONFIG_FLASH_PAGE_LAYOUT) @@ -1302,11 +1173,11 @@ static int enter_dpd(const struct device *const dev) .op_code = SPI_NOR_CMD_DPD, }; uint32_t t_enter_dpd = DT_INST_PROP_OR(0, t_enter_dpd, 0); - int ret; + int rc; - ret = qspi_send_cmd(dev, &cmd, false); - if (ret < 0) { - return ret; + rc = qspi_send_cmd(dev, &cmd, false); + if (rc < 0) { + return rc; } if (t_enter_dpd) { @@ -1324,15 +1195,34 @@ static int enter_dpd(const struct device *const dev) static int exit_dpd(const struct device *const dev) { if (IS_ENABLED(DT_INST_PROP(0, has_dpd))) { + nrf_qspi_pins_t pins; + nrf_qspi_pins_t disconnected_pins = { + .sck_pin = NRF_QSPI_PIN_NOT_CONNECTED, + .csn_pin = NRF_QSPI_PIN_NOT_CONNECTED, + .io0_pin = NRF_QSPI_PIN_NOT_CONNECTED, + .io1_pin = NRF_QSPI_PIN_NOT_CONNECTED, + .io2_pin = NRF_QSPI_PIN_NOT_CONNECTED, + .io3_pin = NRF_QSPI_PIN_NOT_CONNECTED, + }; struct qspi_cmd cmd = { .op_code = SPI_NOR_CMD_RDPD, }; uint32_t t_exit_dpd = DT_INST_PROP_OR(0, t_exit_dpd, 0); - int ret; + nrfx_err_t res; + int rc; + + nrf_qspi_pins_get(NRF_QSPI, &pins); + nrf_qspi_pins_set(NRF_QSPI, &disconnected_pins); + res = nrfx_qspi_activate(true); + nrf_qspi_pins_set(NRF_QSPI, &pins); + + if (res != NRFX_SUCCESS) { + return -EIO; + } - ret = qspi_send_cmd(dev, &cmd, false); - if (ret < 0) { - return ret; + rc = qspi_send_cmd(dev, &cmd, false); + if (rc < 0) { + return rc; } if (t_exit_dpd) { @@ -1347,101 +1237,97 @@ static int exit_dpd(const struct device *const dev) } #ifdef CONFIG_PM_DEVICE +static int qspi_suspend(const struct device *dev) +{ + const struct qspi_nor_config *dev_config = dev->config; + nrfx_err_t res; + int rc; + + res = nrfx_qspi_mem_busy_check(); + if (res != NRFX_SUCCESS) { + return -EBUSY; + } + + rc = enter_dpd(dev); + if (rc < 0) { + return rc; + } + + nrfx_qspi_uninit(); + + return pinctrl_apply_state(dev_config->pcfg, PINCTRL_STATE_SLEEP); +} + +static int qspi_resume(const struct device *dev) +{ + const struct qspi_nor_config *dev_config = dev->config; + nrfx_err_t res; + int rc; + + rc = pinctrl_apply_state(dev_config->pcfg, PINCTRL_STATE_DEFAULT); + if (rc < 0) { + return rc; + } + + res = nrfx_qspi_init(&dev_config->nrfx_cfg, qspi_handler, dev->data); + if (res != NRFX_SUCCESS) { + return -EIO; + } + + return exit_dpd(dev); +} + static int qspi_nor_pm_action(const struct device *dev, enum pm_device_action action) { - struct qspi_nor_data *dev_data = dev->data; - const struct qspi_nor_config *dev_config = dev->config; - int ret; - nrfx_err_t err; + int rc; if (pm_device_is_busy(dev)) { return -EBUSY; } + qspi_lock(dev); + qspi_clock_div_change(); + switch (action) { case PM_DEVICE_ACTION_SUSPEND: -#ifndef CONFIG_PM_DEVICE_RUNTIME - /* If PM_DEVICE_RUNTIME, we don't uninit after RESUME */ - ret = qspi_device_init(dev); - if (ret < 0) { - return ret; - } -#endif - - if (nrfx_qspi_mem_busy_check() != NRFX_SUCCESS) { - return -EBUSY; - } - - ret = enter_dpd(dev); - if (ret < 0) { - return ret; - } - - nrfx_qspi_uninit(); - ret = pinctrl_apply_state(dev_config->pcfg, - PINCTRL_STATE_SLEEP); - if (ret < 0) { - return ret; - } + rc = qspi_suspend(dev); break; case PM_DEVICE_ACTION_RESUME: - ret = pinctrl_apply_state(dev_config->pcfg, - PINCTRL_STATE_DEFAULT); - if (ret < 0) { - return ret; - } - err = nrfx_qspi_init(&dev_config->nrfx_cfg, - qspi_handler, - dev_data); - if (err != NRFX_SUCCESS) { - return -EIO; - } - - ret = exit_dpd(dev); - if (ret < 0) { - return ret; - } - -#ifndef CONFIG_PM_DEVICE_RUNTIME - /* If PM_DEVICE_RUNTIME, we're immediately going to use the device */ - qspi_device_uninit(dev); -#endif + rc = qspi_resume(dev); break; default: - return -ENOTSUP; + rc = -ENOTSUP; } - return 0; + qspi_clock_div_restore(); + qspi_unlock(dev); + + return rc; } #endif /* CONFIG_PM_DEVICE */ void z_impl_nrf_qspi_nor_xip_enable(const struct device *dev, bool enable) { struct qspi_nor_data *dev_data = dev->data; - int ret; if (dev_data->xip_enabled == enable) { return; } - ret = qspi_device_init(dev); - - if (ret != 0) { - LOG_ERR("NRF QSPI NOR XIP %s failed with %d\n", enable ? "enable" : "disable", ret); - return; - } + qspi_acquire(dev); #if NRF_QSPI_HAS_XIPEN nrf_qspi_xip_set(NRF_QSPI, enable); #endif - qspi_lock(dev); + if (enable) { + (void)nrfx_qspi_activate(false); + } dev_data->xip_enabled = enable; - qspi_unlock(dev); - qspi_device_uninit(dev); + qspi_release(dev); } #ifdef CONFIG_USERSPACE @@ -1459,11 +1345,12 @@ void z_vrfy_nrf_qspi_nor_xip_enable(const struct device *dev, bool enable) #endif /* CONFIG_USERSPACE */ static struct qspi_nor_data qspi_nor_dev_data = { +#if !defined(CONFIG_PM_DEVICE_RUNTIME) && defined(CONFIG_MULTITHREADING) + .count = Z_SEM_INITIALIZER(qspi_nor_dev_data.count, 0, K_SEM_MAX_LIMIT), +#endif #ifdef CONFIG_MULTITHREADING - .trans = Z_SEM_INITIALIZER(qspi_nor_dev_data.trans, 1, 1), .sem = Z_SEM_INITIALIZER(qspi_nor_dev_data.sem, 1, 1), .sync = Z_SEM_INITIALIZER(qspi_nor_dev_data.sync, 0, 1), - .count = Z_SEM_INITIALIZER(qspi_nor_dev_data.count, 0, K_SEM_MAX_LIMIT), #endif /* CONFIG_MULTITHREADING */ }; diff --git a/drivers/flash/soc_flash_nrf.c b/drivers/flash/soc_flash_nrf.c index fa433d1044b..ea76ae1e3f8 100644 --- a/drivers/flash/soc_flash_nrf.c +++ b/drivers/flash/soc_flash_nrf.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2018 Nordic Semiconductor ASA + * Copyright (c) 2017-2023 Nordic Semiconductor ASA * Copyright (c) 2016 Linaro Limited * Copyright (c) 2016 Intel Corporation * @@ -37,6 +37,11 @@ LOG_MODULE_REGISTER(flash_nrf); #define SOC_NV_FLASH_NODE DT_INST(0, soc_nv_flash) +#if CONFIG_TRUSTED_EXECUTION_NONSECURE && USE_PARTITION_MANAGER +#include +#include +#endif /* CONFIG_TRUSTED_EXECUTION_NONSECURE && USE_PARTITION_MANAGER */ + #ifndef CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE #define FLASH_SLOT_WRITE 7500 #if defined(CONFIG_SOC_FLASH_NRF_PARTIAL_ERASE) @@ -116,6 +121,26 @@ static inline bool is_uicr_addr_valid(off_t addr, size_t len) #endif /* CONFIG_SOC_FLASH_NRF_UICR */ } +#if CONFIG_SOC_FLASH_NRF_UICR && IS_ENABLED(NRF91_ERRATA_7_ENABLE_WORKAROUND) +static inline void nrf91_errata_7_enter(void) +{ + __disable_irq(); +} + +static inline void nrf91_errata_7_exit(void) +{ + __DSB(); + __enable_irq(); +} + +static void nrf_buffer_read_91_uicr(void *data, off_t addr, size_t len) +{ + nrf91_errata_7_enter(); + nrf_nvmc_buffer_read(data, (uint32_t)addr, len); + nrf91_errata_7_exit(); +} +#endif + static void nvmc_wait_ready(void) { while (!nrfx_nvmc_write_done_check()) { @@ -125,9 +150,11 @@ static void nvmc_wait_ready(void) static int flash_nrf_read(const struct device *dev, off_t addr, void *data, size_t len) { + const bool within_uicr = is_uicr_addr_valid(addr, len); + if (is_regular_addr_valid(addr, len)) { addr += DT_REG_ADDR(SOC_NV_FLASH_NODE); - } else if (!is_uicr_addr_valid(addr, len)) { + } else if (!within_uicr) { LOG_ERR("invalid address: 0x%08lx:%zu", (unsigned long)addr, len); return -EINVAL; @@ -137,6 +164,18 @@ static int flash_nrf_read(const struct device *dev, off_t addr, return 0; } +#if CONFIG_TRUSTED_EXECUTION_NONSECURE && USE_PARTITION_MANAGER && PM_APP_ADDRESS + if (addr < PM_APP_ADDRESS) { + return soc_secure_mem_read(data, (void *)addr, len); + } +#endif +#if CONFIG_SOC_FLASH_NRF_UICR && IS_ENABLED(NRF91_ERRATA_7_ENABLE_WORKAROUND) + if (within_uicr) { + nrf_buffer_read_91_uicr(data, (uint32_t)addr, len); + return 0; + } +#endif + nrf_nvmc_buffer_read(data, (uint32_t)addr, len); return 0; diff --git a/drivers/gpio/Kconfig.nrfx b/drivers/gpio/Kconfig.nrfx index 6757b6b12e9..356c43cb5fa 100644 --- a/drivers/gpio/Kconfig.nrfx +++ b/drivers/gpio/Kconfig.nrfx @@ -1,10 +1,19 @@ # Copyright (c) 2018 Nordic Semiconductor ASA # SPDX-License-Identifier: Apache-2.0 -config GPIO_NRFX +menuconfig GPIO_NRFX bool "nRF GPIO driver" default y depends on DT_HAS_NORDIC_NRF_GPIO_ENABLED select NRFX_GPIOTE help Enable GPIO driver for nRF line of MCUs. + +config GPIO_NRFX_INTERRUPT + bool "Interrupt support" + depends on GPIO_NRFX + default y + help + The option can be used to disable the GPIO interrupt support to + significantly reduce memory footprint in case of application that does + not need GPIO interrupts. diff --git a/drivers/gpio/gpio_ads114s0x.c b/drivers/gpio/gpio_ads114s0x.c index 79383d38154..13f4f7f1266 100644 --- a/drivers/gpio/gpio_ads114s0x.c +++ b/drivers/gpio/gpio_ads114s0x.c @@ -106,17 +106,6 @@ static int gpio_ads114s0x_port_toggle_bits(const struct device *dev, gpio_port_p return ads114s0x_gpio_port_toggle_bits(config->parent, pins); } -static int gpio_ads114s0x_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin, - enum gpio_int_mode mode, enum gpio_int_trig trig) -{ - ARG_UNUSED(dev); - ARG_UNUSED(pin); - ARG_UNUSED(mode); - ARG_UNUSED(trig); - - return -ENOTSUP; -} - static int gpio_ads114s0x_init(const struct device *dev) { const struct gpio_ads114s0x_config *config = dev->config; @@ -135,7 +124,6 @@ static const struct gpio_driver_api gpio_ads114s0x_api = { .port_set_bits_raw = gpio_ads114s0x_port_set_bits_raw, .port_clear_bits_raw = gpio_ads114s0x_port_clear_bits_raw, .port_toggle_bits = gpio_ads114s0x_port_toggle_bits, - .pin_interrupt_configure = gpio_ads114s0x_pin_interrupt_configure, .port_get_raw = gpio_ads114s0x_port_get_raw, }; diff --git a/drivers/gpio/gpio_axp192.c b/drivers/gpio/gpio_axp192.c index ee75fe9b276..95625d99dfe 100644 --- a/drivers/gpio/gpio_axp192.c +++ b/drivers/gpio/gpio_axp192.c @@ -159,17 +159,6 @@ static int gpio_axp192_port_toggle_bits(const struct device *dev, gpio_port_pins return ret; } -static int gpio_axp192_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin, - enum gpio_int_mode mode, enum gpio_int_trig trig) -{ - ARG_UNUSED(dev); - ARG_UNUSED(pin); - ARG_UNUSED(mode); - ARG_UNUSED(trig); - - return -ENOTSUP; -} - #ifdef CONFIG_GPIO_GET_CONFIG static int gpio_axp192_get_config(const struct device *dev, gpio_pin_t pin, gpio_flags_t *out_flags) { @@ -276,7 +265,6 @@ static const struct gpio_driver_api gpio_axp192_api = { .port_set_bits_raw = gpio_axp192_port_set_bits_raw, .port_clear_bits_raw = gpio_axp192_port_clear_bits_raw, .port_toggle_bits = gpio_axp192_port_toggle_bits, - .pin_interrupt_configure = gpio_axp192_pin_interrupt_configure, .manage_callback = gpio_axp192_manage_callback, #ifdef CONFIG_GPIO_GET_DIRECTION .port_get_direction = gpio_axp192_port_get_direction, diff --git a/drivers/gpio/gpio_bd8lb600fs.c b/drivers/gpio/gpio_bd8lb600fs.c index 555a63b91fd..30ae851d90b 100644 --- a/drivers/gpio/gpio_bd8lb600fs.c +++ b/drivers/gpio/gpio_bd8lb600fs.c @@ -193,12 +193,6 @@ static int bd8lb600fs_port_toggle_bits(const struct device *dev, uint32_t mask) return result; } -static int bd8lb600fs_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin, - enum gpio_int_mode mode, enum gpio_int_trig trig) -{ - return -ENOTSUP; -} - static const struct gpio_driver_api api_table = { .pin_configure = bd8lb600fs_pin_configure, .port_get_raw = bd8lb600fs_port_get_raw, @@ -206,7 +200,6 @@ static const struct gpio_driver_api api_table = { .port_set_bits_raw = bd8lb600fs_port_set_bits_raw, .port_clear_bits_raw = bd8lb600fs_port_clear_bits_raw, .port_toggle_bits = bd8lb600fs_port_toggle_bits, - .pin_interrupt_configure = bd8lb600fs_pin_interrupt_configure, }; static int bd8lb600fs_init(const struct device *dev) diff --git a/drivers/gpio/gpio_creg_gpio.c b/drivers/gpio/gpio_creg_gpio.c index 05b09184df9..1b9f4d63a50 100644 --- a/drivers/gpio/gpio_creg_gpio.c +++ b/drivers/gpio/gpio_creg_gpio.c @@ -104,14 +104,6 @@ static int port_toggle_bits(const struct device *dev, return port_write(dev, 0, 0, pins); } -static int pin_interrupt_configure(const struct device *dev, - gpio_pin_t pin, - enum gpio_int_mode mode, - enum gpio_int_trig trig) -{ - return -ENOTSUP; -} - static int pin_config(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags) @@ -163,7 +155,6 @@ static const struct gpio_driver_api api_table = { .port_set_bits_raw = port_set_bits, .port_clear_bits_raw = port_clear_bits, .port_toggle_bits = port_toggle_bits, - .pin_interrupt_configure = pin_interrupt_configure, }; static const struct creg_gpio_config creg_gpio_cfg = { diff --git a/drivers/gpio/gpio_cy8c95xx.c b/drivers/gpio/gpio_cy8c95xx.c index b540a7b96bf..155bfb4988a 100644 --- a/drivers/gpio/gpio_cy8c95xx.c +++ b/drivers/gpio/gpio_cy8c95xx.c @@ -214,14 +214,6 @@ static int port_toggle_bits(const struct device *dev, return port_write(dev, 0, 0, pins); } -static int pin_interrupt_configure(const struct device *dev, - gpio_pin_t pin, - enum gpio_int_mode mode, - enum gpio_int_trig trig) -{ - return -ENOTSUP; -} - /** * @brief Initialization function of CY8C95XX * @@ -277,7 +269,6 @@ static const struct gpio_driver_api api_table = { .port_set_bits_raw = port_set_bits, .port_clear_bits_raw = port_clear_bits, .port_toggle_bits = port_toggle_bits, - .pin_interrupt_configure = pin_interrupt_configure, }; static struct k_sem cy8c95xx_lock = Z_SEM_INITIALIZER(cy8c95xx_lock, 1, 1); diff --git a/drivers/gpio/gpio_fxl6408.c b/drivers/gpio/gpio_fxl6408.c index 164a4396c13..7352390e371 100644 --- a/drivers/gpio/gpio_fxl6408.c +++ b/drivers/gpio/gpio_fxl6408.c @@ -377,15 +377,6 @@ static int gpio_fxl6408_port_toggle_bits(const struct device *dev, return ret; } -static int gpio_fxl6408_pin_interrupt_configure(const struct device *port, - gpio_pin_t pin, - enum gpio_int_mode mode, - enum gpio_int_trig trig) -{ - LOG_DBG("Pin interrupts not supported."); - return -ENOTSUP; -} - int gpio_fxl6408_init(const struct device *dev) { struct gpio_fxl6408_drv_data *const drv_data = @@ -409,7 +400,6 @@ static const struct gpio_driver_api gpio_fxl_driver = { .port_set_bits_raw = gpio_fxl6408_port_set_bits_raw, .port_clear_bits_raw = gpio_fxl6408_port_clear_bits_raw, .port_toggle_bits = gpio_fxl6408_port_toggle_bits, - .pin_interrupt_configure = gpio_fxl6408_pin_interrupt_configure }; #define GPIO_FXL6408_DEVICE_INSTANCE(inst) \ diff --git a/drivers/gpio/gpio_lmp90xxx.c b/drivers/gpio/gpio_lmp90xxx.c index 1bd99d88cf2..2514ef35211 100644 --- a/drivers/gpio/gpio_lmp90xxx.c +++ b/drivers/gpio/gpio_lmp90xxx.c @@ -122,19 +122,6 @@ static int gpio_lmp90xxx_port_toggle_bits(const struct device *dev, return lmp90xxx_gpio_port_toggle_bits(config->parent, pins); } -static int gpio_lmp90xxx_pin_interrupt_configure(const struct device *dev, - gpio_pin_t pin, - enum gpio_int_mode mode, - enum gpio_int_trig trig) -{ - ARG_UNUSED(dev); - ARG_UNUSED(pin); - ARG_UNUSED(mode); - ARG_UNUSED(trig); - - return -ENOTSUP; -} - static int gpio_lmp90xxx_init(const struct device *dev) { const struct gpio_lmp90xxx_config *config = dev->config; @@ -154,7 +141,6 @@ static const struct gpio_driver_api gpio_lmp90xxx_api = { .port_set_bits_raw = gpio_lmp90xxx_port_set_bits_raw, .port_clear_bits_raw = gpio_lmp90xxx_port_clear_bits_raw, .port_toggle_bits = gpio_lmp90xxx_port_toggle_bits, - .pin_interrupt_configure = gpio_lmp90xxx_pin_interrupt_configure, .port_get_raw = gpio_lmp90xxx_port_get_raw, }; diff --git a/drivers/gpio/gpio_mcp23s17.c b/drivers/gpio/gpio_mcp23s17.c index 96a266fdf6d..4849c3a02fe 100644 --- a/drivers/gpio/gpio_mcp23s17.c +++ b/drivers/gpio/gpio_mcp23s17.c @@ -340,14 +340,6 @@ static int mcp23s17_port_toggle_bits(const struct device *dev, uint32_t mask) return ret; } -static int mcp23s17_pin_interrupt_configure(const struct device *dev, - gpio_pin_t pin, - enum gpio_int_mode mode, - enum gpio_int_trig trig) -{ - return -ENOTSUP; -} - static const struct gpio_driver_api api_table = { .pin_configure = mcp23s17_config, .port_get_raw = mcp23s17_port_get_raw, @@ -355,7 +347,6 @@ static const struct gpio_driver_api api_table = { .port_set_bits_raw = mcp23s17_port_set_bits_raw, .port_clear_bits_raw = mcp23s17_port_clear_bits_raw, .port_toggle_bits = mcp23s17_port_toggle_bits, - .pin_interrupt_configure = mcp23s17_pin_interrupt_configure, }; static int mcp23s17_init(const struct device *dev) diff --git a/drivers/gpio/gpio_mmio32.c b/drivers/gpio/gpio_mmio32.c index e9e58373b5b..0fe5e9fedd8 100644 --- a/drivers/gpio/gpio_mmio32.c +++ b/drivers/gpio/gpio_mmio32.c @@ -147,18 +147,6 @@ static int gpio_mmio32_port_toggle_bits(const struct device *dev, return 0; } -static int gpio_mmio32_pin_interrupt_configure(const struct device *dev, - gpio_pin_t pin, - enum gpio_int_mode mode, - enum gpio_int_trig trig) -{ - if (mode != GPIO_INT_MODE_DISABLED) { - return -ENOTSUP; - } - - return 0; -} - const struct gpio_driver_api gpio_mmio32_api = { .pin_configure = gpio_mmio32_config, .port_get_raw = gpio_mmio32_port_get_raw, @@ -166,7 +154,6 @@ const struct gpio_driver_api gpio_mmio32_api = { .port_set_bits_raw = gpio_mmio32_port_set_bits_raw, .port_clear_bits_raw = gpio_mmio32_port_clear_bits_raw, .port_toggle_bits = gpio_mmio32_port_toggle_bits, - .pin_interrupt_configure = gpio_mmio32_pin_interrupt_configure, }; int gpio_mmio32_init(const struct device *dev) diff --git a/drivers/gpio/gpio_neorv32.c b/drivers/gpio/gpio_neorv32.c index b448962bde7..b14c51c1471 100644 --- a/drivers/gpio/gpio_neorv32.c +++ b/drivers/gpio/gpio_neorv32.c @@ -151,19 +151,6 @@ static int neorv32_gpio_port_toggle_bits(const struct device *dev, return 0; } -static int neorv32_gpio_pin_interrupt_configure(const struct device *dev, - gpio_pin_t pin, - enum gpio_int_mode mode, - enum gpio_int_trig trig) -{ - ARG_UNUSED(dev); - ARG_UNUSED(pin); - ARG_UNUSED(mode); - ARG_UNUSED(trig); - - return -ENOTSUP; -} - static int neorv32_gpio_manage_callback(const struct device *dev, struct gpio_callback *cb, bool set) @@ -215,7 +202,6 @@ static const struct gpio_driver_api neorv32_gpio_driver_api = { .port_set_bits_raw = neorv32_gpio_port_set_bits_raw, .port_clear_bits_raw = neorv32_gpio_port_clear_bits_raw, .port_toggle_bits = neorv32_gpio_port_toggle_bits, - .pin_interrupt_configure = neorv32_gpio_pin_interrupt_configure, .manage_callback = neorv32_gpio_manage_callback, .get_pending_int = neorv32_gpio_get_pending_int, }; diff --git a/drivers/gpio/gpio_npm1300.c b/drivers/gpio/gpio_npm1300.c index 37e7f99c936..65c3cdd6fda 100644 --- a/drivers/gpio/gpio_npm1300.c +++ b/drivers/gpio/gpio_npm1300.c @@ -185,17 +185,6 @@ static int gpio_npm1300_port_toggle_bits(const struct device *dev, gpio_port_pin return gpio_npm1300_port_set_masked_raw(dev, pins, ~value); } -static int gpio_npm1300_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin, - enum gpio_int_mode mode, enum gpio_int_trig trig) -{ - ARG_UNUSED(dev); - ARG_UNUSED(pin); - ARG_UNUSED(mode); - ARG_UNUSED(trig); - - return -ENOTSUP; -} - static const struct gpio_driver_api gpio_npm1300_api = { .pin_configure = gpio_npm1300_configure, .port_get_raw = gpio_npm1300_port_get_raw, @@ -203,7 +192,6 @@ static const struct gpio_driver_api gpio_npm1300_api = { .port_set_bits_raw = gpio_npm1300_port_set_bits_raw, .port_clear_bits_raw = gpio_npm1300_port_clear_bits_raw, .port_toggle_bits = gpio_npm1300_port_toggle_bits, - .pin_interrupt_configure = gpio_npm1300_pin_interrupt_configure, }; static int gpio_npm1300_init(const struct device *dev) diff --git a/drivers/gpio/gpio_npm6001.c b/drivers/gpio/gpio_npm6001.c index 98bd4db49e0..f722c4275d6 100644 --- a/drivers/gpio/gpio_npm6001.c +++ b/drivers/gpio/gpio_npm6001.c @@ -192,19 +192,6 @@ static int gpio_npm6001_port_toggle_bits(const struct device *dev, ~val & NPM6001_PIN_MSK); } -static int gpio_npm6001_pin_interrupt_configure(const struct device *dev, - gpio_pin_t pin, - enum gpio_int_mode mode, - enum gpio_int_trig trig) -{ - ARG_UNUSED(dev); - ARG_UNUSED(pin); - ARG_UNUSED(mode); - ARG_UNUSED(trig); - - return -ENOTSUP; -} - static const struct gpio_driver_api gpio_npm6001_api = { .pin_configure = gpio_npm6001_configure, .port_get_raw = gpio_npm6001_port_get_raw, @@ -212,7 +199,6 @@ static const struct gpio_driver_api gpio_npm6001_api = { .port_set_bits_raw = gpio_npm6001_port_set_bits_raw, .port_clear_bits_raw = gpio_npm6001_port_clear_bits_raw, .port_toggle_bits = gpio_npm6001_port_toggle_bits, - .pin_interrupt_configure = gpio_npm6001_pin_interrupt_configure, }; static int gpio_npm6001_init(const struct device *dev) diff --git a/drivers/gpio/gpio_nrfx.c b/drivers/gpio/gpio_nrfx.c index 1683b8b2cea..0aa282dda37 100644 --- a/drivers/gpio/gpio_nrfx.c +++ b/drivers/gpio/gpio_nrfx.c @@ -85,17 +85,19 @@ static nrf_gpio_pin_pull_t get_pull(gpio_flags_t flags) static int gpio_nrfx_pin_configure(const struct device *port, gpio_pin_t pin, gpio_flags_t flags) { - nrfx_err_t err; + nrfx_err_t err = NRFX_SUCCESS; uint8_t ch; - bool free_ch; + bool free_ch = false; const struct gpio_nrfx_cfg *cfg = get_port_cfg(port); nrfx_gpiote_pin_t abs_pin = NRF_GPIO_PIN_MAP(cfg->port_num, pin); /* Get the GPIOTE channel associated with this pin, if any. It needs * to be freed when the pin is reconfigured or disconnected. */ - err = nrfx_gpiote_channel_get(abs_pin, &ch); - free_ch = (err == NRFX_SUCCESS); + if (IS_ENABLED(CONFIG_GPIO_NRFX_INTERRUPT)) { + err = nrfx_gpiote_channel_get(abs_pin, &ch); + free_ch = (err == NRFX_SUCCESS); + } if ((flags & (GPIO_INPUT | GPIO_OUTPUT)) == GPIO_DISCONNECTED) { /* Ignore the error code. The pin may not have been used. */ @@ -109,19 +111,21 @@ static int gpio_nrfx_pin_configure(const struct device *port, gpio_pin_t pin, return 0; } - nrfx_gpiote_trigger_config_t trigger_config = { - .trigger = NRFX_GPIOTE_TRIGGER_NONE - }; + if (IS_ENABLED(CONFIG_GPIO_NRFX_INTERRUPT)) { + nrfx_gpiote_trigger_config_t trigger_config = { + .trigger = NRFX_GPIOTE_TRIGGER_NONE + }; - /* Remove previously configured trigger when pin is reconfigured. */ - err = nrfx_gpiote_input_configure(abs_pin, NULL, &trigger_config, NULL); - if (err != NRFX_SUCCESS) { - return -EINVAL; - } + /* Remove previously configured trigger when pin is reconfigured. */ + err = nrfx_gpiote_input_configure(abs_pin, NULL, &trigger_config, NULL); + if (err != NRFX_SUCCESS) { + return -EINVAL; + } - if (free_ch) { - err = nrfx_gpiote_channel_free(ch); - __ASSERT_NO_MSG(err == NRFX_SUCCESS); + if (free_ch) { + err = nrfx_gpiote_channel_free(ch); + __ASSERT_NO_MSG(err == NRFX_SUCCESS); + } } if (flags & GPIO_OUTPUT) { @@ -219,6 +223,7 @@ static int gpio_nrfx_port_toggle_bits(const struct device *port, return 0; } +#ifdef CONFIG_GPIO_NRFX_INTERRUPT static nrfx_gpiote_trigger_t get_trigger(enum gpio_int_mode mode, enum gpio_int_trig trig) { @@ -285,6 +290,7 @@ static int gpio_nrfx_manage_callback(const struct device *port, return gpio_manage_callback(&get_port_data(port)->callbacks, callback, set); } +#endif /* CONFIG_GPIO_NRFX_INTERRUPT */ #ifdef CONFIG_GPIO_GET_DIRECTION static int gpio_nrfx_port_get_direction(const struct device *port, @@ -322,6 +328,7 @@ static int gpio_nrfx_port_get_direction(const struct device *port, } #endif /* CONFIG_GPIO_GET_DIRECTION */ +#ifdef CONFIG_GPIO_NRFX_INTERRUPT /* Get port device from port id. */ static const struct device *get_dev(uint32_t port_id) { @@ -358,6 +365,7 @@ static void nrfx_gpio_handler(nrfx_gpiote_pin_t abs_pin, gpio_fire_callbacks(list, port, BIT(pin)); } +#endif /* CONFIG_GPIO_NRFX_INTERRUPT */ #define GPIOTE_NODE DT_INST(0, nordic_nrf_gpiote) @@ -374,10 +382,12 @@ static int gpio_nrfx_init(const struct device *port) return -EIO; } +#ifdef CONFIG_GPIO_NRFX_INTERRUPT nrfx_gpiote_global_callback_set(nrfx_gpio_handler, NULL); IRQ_CONNECT(DT_IRQN(GPIOTE_NODE), DT_IRQ(GPIOTE_NODE, priority), nrfx_isr, nrfx_gpiote_irq_handler, 0); +#endif /* CONFIG_GPIO_NRFX_INTERRUPT */ return 0; } @@ -389,8 +399,10 @@ static const struct gpio_driver_api gpio_nrfx_drv_api_funcs = { .port_set_bits_raw = gpio_nrfx_port_set_bits_raw, .port_clear_bits_raw = gpio_nrfx_port_clear_bits_raw, .port_toggle_bits = gpio_nrfx_port_toggle_bits, +#ifdef CONFIG_GPIO_NRFX_INTERRUPT .pin_interrupt_configure = gpio_nrfx_pin_interrupt_configure, .manage_callback = gpio_nrfx_manage_callback, +#endif #ifdef CONFIG_GPIO_GET_DIRECTION .port_get_direction = gpio_nrfx_port_get_direction, #endif diff --git a/drivers/gpio/gpio_nxp_s32.c b/drivers/gpio/gpio_nxp_s32.c index 3bdf9f4599d..de5dbd39dd7 100644 --- a/drivers/gpio/gpio_nxp_s32.c +++ b/drivers/gpio/gpio_nxp_s32.c @@ -205,14 +205,12 @@ static void nxp_s32_gpio_isr(uint8_t pin, void *arg) gpio_fire_callbacks(&data->callbacks, dev, BIT(pin)); } -#endif /* CONFIG_NXP_S32_EIRQ */ static int nxp_s32_gpio_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin, enum gpio_int_mode mode, enum gpio_int_trig trig) { -#ifdef CONFIG_NXP_S32_EIRQ const struct gpio_nxp_s32_config *config = dev->config; const struct eirq_nxp_s32_info *eirq_info = config->eirq_info; @@ -255,35 +253,18 @@ static int nxp_s32_gpio_pin_interrupt_configure(const struct device *dev, } return 0; -#else - ARG_UNUSED(dev); - ARG_UNUSED(pin); - ARG_UNUSED(mode); - ARG_UNUSED(trig); - - return -ENOTSUP; -#endif } static int nxp_s32_gpio_manage_callback(const struct device *dev, struct gpio_callback *cb, bool set) { -#ifdef CONFIG_NXP_S32_EIRQ struct gpio_nxp_s32_data *data = dev->data; return gpio_manage_callback(&data->callbacks, cb, set); -#else - ARG_UNUSED(dev); - ARG_UNUSED(cb); - ARG_UNUSED(set); - - return -ENOTSUP; -#endif } static uint32_t nxp_s32_gpio_get_pending_int(const struct device *dev) { -#ifdef CONFIG_NXP_S32_EIRQ const struct gpio_nxp_s32_config *config = dev->config; const struct eirq_nxp_s32_info *eirq_info = config->eirq_info; @@ -300,14 +281,8 @@ static uint32_t nxp_s32_gpio_get_pending_int(const struct device *dev) * that GPIO port belongs to */ return eirq_nxp_s32_get_pending(eirq_info->eirq_dev); - -#else - ARG_UNUSED(dev); - - return -ENOTSUP; -#endif } - +#endif /* CONFIG_NXP_S32_EIRQ */ #ifdef CONFIG_GPIO_GET_CONFIG static int nxp_s32_gpio_pin_get_config(const struct device *dev, @@ -400,9 +375,11 @@ static const struct gpio_driver_api gpio_nxp_s32_driver_api = { .port_set_bits_raw = nxp_s32_gpio_port_set_bits_raw, .port_clear_bits_raw = nxp_s32_gpio_port_clear_bits_raw, .port_toggle_bits = nxp_s32_gpio_port_toggle_bits, +#ifdef CONFIG_NXP_S32_EIRQ .pin_interrupt_configure = nxp_s32_gpio_pin_interrupt_configure, .manage_callback = nxp_s32_gpio_manage_callback, .get_pending_int = nxp_s32_gpio_get_pending_int, +#endif #ifdef CONFIG_GPIO_GET_CONFIG .pin_get_config = nxp_s32_gpio_pin_get_config, #endif diff --git a/drivers/gpio/gpio_pca95xx.c b/drivers/gpio/gpio_pca95xx.c index 66b21fd5b88..f8489128332 100644 --- a/drivers/gpio/gpio_pca95xx.c +++ b/drivers/gpio/gpio_pca95xx.c @@ -644,7 +644,6 @@ static void gpio_pca95xx_interrupt_callback(const struct device *dev, /* Cannot read PCA95xx registers from ISR context, queue worker */ k_work_submit(&drv_data->interrupt_worker); } -#endif /* CONFIG_GPIO_PCA95XX_INTERRUPT */ static int gpio_pca95xx_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin, @@ -652,13 +651,6 @@ static int gpio_pca95xx_pin_interrupt_configure(const struct device *dev, enum gpio_int_trig trig) { int ret = 0; - - if (!IS_ENABLED(CONFIG_GPIO_PCA95XX_INTERRUPT) - && (mode != GPIO_INT_MODE_DISABLED)) { - return -ENOTSUP; - } - -#ifdef CONFIG_GPIO_PCA95XX_INTERRUPT const struct gpio_pca95xx_config * const config = dev->config; struct gpio_pca95xx_drv_data * const drv_data = (struct gpio_pca95xx_drv_data * const)dev->data; @@ -742,11 +734,9 @@ static int gpio_pca95xx_pin_interrupt_configure(const struct device *dev, err: k_sem_give(&drv_data->lock); -#endif /* CONFIG_GPIO_PCA95XX_INTERRUPT */ return ret; } -#ifdef CONFIG_GPIO_PCA95XX_INTERRUPT static int gpio_pca95xx_manage_callback(const struct device *dev, struct gpio_callback *callback, bool set) @@ -766,7 +756,7 @@ static int gpio_pca95xx_manage_callback(const struct device *dev, k_sem_give(&drv_data->lock); return 0; } -#endif +#endif /* CONFIG_GPIO_PCA95XX_INTERRUPT */ static const struct gpio_driver_api gpio_pca95xx_drv_api_funcs = { .pin_configure = gpio_pca95xx_config, @@ -775,8 +765,8 @@ static const struct gpio_driver_api gpio_pca95xx_drv_api_funcs = { .port_set_bits_raw = gpio_pca95xx_port_set_bits_raw, .port_clear_bits_raw = gpio_pca95xx_port_clear_bits_raw, .port_toggle_bits = gpio_pca95xx_port_toggle_bits, - .pin_interrupt_configure = gpio_pca95xx_pin_interrupt_configure, #ifdef CONFIG_GPIO_PCA95XX_INTERRUPT + .pin_interrupt_configure = gpio_pca95xx_pin_interrupt_configure, .manage_callback = gpio_pca95xx_manage_callback, #endif }; diff --git a/drivers/gpio/gpio_sc18im704.c b/drivers/gpio/gpio_sc18im704.c index 96fd56cadb6..c45e9d51a60 100644 --- a/drivers/gpio/gpio_sc18im704.c +++ b/drivers/gpio/gpio_sc18im704.c @@ -239,17 +239,6 @@ static int gpio_sc18im_port_toggle_bits(const struct device *port, gpio_port_pin return gpio_sc18im_port_set_raw(port, 0, 0, (uint8_t)pins); } -static int gpio_sc18im_pin_interrupt_configure(const struct device *port, gpio_pin_t pin, - enum gpio_int_mode mode, enum gpio_int_trig trig) -{ - ARG_UNUSED(port); - ARG_UNUSED(pin); - ARG_UNUSED(mode); - ARG_UNUSED(trig); - - return -ENOTSUP; -} - static int gpio_sc18im_init(const struct device *dev) { const struct gpio_sc18im_config *cfg = dev->config; @@ -272,7 +261,6 @@ static const struct gpio_driver_api gpio_sc18im_driver_api = { .port_set_bits_raw = gpio_sc18im_port_set_bits_raw, .port_clear_bits_raw = gpio_sc18im_port_clear_bits_raw, .port_toggle_bits = gpio_sc18im_port_toggle_bits, - .pin_interrupt_configure = gpio_sc18im_pin_interrupt_configure, }; #define CHECK_COMPAT(node) \ diff --git a/drivers/gpio/gpio_sn74hc595.c b/drivers/gpio/gpio_sn74hc595.c index 38f5de94d38..16eb35bb690 100644 --- a/drivers/gpio/gpio_sn74hc595.c +++ b/drivers/gpio/gpio_sn74hc595.c @@ -134,16 +134,6 @@ static int gpio_sn74hc595_port_toggle_bits(const struct device *dev, uint32_t ma return ret; } -static int gpio_sn74hc595_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin, - enum gpio_int_mode mode, enum gpio_int_trig trig) -{ - ARG_UNUSED(dev); - ARG_UNUSED(pin); - ARG_UNUSED(mode); - ARG_UNUSED(trig); - return -ENOTSUP; -} - static const struct gpio_driver_api gpio_sn74hc595_drv_api_funcs = { .pin_configure = gpio_sn74hc595_config, .port_get_raw = gpio_sn74hc595_port_get_raw, @@ -151,7 +141,6 @@ static const struct gpio_driver_api gpio_sn74hc595_drv_api_funcs = { .port_set_bits_raw = gpio_sn74hc595_port_set_bits_raw, .port_clear_bits_raw = gpio_sn74hc595_port_clear_bits_raw, .port_toggle_bits = gpio_sn74hc595_port_toggle_bits, - .pin_interrupt_configure = gpio_sn74hc595_pin_interrupt_configure, }; /** diff --git a/drivers/gpio/gpio_stmpe1600.c b/drivers/gpio/gpio_stmpe1600.c index 518b41c7c57..2331fdf9fed 100644 --- a/drivers/gpio/gpio_stmpe1600.c +++ b/drivers/gpio/gpio_stmpe1600.c @@ -249,14 +249,6 @@ static int stmpe1600_port_toggle_bits(const struct device *dev, uint32_t mask) return ret; } -static int stmpe1600_pin_interrupt_configure(const struct device *dev, - gpio_pin_t pin, - enum gpio_int_mode mode, - enum gpio_int_trig trig) -{ - return -ENOTSUP; -} - static int stmpe1600_init(const struct device *dev) { const struct stmpe1600_config *const config = dev->config; @@ -300,7 +292,6 @@ static const struct gpio_driver_api stmpe1600_drv_api = { .port_set_bits_raw = stmpe1600_port_set_bits_raw, .port_clear_bits_raw = stmpe1600_port_clear_bits_raw, .port_toggle_bits = stmpe1600_port_toggle_bits, - .pin_interrupt_configure = stmpe1600_pin_interrupt_configure, }; #define STMPE1600_INIT(inst) \ diff --git a/drivers/gpio/gpio_sx1509b.c b/drivers/gpio/gpio_sx1509b.c index c62f803e307..b8ecdf4d772 100644 --- a/drivers/gpio/gpio_sx1509b.c +++ b/drivers/gpio/gpio_sx1509b.c @@ -468,6 +468,7 @@ static int port_toggle_bits(const struct device *dev, return port_write(dev, 0, 0, pins); } +#ifdef CONFIG_GPIO_SX1509B_INTERRUPT static int pin_interrupt_configure(const struct device *dev, gpio_pin_t pin, enum gpio_int_mode mode, @@ -475,12 +476,6 @@ static int pin_interrupt_configure(const struct device *dev, { int rc = 0; - if (!IS_ENABLED(CONFIG_GPIO_SX1509B_INTERRUPT) - && (mode != GPIO_INT_MODE_DISABLED)) { - return -ENOTSUP; - } - -#ifdef CONFIG_GPIO_SX1509B_INTERRUPT /* Device does not support level-triggered interrupts. */ if (mode == GPIO_INT_MODE_LEVEL) { return -ENOTSUP; @@ -531,10 +526,10 @@ static int pin_interrupt_configure(const struct device *dev, rc = i2c_write_dt(&cfg->bus, &irq_buf.reg, sizeof(irq_buf)); k_sem_give(&drv_data->lock); -#endif /* CONFIG_GPIO_SX1509B_INTERRUPT */ return rc; } +#endif /* CONFIG_GPIO_SX1509B_INTERRUPT */ /** * @brief Initialization function of SX1509B @@ -647,8 +642,8 @@ static const struct gpio_driver_api api_table = { .port_set_bits_raw = port_set_bits, .port_clear_bits_raw = port_clear_bits, .port_toggle_bits = port_toggle_bits, - .pin_interrupt_configure = pin_interrupt_configure, #ifdef CONFIG_GPIO_SX1509B_INTERRUPT + .pin_interrupt_configure = pin_interrupt_configure, .manage_callback = gpio_sx1509b_manage_callback, #endif }; diff --git a/drivers/gpio/gpio_test.c b/drivers/gpio/gpio_test.c index 192028acac0..3befab37fc2 100644 --- a/drivers/gpio/gpio_test.c +++ b/drivers/gpio/gpio_test.c @@ -63,26 +63,6 @@ static int vnd_gpio_port_toggle_bits(const struct device *port, return -ENOTSUP; } -static int vnd_gpio_pin_interrupt_configure(const struct device *port, - gpio_pin_t pin, - enum gpio_int_mode mode, - enum gpio_int_trig trig) -{ - return -ENOTSUP; -} - -static int vnd_gpio_manage_callback(const struct device *port, - struct gpio_callback *cb, - bool set) -{ - return -ENOTSUP; -} - -static uint32_t vnd_gpio_get_pending_int(const struct device *dev) -{ - return 0; -} - static const struct gpio_driver_api vnd_gpio_api = { .pin_configure = vnd_gpio_pin_configure, .port_get_raw = vnd_gpio_port_get_raw, @@ -90,9 +70,6 @@ static const struct gpio_driver_api vnd_gpio_api = { .port_set_bits_raw = vnd_gpio_port_set_bits_raw, .port_clear_bits_raw = vnd_gpio_port_clear_bits_raw, .port_toggle_bits = vnd_gpio_port_toggle_bits, - .pin_interrupt_configure = vnd_gpio_pin_interrupt_configure, - .manage_callback = vnd_gpio_manage_callback, - .get_pending_int = vnd_gpio_get_pending_int }; #define VND_GPIO_INIT(n) \ diff --git a/drivers/gpio/gpio_xlnx_axi.c b/drivers/gpio/gpio_xlnx_axi.c index 99e9e350aa2..f070e691e5d 100644 --- a/drivers/gpio/gpio_xlnx_axi.c +++ b/drivers/gpio/gpio_xlnx_axi.c @@ -198,6 +198,7 @@ static int gpio_xlnx_axi_port_toggle_bits(const struct device *dev, gpio_port_pi return 0; } +#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(interrupts) /** * Enables interrupts for the given pins on the channel * The axi gpio can only enable interrupts for an entire port, so we need to track @@ -206,7 +207,6 @@ static int gpio_xlnx_axi_port_toggle_bits(const struct device *dev, gpio_port_pi static int gpio_xlnx_axi_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin, enum gpio_int_mode mode, enum gpio_int_trig trig) { -#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(interrupts) const struct gpio_xlnx_axi_config *config = dev->config; struct gpio_xlnx_axi_data *data = dev->data; const uint32_t pin_mask = BIT(pin); @@ -390,9 +390,11 @@ static const struct gpio_driver_api gpio_xlnx_axi_driver_api = { .port_set_bits_raw = gpio_xlnx_axi_port_set_bits_raw, .port_clear_bits_raw = gpio_xlnx_axi_port_clear_bits_raw, .port_toggle_bits = gpio_xlnx_axi_port_toggle_bits, +#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(interrupts) .pin_interrupt_configure = gpio_xlnx_axi_pin_interrupt_configure, .manage_callback = gpio_xlnx_axi_manage_callback, .get_pending_int = gpio_xlnx_axi_get_pending_int, +#endif }; #define GPIO_XLNX_AXI_GPIO2_HAS_COMPAT_STATUS_OKAY(n) \ diff --git a/drivers/gpio/gpio_xmc4xxx.c b/drivers/gpio/gpio_xmc4xxx.c index 29462308481..d90f68d3b32 100644 --- a/drivers/gpio/gpio_xmc4xxx.c +++ b/drivers/gpio/gpio_xmc4xxx.c @@ -117,10 +117,10 @@ static void gpio_xmc4xxx_isr(const struct device *dev, int pin) } #endif +#ifdef CONFIG_XMC4XXX_INTC static int gpio_xmc4xxx_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin, enum gpio_int_mode mode, enum gpio_int_trig trig) { -#if defined(CONFIG_XMC4XXX_INTC) const struct gpio_xmc4xxx_config *config = dev->config; int port_id = PORT_TO_PORT_ID(config->port); @@ -132,15 +132,8 @@ static int gpio_xmc4xxx_pin_interrupt_configure(const struct device *dev, gpio_p } else { return -EINVAL; } -#else - ARG_UNUSED(dev); - ARG_UNUSED(pin); - ARG_UNUSED(mode); - ARG_UNUSED(trig); - - return -ENOTSUP; -#endif } +#endif static int gpio_xmc4xxx_get_raw(const struct device *dev, gpio_port_value_t *value) { @@ -215,8 +208,10 @@ static const struct gpio_driver_api gpio_xmc4xxx_driver_api = { .port_set_bits_raw = gpio_xmc4xxx_set_bits_raw, .port_clear_bits_raw = gpio_xmc4xxx_clear_bits_raw, .port_toggle_bits = gpio_xmc4xxx_toggle_bits, +#ifdef CONFIG_XMC4XXX_INTC .pin_interrupt_configure = gpio_xmc4xxx_pin_interrupt_configure, - .manage_callback = IS_ENABLED(CONFIG_XMC4XXX_INTC) ? gpio_xmc4xxx_manage_callback : NULL, + .manage_callback = gpio_xmc4xxx_manage_callback, +#endif }; #define GPIO_XMC4XXX_INIT(index) \ diff --git a/drivers/ieee802154/ieee802154_nrf5.c b/drivers/ieee802154/ieee802154_nrf5.c index f636da67983..35dbcaf11c8 100644 --- a/drivers/ieee802154/ieee802154_nrf5.c +++ b/drivers/ieee802154/ieee802154_nrf5.c @@ -57,6 +57,9 @@ struct nrf5_802154_config { }; static struct nrf5_802154_data nrf5_data; +#if defined(CONFIG_IEEE802154_RAW_MODE) +static const struct device *nrf5_dev; +#endif #define ACK_REQUEST_BYTE 1 #define ACK_REQUEST_BIT (1 << 5) @@ -98,6 +101,15 @@ static struct nrf5_802154_data nrf5_data; #define IEEE802154_NRF5_VENDOR_OUI (uint32_t)0xF4CE36 #endif +static inline const struct device *nrf5_get_device(void) +{ +#if defined(CONFIG_IEEE802154_RAW_MODE) + return nrf5_dev; +#else + return net_if_get_device(nrf5_data.iface); +#endif +} + static void nrf5_get_eui64(uint8_t *mac) { uint64_t factoryAddress; @@ -736,6 +748,9 @@ static int nrf5_init(const struct device *dev) { const struct nrf5_802154_config *nrf5_radio_cfg = NRF5_802154_CFG(dev); struct nrf5_802154_data *nrf5_radio = NRF5_802154_DATA(dev); +#if defined(CONFIG_IEEE802154_RAW_MODE) + nrf5_dev = dev; +#endif k_fifo_init(&nrf5_radio->rx_fifo); k_sem_init(&nrf5_radio->tx_wait, 0, 1); @@ -776,25 +791,17 @@ static void nrf5_iface_init(struct net_if *iface) #if defined(CONFIG_NRF_802154_ENCRYPTION) static void nrf5_config_mac_keys(struct ieee802154_key *mac_keys) { - static nrf_802154_key_id_t stored_key_ids[NRF_802154_SECURITY_KEY_STORAGE_SIZE]; - static uint8_t stored_ids[NRF_802154_SECURITY_KEY_STORAGE_SIZE]; - uint8_t i; + nrf_802154_security_key_remove_all(); - for (i = 0; i < NRF_802154_SECURITY_KEY_STORAGE_SIZE && stored_key_ids[i].p_key_id; i++) { - nrf_802154_security_key_remove(&stored_key_ids[i]); - stored_key_ids[i].p_key_id = NULL; - } - - i = 0; - for (struct ieee802154_key *keys = mac_keys; keys->key_value - && i < NRF_802154_SECURITY_KEY_STORAGE_SIZE; keys++, i++) { + for (uint8_t i = 0; mac_keys->key_value + && i < NRF_802154_SECURITY_KEY_STORAGE_SIZE; mac_keys++, i++) { nrf_802154_key_t key = { - .value.p_cleartext_key = keys->key_value, - .id.mode = keys->key_id_mode, - .id.p_key_id = &(keys->key_index), + .value.p_cleartext_key = mac_keys->key_value, + .id.mode = mac_keys->key_id_mode, + .id.p_key_id = mac_keys->key_id, .type = NRF_802154_KEY_CLEARTEXT, .frame_counter = 0, - .use_global_frame_counter = !(keys->frame_counter_per_key), + .use_global_frame_counter = !(mac_keys->frame_counter_per_key), }; __ASSERT_EVAL((void)nrf_802154_security_key_store(&key), @@ -802,10 +809,6 @@ static void nrf5_config_mac_keys(struct ieee802154_key *mac_keys) err == NRF_802154_SECURITY_ERROR_NONE || err == NRF_802154_SECURITY_ERROR_ALREADY_PRESENT, "Storing key failed, err: %d", err); - - stored_ids[i] = *key.id.p_key_id; - stored_key_ids[i].mode = key.id.mode; - stored_key_ids[i].p_key_id = &stored_ids[i]; }; } #endif /* CONFIG_NRF_802154_ENCRYPTION */ @@ -1016,7 +1019,7 @@ void nrf_802154_received_timestamp_raw(uint8_t *data, int8_t power, uint8_t lqi, void nrf_802154_receive_failed(nrf_802154_rx_error_t error, uint32_t id) { - const struct device *dev = net_if_get_device(nrf5_data.iface); + const struct device *dev = nrf5_get_device(); #if defined(CONFIG_IEEE802154_CSL_ENDPOINT) if (id == DRX_SLOT_RX) { @@ -1133,7 +1136,7 @@ void nrf_802154_energy_detected(const nrf_802154_energy_detected_t *result) energy_scan_done_cb_t callback = nrf5_data.energy_scan_done; nrf5_data.energy_scan_done = NULL; - callback(net_if_get_device(nrf5_data.iface), result->ed_dbm); + callback(nrf5_get_device(), result->ed_dbm); } } @@ -1143,7 +1146,7 @@ void nrf_802154_energy_detection_failed(nrf_802154_ed_error_t error) energy_scan_done_cb_t callback = nrf5_data.energy_scan_done; nrf5_data.energy_scan_done = NULL; - callback(net_if_get_device(nrf5_data.iface), SHRT_MAX); + callback(nrf5_get_device(), SHRT_MAX); } } diff --git a/drivers/sensor/qdec_nrfx/qdec_nrfx.c b/drivers/sensor/qdec_nrfx/qdec_nrfx.c index 81c3614fc05..b11ce70fb9c 100644 --- a/drivers/sensor/qdec_nrfx/qdec_nrfx.c +++ b/drivers/sensor/qdec_nrfx/qdec_nrfx.c @@ -135,13 +135,6 @@ static void qdec_nrfx_event_handler(nrfx_qdec_event_t event, void *p_context) unsigned int key; switch (event.type) { - case NRF_QDEC_EVENT_SAMPLERDY: - /* The underlying HAL driver may improperly forward an samplerdy event even if it's - * disabled in the configuration. Ignore the event to prevent error logs until the - * issue is fixed in HAL. - */ - break; - case NRF_QDEC_EVENT_REPORTRDY: accumulate(dev_data, event.data.report.acc); diff --git a/drivers/serial/Kconfig.test b/drivers/serial/Kconfig.test index 5e7353ba65a..e7f38edc591 100644 --- a/drivers/serial/Kconfig.test +++ b/drivers/serial/Kconfig.test @@ -8,3 +8,4 @@ config SERIAL_TEST select SERIAL_HAS_DRIVER select SERIAL_SUPPORT_INTERRUPT select SERIAL_SUPPORT_ASYNC + select RING_BUFFER if (UART_INTERRUPT_DRIVEN || UART_ASYNC_API) diff --git a/drivers/serial/serial_test.c b/drivers/serial/serial_test.c index 95e036e5992..b1bb46228be 100644 --- a/drivers/serial/serial_test.c +++ b/drivers/serial/serial_test.c @@ -9,12 +9,18 @@ * devices for the "vnd,serial" devicetree compatible used in test code. */ +#include + +#include #include #include #include #include +#include #include +LOG_MODULE_REGISTER(mock_serial, CONFIG_LOG_DEFAULT_LEVEL); + #define DT_DRV_COMPAT vnd_serial struct serial_vnd_data { #ifdef CONFIG_RING_BUFFER @@ -23,8 +29,156 @@ struct serial_vnd_data { #endif serial_vnd_write_cb_t callback; void *callback_data; +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + uart_irq_callback_user_data_t irq_isr; + bool irq_rx_enabled; + bool irq_tx_enabled; +#endif +#ifdef CONFIG_UART_ASYNC_API + uart_callback_t async_cb; + void *async_cb_user_data; + uint8_t *read_buf; + size_t read_size; + size_t read_position; +#endif }; +#ifdef CONFIG_UART_INTERRUPT_DRIVEN +static bool is_irq_rx_pending(const struct device *dev) +{ + struct serial_vnd_data *data = dev->data; + + return !ring_buf_is_empty(data->read_queue); +} + +static bool is_irq_tx_pending(const struct device *dev) +{ + struct serial_vnd_data *data = dev->data; + + return ring_buf_space_get(data->written) != 0; +} + +static void irq_process(const struct device *dev) +{ + struct serial_vnd_data *data = dev->data; + + for (;;) { + bool rx_rdy = is_irq_rx_pending(dev); + bool tx_rdy = is_irq_tx_pending(dev); + bool rx_int = rx_rdy && data->irq_rx_enabled; + bool tx_int = tx_rdy && data->irq_tx_enabled; + + LOG_DBG("rx_rdy %d tx_rdy %d", rx_rdy, tx_rdy); + LOG_DBG("rx_int %d tx_int %d", rx_int, tx_int); + + if (!(rx_int || tx_int)) { + break; + } + + LOG_DBG("isr"); + if (!data->irq_isr) { + LOG_ERR("no isr registered"); + break; + } + data->irq_isr(dev, NULL); + }; +} + +static void irq_rx_enable(const struct device *dev) +{ + struct serial_vnd_data *data = dev->data; + + data->irq_rx_enabled = true; + LOG_DBG("rx enabled"); + irq_process(dev); +} + +static void irq_rx_disable(const struct device *dev) +{ + struct serial_vnd_data *data = dev->data; + + data->irq_rx_enabled = false; + LOG_DBG("rx disabled"); +} + +static int irq_rx_ready(const struct device *dev) +{ + struct serial_vnd_data *data = dev->data; + bool ready = !ring_buf_is_empty(data->read_queue); + + LOG_DBG("rx ready: %d", ready); + return ready; +} + +static void irq_tx_enable(const struct device *dev) +{ + struct serial_vnd_data *data = dev->data; + + LOG_DBG("tx enabled"); + data->irq_tx_enabled = true; + irq_process(dev); +} + +static void irq_tx_disable(const struct device *dev) +{ + struct serial_vnd_data *data = dev->data; + + data->irq_tx_enabled = false; + LOG_DBG("tx disabled"); +} + +static int irq_tx_ready(const struct device *dev) +{ + struct serial_vnd_data *data = dev->data; + bool ready = (ring_buf_space_get(data->written) != 0); + + LOG_DBG("tx ready: %d", ready); + return ready; +} + +static void irq_callback_set(const struct device *dev, uart_irq_callback_user_data_t cb, + void *user_data) +{ + struct serial_vnd_data *data = dev->data; + + /* Not implemented. Ok because `user_data` is always NULL in the current + * implementation of core UART API. + */ + __ASSERT_NO_MSG(user_data == NULL); + +#if defined(CONFIG_UART_EXCLUSIVE_API_CALLBACKS) && defined(CONFIG_UART_ASYNC_API) + if (data->read_buf) { + LOG_ERR("Setting callback to NULL while asynchronous API is in use."); + } + data->async_cb = NULL; + data->async_cb_user_data = NULL; +#endif + + data->irq_isr = cb; + LOG_DBG("callback set"); +} + +static int fifo_fill(const struct device *dev, const uint8_t *tx_data, int size) +{ + struct serial_vnd_data *data = dev->data; + uint32_t write_len = ring_buf_put(data->written, tx_data, size); + + if (data->callback) { + data->callback(dev, data->callback_data); + } + return write_len; +} + +static int fifo_read(const struct device *dev, uint8_t *rx_data, const int size) +{ + struct serial_vnd_data *data = dev->data; + int read_len = ring_buf_get(data->read_queue, rx_data, size); + + LOG_HEXDUMP_DBG(rx_data, read_len, ""); + return read_len; +} +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + static int serial_vnd_poll_in(const struct device *dev, unsigned char *c) { #ifdef CONFIG_RING_BUFFER @@ -59,15 +213,35 @@ static void serial_vnd_poll_out(const struct device *dev, unsigned char c) } } +#ifdef CONFIG_UART_ASYNC_API +static void async_rx_run(const struct device *dev); +#endif + #ifdef CONFIG_RING_BUFFER -int serial_vnd_queue_in_data(const struct device *dev, unsigned char *c, uint32_t size) +int serial_vnd_queue_in_data(const struct device *dev, const unsigned char *c, uint32_t size) { struct serial_vnd_data *data = dev->data; + int write_size; if (data == NULL || data->read_queue == NULL) { return -ENOTSUP; } - return ring_buf_put(data->read_queue, c, size); + write_size = ring_buf_put(data->read_queue, c, size); + + LOG_DBG("size %u write_size %u", size, write_size); + LOG_HEXDUMP_DBG(c, write_size, ""); + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + if (write_size > 0) { + irq_process(dev); + } +#endif + +#ifdef CONFIG_UART_ASYNC_API + async_rx_run(dev); +#endif + + return write_size; } uint32_t serial_vnd_out_data_size_get(const struct device *dev) @@ -130,6 +304,128 @@ static int serial_vnd_config_get(const struct device *dev, struct uart_config *c } #endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */ +#ifdef CONFIG_UART_ASYNC_API +static int serial_vnd_callback_set(const struct device *dev, uart_callback_t callback, + void *user_data) +{ + struct serial_vnd_data *data = dev->data; + + if (data == NULL) { + return -ENOTSUP; + } + +#if defined(CONFIG_UART_EXCLUSIVE_API_CALLBACKS) && defined(CONFIG_UART_INTERRUPT_DRIVEN) + data->irq_isr = NULL; +#endif + + if (callback == NULL && data->read_buf) { + LOG_ERR("Setting callback to NULL while asynchronous API is in use."); + } + + data->async_cb = callback; + data->async_cb_user_data = user_data; + + return 0; +} + +static int serial_vnd_api_tx(const struct device *dev, const uint8_t *tx_data, size_t len, + int32_t timeout) +{ + struct serial_vnd_data *data = dev->data; + struct uart_event evt; + uint32_t write_len; + + if (data == NULL) { + return -ENOTSUP; + } + + if (data->async_cb == NULL) { + return -EINVAL; + } + + write_len = ring_buf_put(data->written, tx_data, len); + if (data->callback) { + data->callback(dev, data->callback_data); + } + + __ASSERT(write_len == len, "Ring buffer full. Async wait not implemented."); + + evt = (struct uart_event){ + .type = UART_TX_DONE, + .data.tx.buf = tx_data, + .data.tx.len = len, + }; + data->async_cb(dev, &evt, data->async_cb_user_data); + + return 0; +} + +static void async_rx_run(const struct device *dev) +{ + struct serial_vnd_data *data = dev->data; + struct uart_event evt; + uint32_t read_len; + uint32_t read_remaining; + + if (!data->read_buf) { + return; + } + + __ASSERT_NO_MSG(data->async_cb); + + read_remaining = data->read_size - data->read_position; + + read_len = ring_buf_get(data->read_queue, &data->read_buf[data->read_position], + read_remaining); + + if (read_len != 0) { + evt = (struct uart_event){ + .type = UART_RX_RDY, + .data.rx.buf = data->read_buf, + .data.rx.len = read_len, + .data.rx.offset = data->read_position, + }; + data->async_cb(dev, &evt, data->async_cb_user_data); + } + + data->read_position += read_len; + + if (data->read_position == data->read_size) { + data->read_buf = NULL; + evt = (struct uart_event){ + .type = UART_RX_DISABLED, + }; + data->async_cb(dev, &evt, data->async_cb_user_data); + } +} + +static int serial_vnd_rx_enable(const struct device *dev, uint8_t *read_buf, size_t read_size, + int32_t timeout) +{ + struct serial_vnd_data *data = dev->data; + + LOG_WRN("read_size %zd", read_size); + + if (data == NULL) { + return -ENOTSUP; + } + + if (data->async_cb == NULL) { + return -EINVAL; + } + + __ASSERT(timeout == SYS_FOREVER_MS, "Async timeout not implemented."); + + data->read_buf = read_buf; + data->read_size = read_size; + data->read_position = 0; + + async_rx_run(dev); + + return 0; +} +#endif /* CONFIG_UART_ASYNC_API */ + static const struct uart_driver_api serial_vnd_api = { .poll_in = serial_vnd_poll_in, .poll_out = serial_vnd_poll_out, @@ -138,6 +434,22 @@ static const struct uart_driver_api serial_vnd_api = { .configure = serial_vnd_configure, .config_get = serial_vnd_config_get, #endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */ +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + .irq_callback_set = irq_callback_set, + .irq_rx_enable = irq_rx_enable, + .irq_rx_disable = irq_rx_disable, + .irq_rx_ready = irq_rx_ready, + .irq_tx_enable = irq_tx_enable, + .irq_tx_disable = irq_tx_disable, + .irq_tx_ready = irq_tx_ready, + .fifo_read = fifo_read, + .fifo_fill = fifo_fill, +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ +#ifdef CONFIG_UART_ASYNC_API + .callback_set = serial_vnd_callback_set, + .tx = serial_vnd_api_tx, + .rx_enable = serial_vnd_rx_enable, +#endif /* CONFIG_UART_ASYNC_API */ }; #define VND_SERIAL_DATA_BUFFER(n) \ diff --git a/drivers/serial/uart_nrfx_uarte.c b/drivers/serial/uart_nrfx_uarte.c index bdafe6c7d4c..b70af538abf 100644 --- a/drivers/serial/uart_nrfx_uarte.c +++ b/drivers/serial/uart_nrfx_uarte.c @@ -844,15 +844,7 @@ static int uarte_nrfx_rx_enable(const struct device *dev, uint8_t *buf, } data->async->rx_timeout = timeout; - /* Set minimum interval to 3 RTC ticks. 3 is used due to RTC limitation - * which cannot set timeout for next tick. Assuming delay in processing - * 3 instead of 2 is used. Note that lower value would work in a similar - * way but timeouts would always occur later than expected, most likely - * after ~3 ticks. - */ - data->async->rx_timeout_slab = - MAX(timeout / RX_TIMEOUT_DIV, - NRFX_CEIL_DIV(3 * 1000000, CONFIG_SYS_CLOCK_TICKS_PER_SEC)); + data->async->rx_timeout_slab = timeout / RX_TIMEOUT_DIV; data->async->rx_buf = buf; data->async->rx_buf_len = len; @@ -937,11 +929,6 @@ static int uarte_nrfx_callback_set(const struct device *dev, data->async->user_callback = callback; data->async->user_data = user_data; -#if defined(CONFIG_UART_EXCLUSIVE_API_CALLBACKS) && defined(UARTE_INTERRUPT_DRIVEN) - data->int_driven->cb = NULL; - data->int_driven->cb_data = NULL; -#endif - return 0; } @@ -1680,11 +1667,6 @@ static void uarte_nrfx_irq_callback_set(const struct device *dev, data->int_driven->cb = cb; data->int_driven->cb_data = cb_data; - -#if defined(UARTE_ANY_ASYNC) && defined(CONFIG_UART_EXCLUSIVE_API_CALLBACKS) - data->async->user_callback = NULL; - data->async->user_data = NULL; -#endif } #endif /* UARTE_INTERRUPT_DRIVEN */ diff --git a/drivers/spi/spi_nrfx_spis.c b/drivers/spi/spi_nrfx_spis.c index 6ba36a139a0..d2a61530f00 100644 --- a/drivers/spi/spi_nrfx_spis.c +++ b/drivers/spi/spi_nrfx_spis.c @@ -170,6 +170,8 @@ static int transceive(const struct device *dev, { struct spi_nrfx_data *dev_data = dev->data; const struct spi_nrfx_config *dev_config = dev->config; + const struct spi_buf *tx_buf = tx_bufs ? tx_bufs->buffers : NULL; + const struct spi_buf *rx_buf = rx_bufs ? rx_bufs->buffers : NULL; int error; spi_context_lock(&dev_data->ctx, asynchronous, cb, userdata, spi_cfg); @@ -181,8 +183,7 @@ static int transceive(const struct device *dev, (rx_bufs && rx_bufs->count > 1)) { LOG_ERR("Scattered buffers are not supported"); error = -ENOTSUP; - } else if (tx_bufs && tx_bufs->buffers[0].len && - !nrfx_is_in_ram(tx_bufs->buffers[0].buf)) { + } else if (tx_buf && tx_buf->len && !nrfx_is_in_ram(tx_buf->buf)) { LOG_ERR("Only buffers located in RAM are supported"); error = -ENOTSUP; } else { @@ -193,10 +194,10 @@ static int transceive(const struct device *dev, } error = prepare_for_transfer(dev, - tx_bufs ? tx_bufs->buffers[0].buf : NULL, - tx_bufs ? tx_bufs->buffers[0].len : 0, - rx_bufs ? rx_bufs->buffers[0].buf : NULL, - rx_bufs ? rx_bufs->buffers[0].len : 0); + tx_buf ? tx_buf->buf : NULL, + tx_buf ? tx_buf->len : 0, + rx_buf ? rx_buf->buf : NULL, + rx_buf ? rx_buf->len : 0); if (error == 0) { if (dev_config->wake_gpio.port) { /* Set the WAKE line low (tie it to ground) diff --git a/drivers/timer/Kconfig.nrf_rtc b/drivers/timer/Kconfig.nrf_rtc index cda05d4dabe..acb6f123afe 100644 --- a/drivers/timer/Kconfig.nrf_rtc +++ b/drivers/timer/Kconfig.nrf_rtc @@ -19,6 +19,7 @@ if NRF_RTC_TIMER config NRF_RTC_TIMER_USER_CHAN_COUNT int "Additional channels that can be used" + default 2 if NRF_802154_RADIO_DRIVER && SOC_NRF5340_CPUNET default 3 if NRF_802154_RADIO_DRIVER default 0 help diff --git a/drivers/timer/nrf_rtc_timer.c b/drivers/timer/nrf_rtc_timer.c index 4410529608b..9241c5b7a10 100644 --- a/drivers/timer/nrf_rtc_timer.c +++ b/drivers/timer/nrf_rtc_timer.c @@ -17,15 +17,18 @@ #include #include +#define RTC_PRETICK (IS_ENABLED(CONFIG_SOC_NRF53_RTC_PRETICK) && \ + IS_ENABLED(CONFIG_SOC_NRF5340_CPUNET)) + #define EXT_CHAN_COUNT CONFIG_NRF_RTC_TIMER_USER_CHAN_COUNT #define CHAN_COUNT (EXT_CHAN_COUNT + 1) #define RTC NRF_RTC1 #define RTC_IRQn NRFX_IRQ_NUMBER_GET(RTC) #define RTC_LABEL rtc1 -#define RTC_CH_COUNT RTC1_CC_NUM +#define CHAN_COUNT_MAX (RTC1_CC_NUM - (RTC_PRETICK ? 1 : 0)) -BUILD_ASSERT(CHAN_COUNT <= RTC_CH_COUNT, "Not enough compare channels"); +BUILD_ASSERT(CHAN_COUNT <= CHAN_COUNT_MAX, "Not enough compare channels"); /* Ensure that counter driver for RTC1 is not enabled. */ BUILD_ASSERT(DT_NODE_HAS_STATUS(DT_NODELABEL(RTC_LABEL), disabled), "Counter for RTC1 must be disabled"); @@ -44,6 +47,8 @@ BUILD_ASSERT(DT_NODE_HAS_STATUS(DT_NODELABEL(RTC_LABEL), disabled), #define ANCHOR_RANGE_END (7 * COUNTER_SPAN / 8) #define TARGET_TIME_INVALID (UINT64_MAX) +extern void rtc_pretick_rtc1_isr_hook(void); + static volatile uint32_t overflow_cnt; static volatile uint64_t anchor; static uint64_t last_count; @@ -559,6 +564,10 @@ void rtc_nrf_isr(const void *arg) { ARG_UNUSED(arg); + if (RTC_PRETICK) { + rtc_pretick_rtc1_isr_hook(); + } + if (nrf_rtc_int_enable_check(RTC, NRF_RTC_INT_OVERFLOW_MASK) && nrf_rtc_event_check(RTC, NRF_RTC_EVENT_OVERFLOW)) { nrf_rtc_event_clear(RTC, NRF_RTC_EVENT_OVERFLOW); diff --git a/dts/arm/nordic/nrf52820.dtsi b/dts/arm/nordic/nrf52820.dtsi index 6d2f5eb02fe..c3d05fc6ec1 100644 --- a/dts/arm/nordic/nrf52820.dtsi +++ b/dts/arm/nordic/nrf52820.dtsi @@ -88,9 +88,15 @@ interrupts = <1 NRF_DEFAULT_IRQ_PRIORITY>; status = "okay"; dfe-supported; + ieee802154-supported; ble-2mbps-supported; ble-coded-phy-supported; tx-high-power-supported; + + ieee802154: ieee802154 { + compatible = "nordic,nrf-ieee802154"; + status = "disabled"; + }; }; uart0: uart@40002000 { diff --git a/dts/arm/nordic/nrf52840.dtsi b/dts/arm/nordic/nrf52840.dtsi index 8c89968c108..f4209c67d19 100644 --- a/dts/arm/nordic/nrf52840.dtsi +++ b/dts/arm/nordic/nrf52840.dtsi @@ -5,7 +5,7 @@ / { chosen { - zephyr,entropy = &rng; + zephyr,entropy = &cryptocell; zephyr,flash-controller = &flash_controller; }; diff --git a/dts/arm/nordic/nrf5340_cpuapp.dtsi b/dts/arm/nordic/nrf5340_cpuapp.dtsi index 7a32c5398db..1294203f00a 100644 --- a/dts/arm/nordic/nrf5340_cpuapp.dtsi +++ b/dts/arm/nordic/nrf5340_cpuapp.dtsi @@ -33,7 +33,7 @@ }; chosen { - zephyr,entropy = &rng_hci; + zephyr,entropy = &cryptocell; zephyr,flash-controller = &flash_controller; }; diff --git a/dts/arm/nordic/nrf91.dtsi b/dts/arm/nordic/nrf91.dtsi index d872680fb20..a2a490ab0b1 100644 --- a/dts/arm/nordic/nrf91.dtsi +++ b/dts/arm/nordic/nrf91.dtsi @@ -28,6 +28,7 @@ }; chosen { + zephyr,entropy = &cryptocell; zephyr,flash-controller = &flash_controller; }; diff --git a/dts/bindings/arm/nordic,nrf-uicr.yaml b/dts/bindings/arm/nordic,nrf-uicr.yaml index 82ac8fc2089..b39a6ed6855 100644 --- a/dts/bindings/arm/nordic,nrf-uicr.yaml +++ b/dts/bindings/arm/nordic,nrf-uicr.yaml @@ -7,3 +7,26 @@ include: base.yaml properties: reg: required: true + + nfct-pins-as-gpios: + type: boolean + description: | + When enabled this property will configure pins dedicated to NFCT + peripheral as regular GPIOs. + + NFC pins in nRF52 series: P0.09 and P0.10 + NFC pins in nRF5340: P0.02 and P0.03 + + This setting, once applied, can only be unset by erasing the UICR + registers. Refer to the reference manual for more details. + + gpio-as-nreset: + type: boolean + description: | + When enabled, this property will configure the reset GPIO as nRESET. + + nRESET pin in nRF52805/52810/52811/52832 series: P0.21 + nRESET pin in nRF52820/52833/52840 series: P0.18 + + This setting, once applied, can only be unset by erasing the UICR + registers. Refer to the reference manual for more details. diff --git a/dts/bindings/test/vnd,serial.yaml b/dts/bindings/test/vnd,serial.yaml index dc2327b4827..c2d931b0ed7 100644 --- a/dts/bindings/test/vnd,serial.yaml +++ b/dts/bindings/test/vnd,serial.yaml @@ -5,9 +5,6 @@ compatible: "vnd,serial" include: uart-controller.yaml properties: - reg: - required: true - baud-rate: type: int diff --git a/include/zephyr/arch/arm/aarch32/cortex_m/scripts/linker.ld b/include/zephyr/arch/arm/aarch32/cortex_m/scripts/linker.ld index 92b6f02d029..85d55b2de29 100644 --- a/include/zephyr/arch/arm/aarch32/cortex_m/scripts/linker.ld +++ b/include/zephyr/arch/arm/aarch32/cortex_m/scripts/linker.ld @@ -26,6 +26,35 @@ #endif #define RAMABLE_REGION RAM +#if USE_PARTITION_MANAGER + +#include + +#if CONFIG_NCS_IS_VARIANT_IMAGE && defined(PM_S0_ID) +/* We are linking against S1, create symbol containing the flash ID of S0. + * This is used when writing code operating on the "other" slot. + */ +_image_1_primary_slot_id = PM_S0_ID; + +#else /* ! CONFIG_NCS_IS_VARIANT_IMAGE */ + +#ifdef PM_S1_ID +/* We are linking against S0, create symbol containing the flash ID of S1. + * This is used when writing code operating on the "other" slot. + */ +_image_1_primary_slot_id = PM_S1_ID; +#endif /* PM_S1_ID */ + +#endif /* CONFIG_NCS_IS_VARIANT_IMAGE */ + +#define ROM_ADDR PM_ADDRESS +#define ROM_SIZE PM_SIZE + +#define RAM_SIZE PM_SRAM_SIZE +#define RAM_ADDR PM_SRAM_ADDRESS + +#else /* ! USE_PARTITION_MANAGER */ + #if !defined(CONFIG_XIP) && (CONFIG_FLASH_SIZE == 0) #define ROM_ADDR RAM_ADDR #else @@ -52,6 +81,23 @@ #define RAM_ADDR CONFIG_SRAM_BASE_ADDRESS #endif +#endif /* USE_PARTITION_MANAGER */ + +#if DT_NODE_HAS_STATUS(DT_CHOSEN(zephyr_ccm), okay) +#define CCM_SIZE DT_REG_SIZE(DT_CHOSEN(zephyr_ccm)) +#define CCM_ADDR DT_REG_ADDR(DT_CHOSEN(zephyr_ccm)) +#endif + +#if DT_NODE_HAS_STATUS(DT_CHOSEN(zephyr_itcm), okay) +#define ITCM_SIZE DT_REG_SIZE(DT_CHOSEN(zephyr_itcm)) +#define ITCM_ADDR DT_REG_ADDR(DT_CHOSEN(zephyr_itcm)) +#endif + +#if DT_NODE_HAS_STATUS(DT_CHOSEN(zephyr_dtcm), okay) +#define DTCM_SIZE DT_REG_SIZE(DT_CHOSEN(zephyr_dtcm)) +#define DTCM_ADDR DT_REG_ADDR(DT_CHOSEN(zephyr_dtcm)) +#endif + #if defined(CONFIG_CUSTOM_SECTION_ALIGN) _region_min_align = CONFIG_CUSTOM_SECTION_MIN_ALIGN_SIZE; #else diff --git a/include/zephyr/arch/arm/aarch32/misc.h b/include/zephyr/arch/arm/aarch32/misc.h index 24ed69f663c..ab67a35e94c 100644 --- a/include/zephyr/arch/arm/aarch32/misc.h +++ b/include/zephyr/arch/arm/aarch32/misc.h @@ -51,6 +51,15 @@ extern bool z_arm_thread_is_in_user_mode(void); bool z_arm_on_enter_cpu_idle(void); #endif +#if defined(CONFIG_ARM_ON_ENTER_CPU_IDLE_PREPARE_HOOK) +/* Prototype of a hook that can be enabled to be called every time the CPU is + * made idle (the calls will be done from k_cpu_idle() and k_cpu_atomic_idle()). + * The function is called before interrupts are disabled and can prepare to + * upcoming call to z_arm_on_enter_cpu_idle. + */ +void z_arm_on_enter_cpu_idle_prepare(void); +#endif + #endif #ifdef __cplusplus diff --git a/include/zephyr/bluetooth/conn.h b/include/zephyr/bluetooth/conn.h index 9fd78b6a89b..f8a15bea9b8 100644 --- a/include/zephyr/bluetooth/conn.h +++ b/include/zephyr/bluetooth/conn.h @@ -479,6 +479,42 @@ struct bt_conn_le_tx_power { int8_t max_level; }; + +/** LE Transmit Power Reporting Structure */ +struct bt_conn_le_tx_power_report { + + /** Reason for Transmit power reporting, + * as documented in Core Spec. Version 5.4 Vol. 4, Part E, 7.7.65.33. + */ + uint8_t reason; + + /** Phy of Transmit power reporting. */ + enum bt_conn_le_tx_power_phy phy; + + /** Transmit power level + * - 0xXX - Transmit power level + * + Range: -127 to 20 + * + Units: dBm + * + * - 0x7E - Remote device is not managing power levels on this PHY. + * - 0x7F - Transmit power level is not available + */ + int8_t tx_power_level; + + /** Bit 0: Transmit power level is at minimum level. + * Bit 1: Transmit power level is at maximum level. + */ + uint8_t tx_power_level_flag; + + /** Change in transmit power level + * - 0xXX - Change in transmit power level (positive indicates increased + * power, negative indicates decreased power, zero indicates unchanged) + * Units: dB + * - 0x7F - Change is not available or is out of range. + */ + int8_t delta; +}; + /** @brief Passkey Keypress Notification type * * The numeric values are the same as in the Core specification for Pairing @@ -530,6 +566,41 @@ int bt_conn_get_remote_info(struct bt_conn *conn, int bt_conn_le_get_tx_power_level(struct bt_conn *conn, struct bt_conn_le_tx_power *tx_power_level); +/** @brief Get local enhanced connection transmit power level. + * + * @param conn Connection object. + * @param tx_power Transmit power level descriptor. + * + * @return Zero on success or (negative) error code on failure. + * @retval -ENOBUFS HCI command buffer is not available. + */ +int bt_conn_le_enhanced_get_tx_power_level(struct bt_conn *conn, + struct bt_conn_le_tx_power *tx_power); + +/** @brief Get remote (peer) transmit power level. + * + * @param conn Connection object. + * @param phy PHY information. + * + * @return Zero on success or (negative) error code on failure. + * @retval -ENOBUFS HCI command buffer is not available. + */ +int bt_conn_le_get_remote_tx_power_level(struct bt_conn *conn, + enum bt_conn_le_tx_power_phy phy); + +/** @brief Enable transmit power reporting. + * + * @param conn Connection object. + * @param local_enable Enable/disable reporting for local. + * @param remote_enable Enable/disable reporting for remote. + * + * @return Zero on success or (negative) error code on failure. + * @retval -ENOBUFS HCI command buffer is not available. + */ +int bt_conn_le_set_tx_power_report_enable(struct bt_conn *conn, + bool local_enable, + bool remote_enable); + /** @brief Update the connection parameters. * * If the local device is in the peripheral role then updating the connection @@ -1049,6 +1120,22 @@ struct bt_conn_cb { const struct bt_df_conn_iq_samples_report *iq_report); #endif /* CONFIG_BT_DF_CONNECTION_CTE_RX */ +#if defined(CONFIG_BT_TRANSMIT_POWER_CONTROL) + /** @brief LE Read Remote Transmit Power Level procedure has completed or LE + * Transmit Power Reporting event. + * + * This callback notifies the application that either the remote transmit power level + * has been read from the peer or transmit power level has changed for the local or + * remote controller when transmit power reporting is enabled for the respective side + * using @ref bt_conn_le_set_tx_power_report_enable. + * + * @param conn Connection object. + * @param report Transmit power report. + */ + void (*tx_power_report)(struct bt_conn *conn, + const struct bt_conn_le_tx_power_report *report); +#endif /* CONFIG_BT_TRANSMIT_POWER_CONTROL */ + struct bt_conn_cb *_next; }; diff --git a/include/zephyr/bluetooth/gatt.h b/include/zephyr/bluetooth/gatt.h index e4aae4e4d41..f0ee8d5981a 100644 --- a/include/zephyr/bluetooth/gatt.h +++ b/include/zephyr/bluetooth/gatt.h @@ -736,11 +736,6 @@ struct bt_gatt_ccc_cfg { uint8_t id; /** Remote peer address. */ bt_addr_le_t peer; - /** - * Separate storage for encrypted and unencrypted context. This - * indicate that the link was encrypted when the CCC was written. - */ - bool link_encrypted; /** Configuration value. */ uint16_t value; }; diff --git a/include/zephyr/bluetooth/hci.h b/include/zephyr/bluetooth/hci.h index 8dd7434e445..258f86519b6 100644 --- a/include/zephyr/bluetooth/hci.h +++ b/include/zephyr/bluetooth/hci.h @@ -97,6 +97,19 @@ int bt_hci_get_conn_handle(const struct bt_conn *conn, uint16_t *conn_handle); */ int bt_hci_get_adv_handle(const struct bt_le_ext_adv *adv, uint8_t *adv_handle); +/** @brief Obtain the version string given a core version number. + * + * The core version of a controller can be obtained by issuing + * the HCI Read Local Version Information command. + * + * See also the defines prefixed with BT_HCI_VERSION_. + * + * @param core_version The core version. + * + * @return Version string corresponding to the core version number. + */ +const char *bt_hci_get_ver_str(uint8_t core_version); + /** @typedef bt_hci_vnd_evt_cb_t * @brief Callback type for vendor handling of HCI Vendor-Specific Events. * diff --git a/include/zephyr/bluetooth/hci_types.h b/include/zephyr/bluetooth/hci_types.h index d9dff357d35..dc1566a0df4 100644 --- a/include/zephyr/bluetooth/hci_types.h +++ b/include/zephyr/bluetooth/hci_types.h @@ -571,6 +571,35 @@ struct bt_hci_rp_read_tx_power_level { int8_t tx_power_level; } __packed; +#define BT_HCI_LE_TX_POWER_PHY_1M 0x01 +#define BT_HCI_LE_TX_POWER_PHY_2M 0x02 +#define BT_HCI_LE_TX_POWER_PHY_CODED_S8 0x03 +#define BT_HCI_LE_TX_POWER_PHY_CODED_S2 0x04 +#define BT_HCI_OP_LE_ENH_READ_TX_POWER_LEVEL BT_OP(BT_OGF_LE, 0x0076) +struct bt_hci_cp_le_read_tx_power_level { + uint16_t handle; + uint8_t phy; +} __packed; + +struct bt_hci_rp_le_read_tx_power_level { + uint8_t status; + uint16_t handle; + uint8_t phy; + int8_t current_tx_power_level; + int8_t max_tx_power_level; +} __packed; + +#define BT_HCI_OP_LE_READ_REMOTE_TX_POWER_LEVEL BT_OP(BT_OGF_LE, 0x0077) + +#define BT_HCI_LE_TX_POWER_REPORT_DISABLE 0x00 +#define BT_HCI_LE_TX_POWER_REPORT_ENABLE 0x01 +#define BT_HCI_OP_LE_SET_TX_POWER_REPORT_ENABLE BT_OP(BT_OGF_LE, 0x007A) +struct bt_hci_cp_le_set_tx_power_report_enable { + uint16_t handle; + uint8_t local_enable; + uint8_t remote_enable; +} __packed; + #define BT_HCI_CTL_TO_HOST_FLOW_DISABLE 0x00 #define BT_HCI_CTL_TO_HOST_FLOW_ENABLE 0x01 #define BT_HCI_OP_SET_CTL_TO_HOST_FLOW BT_OP(BT_OGF_BASEBAND, 0x0031) @@ -2903,6 +2932,26 @@ struct bt_hci_evt_le_req_peer_sca_complete { uint8_t sca; } __packed; +/** Reason for Transmit power reporting. + */ +/* Local Transmit power changed. */ +#define BT_HCI_LE_TX_POWER_REPORT_REASON_LOCAL_CHANGED 0x00 +/* Remote Transmit power changed. */ +#define BT_HCI_LE_TX_POWER_REPORT_REASON_REMOTE_CHANGED 0x01 +/* HCI_LE_Read_Remote_Transmit_Power_Level command completed. */ +#define BT_HCI_LE_TX_POWER_REPORT_REASON_READ_REMOTE_COMPLETED 0x02 + +#define BT_HCI_EVT_LE_TRANSMIT_POWER_REPORT 0x21 +struct bt_hci_evt_le_transmit_power_report { + uint8_t status; + uint16_t handle; + uint8_t reason; + uint8_t phy; + int8_t tx_power_level; + uint8_t tx_power_level_flag; + int8_t delta; +} __packed; + #define BT_HCI_EVT_LE_BIGINFO_ADV_REPORT 0x22 struct bt_hci_evt_le_biginfo_adv_report { uint16_t sync_handle; diff --git a/include/zephyr/bluetooth/mesh/access.h b/include/zephyr/bluetooth/mesh/access.h index f3b6c373128..5b1653c2487 100644 --- a/include/zephyr/bluetooth/mesh/access.h +++ b/include/zephyr/bluetooth/mesh/access.h @@ -1137,6 +1137,51 @@ struct bt_mesh_comp { struct bt_mesh_elem *elem; /**< List of elements. */ }; +/** Composition data page 2 record. */ +struct bt_mesh_comp2_record { + /** Mesh profile ID. */ + uint16_t id; + /** Mesh Profile Version. */ + struct { + /** Major version. */ + uint8_t x; + /** Minor version. */ + uint8_t y; + /** Z version. */ + uint8_t z; + } version; + /** Element offset count. */ + uint8_t elem_offset_cnt; + /** Element offset list. */ + const uint8_t *elem_offset; + /** Length of additional data. */ + uint16_t data_len; + /** Additional data. */ + const void *data; +}; + +/** Node Composition data page 2 */ +struct bt_mesh_comp2 { + /** The number of Mesh Profile records on a device. */ + size_t record_cnt; + /** List of records. */ + const struct bt_mesh_comp2_record *record; +}; + +/** @brief Register composition data page 2 of the device. + * + * Register Mesh Profiles information (Ref section 3.12 in + * Bluetooth SIG Assigned Numbers) for composition data + * page 2 of the device. + * + * @note There must be at least one record present in @c comp2 + * + * @param comp2 Pointer to composition data page 2. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_mesh_comp2_register(const struct bt_mesh_comp2 *comp2); + #ifdef __cplusplus } #endif diff --git a/include/zephyr/bluetooth/mesh/cfg_cli.h b/include/zephyr/bluetooth/mesh/cfg_cli.h index e6fd31d7c3a..d2221da38da 100644 --- a/include/zephyr/bluetooth/mesh/cfg_cli.h +++ b/include/zephyr/bluetooth/mesh/cfg_cli.h @@ -1786,6 +1786,40 @@ struct bt_mesh_comp_p1_model_item *bt_mesh_comp_p1_item_pull( struct bt_mesh_comp_p1_ext_item *bt_mesh_comp_p1_pull_ext_item( struct bt_mesh_comp_p1_model_item *item, struct bt_mesh_comp_p1_ext_item *ext_item); +/** Composition data page 2 record parsing structure. */ +struct bt_mesh_comp_p2_record { + /** Mesh profile ID. */ + uint16_t id; + /** Mesh Profile Version. */ + struct { + /** Major version. */ + uint8_t x; + /** Minor version. */ + uint8_t y; + /** Z version. */ + uint8_t z; + } version; + /** Element offset buffer. */ + struct net_buf_simple *elem_buf; + /** Additional data buffer. */ + struct net_buf_simple *data_buf; +}; + +/** @brief Pull a Composition Data Page 2 Record from a composition data page 2 + * instance. + * + * Each call to this function will pull out a new element from the composition + * data page, until all elements have been pulled. + * + * @param buf Composition data page 2 buffer + * @param record Record to fill. + * + * @return A pointer to @c record on success, or NULL if no more elements could + * be pulled. + */ +struct bt_mesh_comp_p2_record *bt_mesh_comp_p2_record_pull(struct net_buf_simple *buf, + struct bt_mesh_comp_p2_record *record); + /** @brief Unpack a list of key index entries from a buffer. * * On success, @c dst_cnt is set to the amount of unpacked key index entries. diff --git a/include/zephyr/bluetooth/mesh/dfd_srv.h b/include/zephyr/bluetooth/mesh/dfd_srv.h index 0e281b29186..666e0d8ad3d 100644 --- a/include/zephyr/bluetooth/mesh/dfd_srv.h +++ b/include/zephyr/bluetooth/mesh/dfd_srv.h @@ -29,11 +29,47 @@ extern "C" { #define CONFIG_BT_MESH_DFD_SRV_TARGETS_MAX 0 #endif +#ifndef CONFIG_BT_MESH_DFD_SRV_SLOT_MAX_SIZE +#define CONFIG_BT_MESH_DFD_SRV_SLOT_MAX_SIZE 0 +#endif + +#ifndef CONFIG_BT_MESH_DFD_SRV_SLOT_SPACE +#define CONFIG_BT_MESH_DFD_SRV_SLOT_SPACE 0 +#endif + struct bt_mesh_dfd_srv; +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD +/** + * + * @brief Initialization parameters for the @ref bt_mesh_dfd_srv with OOB + * upload support. + * + * @param[in] _cb Pointer to a @ref bt_mesh_dfd_srv_cb instance. + * @param[in] _oob_schemes Array of OOB schemes supported by the server, + * each scheme being a code point from the + * Bluetooth SIG Assigned Numbers document. + * @param[in] _oob_schemes_count Number of schemes in @c _oob_schemes. + */ +#define BT_MESH_DFD_SRV_OOB_INIT(_cb, _oob_schemes, _oob_schemes_count) \ + { \ + .cb = _cb, \ + .dfu = BT_MESH_DFU_CLI_INIT(&_bt_mesh_dfd_srv_dfu_cb), \ + .upload = { \ + .blob = { .cb = &_bt_mesh_dfd_srv_blob_cb }, \ + }, \ + .oob_schemes = { \ + .schemes = _oob_schemes, \ + .count = _oob_schemes_count, \ + }, \ + } +#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */ + /** * * @brief Initialization parameters for the @ref bt_mesh_dfd_srv. + * + * @param[in] _cb Pointer to a @ref bt_mesh_dfd_srv_cb instance. */ #define BT_MESH_DFD_SRV_INIT(_cb) \ { \ @@ -75,6 +111,64 @@ struct bt_mesh_dfd_srv_cb { const struct bt_mesh_dfu_slot *slot, const struct bt_mesh_blob_io **io); +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD + /** @brief Firmware upload OOB start callback. + * + * Called at the start of an OOB firmware upload. The application must + * start a firmware check using an OOB mechanism, and then call + * @ref bt_mesh_dfd_srv_oob_check_complete. Depending on the return + * value of this function, the application must then start storing the + * firmware image using an OOB mechanism, and call + * @ref bt_mesh_dfd_srv_oob_store_complete. This callback is mandatory + * to support OOB uploads. + * + * @param srv Firmware Distribution Server model instance. + * @param slot Slot to be used for the upload. + * @param uri Pointer to buffer containing the URI used to + * check for new firmware. + * @param uri_len Length of the URI buffer. + * @param fwid Pointer to buffer containing the current + * firmware ID to be used when checking for + * availability of new firmware. + * @param fwid_len Length of the current firmware ID. Must be set + * to the length of the new firmware ID if it is + * available, or to 0 if new firmware is not + * available. + * + * @return BT_MESH_DFD_SUCCESS on success, or error code otherwise. + */ + int (*start_oob_upload)(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot, + const char *uri, uint8_t uri_len, + const uint8_t *fwid, uint16_t fwid_len); + + /** @brief Cancel store OOB callback + * + * Called when an OOB store is cancelled. The application must stop + * any ongoing OOB image transfer. This callback is mandatory to + * support OOB uploads. + * + * @param srv Firmware Distribution Server model instance. + * @param slot DFU image slot to cancel + */ + void (*cancel_oob_upload)(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot); + + /** @brief Get the progress of an ongoing OOB store + * + * Called by the Firmware Distribution Server model when it needs to + * get the current progress of an ongoing OOB store from the + * application. This callback is mandatory to support OOB uploads. + * + * @param srv Firmware Distribution Server model instance. + * @param slot DFU image slot to get progress for. + * + * @return The current progress of the ongoing OOB store, in percent. + */ + uint8_t (*oob_progress_get)(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot); +#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */ + /** @brief Slot delete callback. * * Called when the Firmware Distribution Server is about to delete a DFU image slot. @@ -129,12 +223,80 @@ struct bt_mesh_dfd_srv { struct { enum bt_mesh_dfd_upload_phase phase; - const struct bt_mesh_dfu_slot *slot; + struct bt_mesh_dfu_slot *slot; const struct flash_area *area; struct bt_mesh_blob_srv blob; +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD + bool is_oob; + bool is_pending_oob_check; + struct { + uint8_t uri_len; + uint8_t uri[CONFIG_BT_MESH_DFU_URI_MAXLEN]; + uint16_t current_fwid_len; + uint8_t current_fwid[CONFIG_BT_MESH_DFU_FWID_MAXLEN]; + struct bt_mesh_msg_ctx ctx; + } oob; +#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */ } upload; + +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD + struct { + const uint8_t *schemes; + const uint8_t count; + } oob_schemes; +#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */ }; +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD +/** @brief Call when an OOB check has completed or failed + * + * This should be called by the application after an OOB check started by the @c start_oob_upload + * callback has completed or failed. The @p status param should be set to one of the following + * values: + * + * * @c BT_MESH_DFD_SUCCESS if the check was succesfull and a new firmware ID was found. + * * @c BT_MESH_DFD_ERR_URI_MALFORMED if the URI is not formatted correctly. + * * @c BT_MESH_DFD_ERR_URI_NOT_SUPPORTED if the URI scheme is not supported by the node. + * * @c BT_MESH_DFD_ERR_URI_UNREACHABLE if the URI can't be reached. + * * @c BT_MESH_DFD_ERR_NEW_FW_NOT_AVAILABLE if the check completes successfully but no new + * firmware is available. + * + * If this function returns 0, the application should then download the firmware to the + * slot. If an error code is returned, the application should abort the OOB upload. + * + * @param srv Firmware Distribution Server model instance. + * @param slot The slot used in the OOB upload. + * @param status The result of the firmware check. + * @param fwid If the check was successful and new firmware found, this should point to a + * buffer containing the new firmware ID to store. + * @param fwid_len The length of the firmware ID pointed to by @p fwid. + * + * @return 0 on success, (negative) error code otherwise. + */ +int bt_mesh_dfd_srv_oob_check_complete(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot, int status, + uint8_t *fwid, size_t fwid_len); + +/** @brief Call when an OOB store has completed or failed + * + * This should be called by the application after an OOB store started after a succesfull call to + * @c bt_mesh_dfd_srv_oob_check_complete has completed successfully or failed. + * + * @param srv Firmware Distribution Server model instance. + * @param slot The slot used when storing the firmware image. + * @param success @c true if the OOB store completed successfully, @c false otherwise. + * @param size The size of the stored firmware image, in bytes. + * @param metadata Pointer to the metadata received OOB, or @c NULL if no metadata was + * received. + * @param metadata_len Size of the metadata pointed to by @p metadata. + * + * @return 0 on success, (negative) error code otherwise. + */ +int bt_mesh_dfd_srv_oob_store_complete(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot, bool success, + size_t size, const uint8_t *metadata, size_t metadata_len); +#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */ + /** @cond INTERNAL_HIDDEN */ extern const struct bt_mesh_model_op _bt_mesh_dfd_srv_op[]; extern const struct bt_mesh_model_cb _bt_mesh_dfd_srv_cb; diff --git a/include/zephyr/bluetooth/mesh/dfu.h b/include/zephyr/bluetooth/mesh/dfu.h index 91f36cdbaeb..4745355f039 100644 --- a/include/zephyr/bluetooth/mesh/dfu.h +++ b/include/zephyr/bluetooth/mesh/dfu.h @@ -34,6 +34,10 @@ extern "C" { #define CONFIG_BT_MESH_DFU_URI_MAXLEN 0 #endif +#ifndef CONFIG_BT_MESH_DFU_SLOT_CNT +#define CONFIG_BT_MESH_DFU_SLOT_CNT 0 +#endif + /** DFU transfer phase. */ enum bt_mesh_dfu_phase { /** Ready to start a Receive Firmware procedure. */ @@ -140,10 +144,7 @@ struct bt_mesh_dfu_img { /** Length of the firmware ID. */ size_t fwid_len; - /** Update URI, or NULL. - * - * Must use one of the http: or https: schemes. - */ + /** Update URI, or NULL. */ const char *uri; }; @@ -155,14 +156,10 @@ struct bt_mesh_dfu_slot { size_t fwid_len; /** Length of the metadata. */ size_t metadata_len; - /** Length of the image URI. */ - size_t uri_len; /** Firmware ID. */ uint8_t fwid[CONFIG_BT_MESH_DFU_FWID_MAXLEN]; /** Metadata. */ uint8_t metadata[CONFIG_BT_MESH_DFU_METADATA_MAXLEN]; - /** Image URI. */ - char uri[CONFIG_BT_MESH_DFU_URI_MAXLEN]; }; /** @} */ diff --git a/include/zephyr/bluetooth/mesh/od_priv_proxy_cli.h b/include/zephyr/bluetooth/mesh/od_priv_proxy_cli.h index 89e63c177a0..f9734d78d3e 100644 --- a/include/zephyr/bluetooth/mesh/od_priv_proxy_cli.h +++ b/include/zephyr/bluetooth/mesh/od_priv_proxy_cli.h @@ -47,7 +47,6 @@ struct bt_mesh_od_priv_proxy_cli { _bt_mesh_od_priv_proxy_cli_op, NULL, cli_data, \ &_bt_mesh_od_priv_proxy_cli_cb) - /** @brief Get the target's On-Demand Private GATT Proxy state. * * This method can be used asynchronously by setting @p val_rsp as NULL. @@ -57,12 +56,13 @@ struct bt_mesh_od_priv_proxy_cli { * To process the response arguments of an async method, register * the @c od_status callback in @c bt_mesh_od_priv_proxy_cli struct. * - * @param ctx Message context for the message. + * @param net_idx Network index to encrypt with. + * @param addr Target node address. * @param val_rsp Response buffer for On-Demand Private GATT Proxy value. * * @return 0 on success, or (negative) error code otherwise. */ -int bt_mesh_od_priv_proxy_cli_get(struct bt_mesh_msg_ctx *ctx, uint8_t *val_rsp); +int bt_mesh_od_priv_proxy_cli_get(uint16_t net_idx, uint16_t addr, uint8_t *val_rsp); /** @brief Set the target's On-Demand Private GATT Proxy state. * @@ -73,13 +73,14 @@ int bt_mesh_od_priv_proxy_cli_get(struct bt_mesh_msg_ctx *ctx, uint8_t *val_rsp) * To process the response arguments of an async method, register * the @c od_status callback in @c bt_mesh_od_priv_proxy_cli struct. * - * @param ctx Message context for the message. + * @param net_idx Network index to encrypt with. + * @param addr Target node address. * @param val On-Demand Private GATT Proxy state to be set * @param val_rsp Response buffer for On-Demand Private GATT Proxy value. * * @return 0 on success, or (negative) error code otherwise. */ -int bt_mesh_od_priv_proxy_cli_set(struct bt_mesh_msg_ctx *ctx, uint8_t val, uint8_t *val_rsp); +int bt_mesh_od_priv_proxy_cli_set(uint16_t net_idx, uint16_t addr, uint8_t val, uint8_t *val_rsp); /** @brief Set the transmission timeout value. * diff --git a/include/zephyr/bluetooth/uuid.h b/include/zephyr/bluetooth/uuid.h index 1fa8ece6466..cab12eac74b 100644 --- a/include/zephyr/bluetooth/uuid.h +++ b/include/zephyr/bluetooth/uuid.h @@ -587,7 +587,7 @@ struct bt_uuid_128 { /** * @brief Proxy Solicitation UUID value */ -#define BT_UUID_MESH_PROXY_SOLICITATION_VAL 0x7fcb +#define BT_UUID_MESH_PROXY_SOLICITATION_VAL 0x1859 /** * @brief Reconnection Configuration Service UUID value */ diff --git a/include/zephyr/drivers/uart/serial_test.h b/include/zephyr/drivers/uart/serial_test.h index 5e011d40321..ff1a2ad6181 100644 --- a/include/zephyr/drivers/uart/serial_test.h +++ b/include/zephyr/drivers/uart/serial_test.h @@ -29,7 +29,7 @@ extern "C" { * * @retval Number of bytes written. */ -int serial_vnd_queue_in_data(const struct device *dev, unsigned char *data, uint32_t size); +int serial_vnd_queue_in_data(const struct device *dev, const unsigned char *data, uint32_t size); /** * @brief Returns size of unread written data. diff --git a/include/zephyr/linker/common-ram.ld b/include/zephyr/linker/common-ram.ld index bfaa26ed400..8ae67c73257 100644 --- a/include/zephyr/linker/common-ram.ld +++ b/include/zephyr/linker/common-ram.ld @@ -12,10 +12,6 @@ #endif #endif /* NETWORKING */ -#if defined(CONFIG_BT_MESH) - ITERABLE_SECTION_RAM(bt_mesh_ext_adv, 4) -#endif - #if defined(CONFIG_GEN_SW_ISR_TABLE) && defined(CONFIG_DYNAMIC_INTERRUPTS) SECTION_DATA_PROLOGUE(sw_isr_table,,) { diff --git a/include/zephyr/logging/log_msg.h b/include/zephyr/logging/log_msg.h index 4eee566bdc5..0279fb0d2cd 100644 --- a/include/zephyr/logging/log_msg.h +++ b/include/zephyr/logging/log_msg.h @@ -669,6 +669,7 @@ static inline void *log_msg_get_tid(struct log_msg *msg) #if CONFIG_LOG_THREAD_ID_PREFIX return msg->hdr.tid; #else + ARG_UNUSED(msg); return NULL; #endif } diff --git a/include/zephyr/mgmt/mcumgr/grp/img_mgmt/img_mgmt_client.h b/include/zephyr/mgmt/mcumgr/grp/img_mgmt/img_mgmt_client.h index 6ee12fb5a61..8cede40d64e 100644 --- a/include/zephyr/mgmt/mcumgr/grp/img_mgmt/img_mgmt_client.h +++ b/include/zephyr/mgmt/mcumgr/grp/img_mgmt/img_mgmt_client.h @@ -40,7 +40,7 @@ struct mcumgr_image_data { /** Image number */ uint32_t img_num; /** Image SHA256 checksum */ - char hash[IMG_MGMT_HASH_LEN]; + char hash[IMG_MGMT_DATA_SHA_LEN]; /** Image Version */ char version[IMG_MGMT_VER_MAX_STR_LEN + 1]; /** Image Flags */ @@ -76,7 +76,7 @@ struct mcumgr_image_upload { */ struct img_gr_upload { /** Image 256-bit hash */ - char sha256[IMG_MGMT_HASH_LEN]; + char sha256[IMG_MGMT_DATA_SHA_LEN]; /** True when Hash is configured, false when not */ bool hash_initialized; /** Image size */ diff --git a/include/zephyr/mgmt/mcumgr/grp/os_mgmt/os_mgmt.h b/include/zephyr/mgmt/mcumgr/grp/os_mgmt/os_mgmt.h index 9c541e11c29..e1ac45e6389 100644 --- a/include/zephyr/mgmt/mcumgr/grp/os_mgmt/os_mgmt.h +++ b/include/zephyr/mgmt/mcumgr/grp/os_mgmt/os_mgmt.h @@ -24,6 +24,7 @@ extern "C" { #define OS_MGMT_ID_RESET 5 #define OS_MGMT_ID_MCUMGR_PARAMS 6 #define OS_MGMT_ID_INFO 7 +#define OS_MGMT_ID_BOOTLOADER_INFO 8 /** * Command result codes for OS management group. @@ -37,6 +38,9 @@ enum os_mgmt_err_code_t { /** The provided format value is not valid. */ OS_MGMT_ERR_INVALID_FORMAT, + + /** Query was not recognized. */ + OS_MGMT_ERR_QUERY_YIELDS_NO_ANSWER, }; /* Bitmask values used by the os info command handler. Note that the width of this variable is diff --git a/include/zephyr/mgmt/mcumgr/mgmt/mgmt.h b/include/zephyr/mgmt/mcumgr/mgmt/mgmt.h index eb9ecfcf2a9..e681ea2b939 100644 --- a/include/zephyr/mgmt/mcumgr/mgmt/mgmt.h +++ b/include/zephyr/mgmt/mcumgr/mgmt/mgmt.h @@ -11,6 +11,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -23,124 +24,6 @@ extern "C" { * @{ */ -/** - * Used at end of MCUmgr handlers to return an error if the message size limit was reached, - * or OK if it was not - */ -#define MGMT_RETURN_CHECK(ok) ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE - -/** Opcodes; encoded in first byte of header. */ -enum mcumgr_op_t { - /** Read op-code */ - MGMT_OP_READ = 0, - - /** Read response op-code */ - MGMT_OP_READ_RSP, - - /** Write op-code */ - MGMT_OP_WRITE, - - /** Write response op-code */ - MGMT_OP_WRITE_RSP, -}; - -/** - * MCUmgr groups. The first 64 groups are reserved for system level mcumgr - * commands. Per-user commands are then defined after group 64. - */ -enum mcumgr_group_t { - /** OS (operating system) group */ - MGMT_GROUP_ID_OS = 0, - - /** Image management group, used for uploading firmware images */ - MGMT_GROUP_ID_IMAGE, - - /** Statistic management group, used for retieving statistics */ - MGMT_GROUP_ID_STAT, - - /** Settings management (config) group, used for reading/writing settings */ - MGMT_GROUP_ID_SETTINGS, - - /** Log management group (unused) */ - MGMT_GROUP_ID_LOG, - - /** Crash group (unused) */ - MGMT_GROUP_ID_CRASH, - - /** Split image management group (unused) */ - MGMT_GROUP_ID_SPLIT, - - /** Run group (unused) */ - MGMT_GROUP_ID_RUN, - - /** FS (file system) group, used for performing file IO operations */ - MGMT_GROUP_ID_FS, - - /** Shell management group, used for executing shell commands */ - MGMT_GROUP_ID_SHELL, - - /** User groups defined from 64 onwards */ - MGMT_GROUP_ID_PERUSER = 64, - - /** Zephyr-specific groups decrease from PERUSER to avoid collision with upstream and - * user-defined groups. - * Zephyr-specific: Basic group - */ - ZEPHYR_MGMT_GRP_BASIC = (MGMT_GROUP_ID_PERUSER - 1), -}; - -/** - * MCUmgr error codes. - */ -enum mcumgr_err_t { - /** No error (success). */ - MGMT_ERR_EOK = 0, - - /** Unknown error. */ - MGMT_ERR_EUNKNOWN, - - /** Insufficient memory (likely not enough space for CBOR object). */ - MGMT_ERR_ENOMEM, - - /** Error in input value. */ - MGMT_ERR_EINVAL, - - /** Operation timed out. */ - MGMT_ERR_ETIMEOUT, - - /** No such file/entry. */ - MGMT_ERR_ENOENT, - - /** Current state disallows command. */ - MGMT_ERR_EBADSTATE, - - /** Response too large. */ - MGMT_ERR_EMSGSIZE, - - /** Command not supported. */ - MGMT_ERR_ENOTSUP, - - /** Corrupt */ - MGMT_ERR_ECORRUPT, - - /** Command blocked by processing of other command */ - MGMT_ERR_EBUSY, - - /** Access to specific function, command or resource denied */ - MGMT_ERR_EACCESSDENIED, - - /** Requested SMP MCUmgr protocol version is not supported (too old) */ - MGMT_ERR_UNSUPPORTED_TOO_OLD, - - /** Requested SMP MCUmgr protocol version is not supported (too new) */ - MGMT_ERR_UNSUPPORTED_TOO_NEW, - - /** User errors defined from 256 onwards */ - MGMT_ERR_EPERUSER = 256 -}; - -#define MGMT_HDR_SIZE 8 - /** @typedef mgmt_alloc_rsp_fn * @brief Allocates a buffer suitable for holding a response. * diff --git a/include/zephyr/mgmt/mcumgr/mgmt/mgmt_defines.h b/include/zephyr/mgmt/mcumgr/mgmt/mgmt_defines.h new file mode 100644 index 00000000000..2f33b6d22a1 --- /dev/null +++ b/include/zephyr/mgmt/mcumgr/mgmt/mgmt_defines.h @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2018-2021 mcumgr authors + * Copyright (c) 2022-2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef H_MGMT_MGMT_DEFINES_ +#define H_MGMT_MGMT_DEFINES_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief MCUmgr mgmt API + * @defgroup mcumgr_mgmt_api MCUmgr mgmt API + * @ingroup mcumgr + * @{ + */ + +/** + * Used at end of MCUmgr handlers to return an error if the message size limit was reached, + * or OK if it was not + */ +#define MGMT_RETURN_CHECK(ok) ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE + +/** Opcodes; encoded in first byte of header. */ +enum mcumgr_op_t { + /** Read op-code */ + MGMT_OP_READ = 0, + + /** Read response op-code */ + MGMT_OP_READ_RSP, + + /** Write op-code */ + MGMT_OP_WRITE, + + /** Write response op-code */ + MGMT_OP_WRITE_RSP, +}; + +/** + * MCUmgr groups. The first 64 groups are reserved for system level mcumgr + * commands. Per-user commands are then defined after group 64. + */ +enum mcumgr_group_t { + /** OS (operating system) group */ + MGMT_GROUP_ID_OS = 0, + + /** Image management group, used for uploading firmware images */ + MGMT_GROUP_ID_IMAGE, + + /** Statistic management group, used for retieving statistics */ + MGMT_GROUP_ID_STAT, + + /** Settings management (config) group, used for reading/writing settings */ + MGMT_GROUP_ID_SETTINGS, + + /** Log management group (unused) */ + MGMT_GROUP_ID_LOG, + + /** Crash group (unused) */ + MGMT_GROUP_ID_CRASH, + + /** Split image management group (unused) */ + MGMT_GROUP_ID_SPLIT, + + /** Run group (unused) */ + MGMT_GROUP_ID_RUN, + + /** FS (file system) group, used for performing file IO operations */ + MGMT_GROUP_ID_FS, + + /** Shell management group, used for executing shell commands */ + MGMT_GROUP_ID_SHELL, + + /** User groups defined from 64 onwards */ + MGMT_GROUP_ID_PERUSER = 64, + + /** Zephyr-specific groups decrease from PERUSER to avoid collision with upstream and + * user-defined groups. + * Zephyr-specific: Basic group + */ + ZEPHYR_MGMT_GRP_BASIC = (MGMT_GROUP_ID_PERUSER - 1), +}; + +/** + * MCUmgr error codes. + */ +enum mcumgr_err_t { + /** No error (success). */ + MGMT_ERR_EOK = 0, + + /** Unknown error. */ + MGMT_ERR_EUNKNOWN, + + /** Insufficient memory (likely not enough space for CBOR object). */ + MGMT_ERR_ENOMEM, + + /** Error in input value. */ + MGMT_ERR_EINVAL, + + /** Operation timed out. */ + MGMT_ERR_ETIMEOUT, + + /** No such file/entry. */ + MGMT_ERR_ENOENT, + + /** Current state disallows command. */ + MGMT_ERR_EBADSTATE, + + /** Response too large. */ + MGMT_ERR_EMSGSIZE, + + /** Command not supported. */ + MGMT_ERR_ENOTSUP, + + /** Corrupt */ + MGMT_ERR_ECORRUPT, + + /** Command blocked by processing of other command */ + MGMT_ERR_EBUSY, + + /** Access to specific function, command or resource denied */ + MGMT_ERR_EACCESSDENIED, + + /** Requested SMP MCUmgr protocol version is not supported (too old) */ + MGMT_ERR_UNSUPPORTED_TOO_OLD, + + /** Requested SMP MCUmgr protocol version is not supported (too new) */ + MGMT_ERR_UNSUPPORTED_TOO_NEW, + + /** User errors defined from 256 onwards */ + MGMT_ERR_EPERUSER = 256 +}; + +#define MGMT_HDR_SIZE 8 + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* MGMT_MGMT_DEFINES_H_ */ diff --git a/include/zephyr/net/coap.h b/include/zephyr/net/coap.h index 95bb0b45c65..6fc21a6280b 100644 --- a/include/zephyr/net/coap.h +++ b/include/zephyr/net/coap.h @@ -42,25 +42,25 @@ extern "C" { * Refer to RFC 7252, section 12.2 for more information. */ enum coap_option_num { - COAP_OPTION_IF_MATCH = 1, - COAP_OPTION_URI_HOST = 3, - COAP_OPTION_ETAG = 4, - COAP_OPTION_IF_NONE_MATCH = 5, - COAP_OPTION_OBSERVE = 6, - COAP_OPTION_URI_PORT = 7, - COAP_OPTION_LOCATION_PATH = 8, - COAP_OPTION_URI_PATH = 11, - COAP_OPTION_CONTENT_FORMAT = 12, - COAP_OPTION_MAX_AGE = 14, - COAP_OPTION_URI_QUERY = 15, - COAP_OPTION_ACCEPT = 17, - COAP_OPTION_LOCATION_QUERY = 20, - COAP_OPTION_BLOCK2 = 23, - COAP_OPTION_BLOCK1 = 27, - COAP_OPTION_SIZE2 = 28, - COAP_OPTION_PROXY_URI = 35, - COAP_OPTION_PROXY_SCHEME = 39, - COAP_OPTION_SIZE1 = 60, + COAP_OPTION_IF_MATCH = 1, /**< If-Match */ + COAP_OPTION_URI_HOST = 3, /**< Uri-Host */ + COAP_OPTION_ETAG = 4, /**< ETag */ + COAP_OPTION_IF_NONE_MATCH = 5, /**< If-None-Match */ + COAP_OPTION_OBSERVE = 6, /**< Observe (RFC 7641) */ + COAP_OPTION_URI_PORT = 7, /**< Uri-Port */ + COAP_OPTION_LOCATION_PATH = 8, /**< Location-Path */ + COAP_OPTION_URI_PATH = 11, /**< Uri-Path */ + COAP_OPTION_CONTENT_FORMAT = 12, /**< Content-Format */ + COAP_OPTION_MAX_AGE = 14, /**< Max-Age */ + COAP_OPTION_URI_QUERY = 15, /**< Uri-Query */ + COAP_OPTION_ACCEPT = 17, /**< Accept */ + COAP_OPTION_LOCATION_QUERY = 20, /**< Location-Query */ + COAP_OPTION_BLOCK2 = 23, /**< Block2 (RFC 7959) */ + COAP_OPTION_BLOCK1 = 27, /**< Block1 (RFC 7959) */ + COAP_OPTION_SIZE2 = 28, /**< Size2 (RFC 7959) */ + COAP_OPTION_PROXY_URI = 35, /**< Proxy-Uri */ + COAP_OPTION_PROXY_SCHEME = 39, /**< Proxy-Scheme */ + COAP_OPTION_SIZE1 = 60 /**< Size1 */ }; /** @@ -69,13 +69,13 @@ enum coap_option_num { * To be used when creating a request or a response. */ enum coap_method { - COAP_METHOD_GET = 1, - COAP_METHOD_POST = 2, - COAP_METHOD_PUT = 3, - COAP_METHOD_DELETE = 4, - COAP_METHOD_FETCH = 5, - COAP_METHOD_PATCH = 6, - COAP_METHOD_IPATCH = 7, + COAP_METHOD_GET = 1, /**< GET */ + COAP_METHOD_POST = 2, /**< POST */ + COAP_METHOD_PUT = 3, /**< PUT */ + COAP_METHOD_DELETE = 4, /**< DELETE */ + COAP_METHOD_FETCH = 5, /**< FETCH */ + COAP_METHOD_PATCH = 6, /**< PATCH */ + COAP_METHOD_IPATCH = 7, /**< IPATCH */ }; #define COAP_REQUEST_MASK 0x07 @@ -115,7 +115,13 @@ enum coap_msgtype { COAP_TYPE_RESET = 3 }; -#define coap_make_response_code(class, det) ((class << 5) | (det)) +/** + * Utility macro to create a CoAP response code. + * @param class Class of the response code (ex. 2, 4, 5, ...) + * @param det Detail of the response code + * @return Response code literal + */ +#define COAP_MAKE_RESPONSE_CODE(class, det) ((class << 5) | (det)) /** * @brief Set of response codes available for a response packet. @@ -123,35 +129,62 @@ enum coap_msgtype { * To be used when creating a response. */ enum coap_response_code { - COAP_RESPONSE_CODE_OK = coap_make_response_code(2, 0), - COAP_RESPONSE_CODE_CREATED = coap_make_response_code(2, 1), - COAP_RESPONSE_CODE_DELETED = coap_make_response_code(2, 2), - COAP_RESPONSE_CODE_VALID = coap_make_response_code(2, 3), - COAP_RESPONSE_CODE_CHANGED = coap_make_response_code(2, 4), - COAP_RESPONSE_CODE_CONTENT = coap_make_response_code(2, 5), - COAP_RESPONSE_CODE_CONTINUE = coap_make_response_code(2, 31), - COAP_RESPONSE_CODE_BAD_REQUEST = coap_make_response_code(4, 0), - COAP_RESPONSE_CODE_UNAUTHORIZED = coap_make_response_code(4, 1), - COAP_RESPONSE_CODE_BAD_OPTION = coap_make_response_code(4, 2), - COAP_RESPONSE_CODE_FORBIDDEN = coap_make_response_code(4, 3), - COAP_RESPONSE_CODE_NOT_FOUND = coap_make_response_code(4, 4), - COAP_RESPONSE_CODE_NOT_ALLOWED = coap_make_response_code(4, 5), - COAP_RESPONSE_CODE_NOT_ACCEPTABLE = coap_make_response_code(4, 6), - COAP_RESPONSE_CODE_INCOMPLETE = coap_make_response_code(4, 8), - COAP_RESPONSE_CODE_CONFLICT = coap_make_response_code(4, 9), - COAP_RESPONSE_CODE_PRECONDITION_FAILED = coap_make_response_code(4, 12), - COAP_RESPONSE_CODE_REQUEST_TOO_LARGE = coap_make_response_code(4, 13), + /** 2.00 - OK */ + COAP_RESPONSE_CODE_OK = COAP_MAKE_RESPONSE_CODE(2, 0), + /** 2.01 - Created */ + COAP_RESPONSE_CODE_CREATED = COAP_MAKE_RESPONSE_CODE(2, 1), + /** 2.02 - Deleted */ + COAP_RESPONSE_CODE_DELETED = COAP_MAKE_RESPONSE_CODE(2, 2), + /** 2.03 - Valid */ + COAP_RESPONSE_CODE_VALID = COAP_MAKE_RESPONSE_CODE(2, 3), + /** 2.04 - Changed */ + COAP_RESPONSE_CODE_CHANGED = COAP_MAKE_RESPONSE_CODE(2, 4), + /** 2.05 - Content */ + COAP_RESPONSE_CODE_CONTENT = COAP_MAKE_RESPONSE_CODE(2, 5), + /** 2.31 - Continue */ + COAP_RESPONSE_CODE_CONTINUE = COAP_MAKE_RESPONSE_CODE(2, 31), + /** 4.00 - Bad Request */ + COAP_RESPONSE_CODE_BAD_REQUEST = COAP_MAKE_RESPONSE_CODE(4, 0), + /** 4.01 - Unauthorized */ + COAP_RESPONSE_CODE_UNAUTHORIZED = COAP_MAKE_RESPONSE_CODE(4, 1), + /** 4.02 - Bad Option */ + COAP_RESPONSE_CODE_BAD_OPTION = COAP_MAKE_RESPONSE_CODE(4, 2), + /** 4.03 - Forbidden */ + COAP_RESPONSE_CODE_FORBIDDEN = COAP_MAKE_RESPONSE_CODE(4, 3), + /** 4.04 - Not Found */ + COAP_RESPONSE_CODE_NOT_FOUND = COAP_MAKE_RESPONSE_CODE(4, 4), + /** 4.05 - Method Not Allowed */ + COAP_RESPONSE_CODE_NOT_ALLOWED = COAP_MAKE_RESPONSE_CODE(4, 5), + /** 4.06 - Not Acceptable */ + COAP_RESPONSE_CODE_NOT_ACCEPTABLE = COAP_MAKE_RESPONSE_CODE(4, 6), + /** 4.08 - Request Entity Incomplete */ + COAP_RESPONSE_CODE_INCOMPLETE = COAP_MAKE_RESPONSE_CODE(4, 8), + /** 4.12 - Precondition Failed */ + COAP_RESPONSE_CODE_CONFLICT = COAP_MAKE_RESPONSE_CODE(4, 9), + /** 4.12 - Precondition Failed */ + COAP_RESPONSE_CODE_PRECONDITION_FAILED = COAP_MAKE_RESPONSE_CODE(4, 12), + /** 4.13 - Request Entity Too Large */ + COAP_RESPONSE_CODE_REQUEST_TOO_LARGE = COAP_MAKE_RESPONSE_CODE(4, 13), + /** 4.15 - Unsupported Content-Format */ COAP_RESPONSE_CODE_UNSUPPORTED_CONTENT_FORMAT = - coap_make_response_code(4, 15), - COAP_RESPONSE_CODE_UNPROCESSABLE_ENTITY = coap_make_response_code(4, 22), - COAP_RESPONSE_CODE_TOO_MANY_REQUESTS = coap_make_response_code(4, 29), - COAP_RESPONSE_CODE_INTERNAL_ERROR = coap_make_response_code(5, 0), - COAP_RESPONSE_CODE_NOT_IMPLEMENTED = coap_make_response_code(5, 1), - COAP_RESPONSE_CODE_BAD_GATEWAY = coap_make_response_code(5, 2), - COAP_RESPONSE_CODE_SERVICE_UNAVAILABLE = coap_make_response_code(5, 3), - COAP_RESPONSE_CODE_GATEWAY_TIMEOUT = coap_make_response_code(5, 4), + COAP_MAKE_RESPONSE_CODE(4, 15), + /** 4.22 - Unprocessable Entity */ + COAP_RESPONSE_CODE_UNPROCESSABLE_ENTITY = COAP_MAKE_RESPONSE_CODE(4, 22), + /** 4.29 - Too Many Requests */ + COAP_RESPONSE_CODE_TOO_MANY_REQUESTS = COAP_MAKE_RESPONSE_CODE(4, 29), + /** 5.00 - Internal Server Error */ + COAP_RESPONSE_CODE_INTERNAL_ERROR = COAP_MAKE_RESPONSE_CODE(5, 0), + /** 5.01 - Not Implemented */ + COAP_RESPONSE_CODE_NOT_IMPLEMENTED = COAP_MAKE_RESPONSE_CODE(5, 1), + /** 5.02 - Bad Gateway */ + COAP_RESPONSE_CODE_BAD_GATEWAY = COAP_MAKE_RESPONSE_CODE(5, 2), + /** 5.03 - Service Unavailable */ + COAP_RESPONSE_CODE_SERVICE_UNAVAILABLE = COAP_MAKE_RESPONSE_CODE(5, 3), + /** 5.04 - Gateway Timeout */ + COAP_RESPONSE_CODE_GATEWAY_TIMEOUT = COAP_MAKE_RESPONSE_CODE(5, 4), + /** 5.05 - Proxying Not Supported */ COAP_RESPONSE_CODE_PROXYING_NOT_SUPPORTED = - coap_make_response_code(5, 5) + COAP_MAKE_RESPONSE_CODE(5, 5) }; #define COAP_CODE_EMPTY (0) @@ -164,15 +197,15 @@ enum coap_response_code { * To be used when encoding or decoding a Content-Format option. */ enum coap_content_format { - COAP_CONTENT_FORMAT_TEXT_PLAIN = 0, /* charset=urf-8 */ - COAP_CONTENT_FORMAT_APP_LINK_FORMAT = 40, - COAP_CONTENT_FORMAT_APP_XML = 41, - COAP_CONTENT_FORMAT_APP_OCTET_STREAM = 42, - COAP_CONTENT_FORMAT_APP_EXI = 47, - COAP_CONTENT_FORMAT_APP_JSON = 50, - COAP_CONTENT_FORMAT_APP_JSON_PATCH_JSON = 51, - COAP_CONTENT_FORMAT_APP_MERGE_PATCH_JSON = 52, - COAP_CONTENT_FORMAT_APP_CBOR = 60, + COAP_CONTENT_FORMAT_TEXT_PLAIN = 0, /**< text/plain;charset=utf-8 */ + COAP_CONTENT_FORMAT_APP_LINK_FORMAT = 40, /**< application/link-format */ + COAP_CONTENT_FORMAT_APP_XML = 41, /**< application/xml */ + COAP_CONTENT_FORMAT_APP_OCTET_STREAM = 42, /**< application/octet-stream */ + COAP_CONTENT_FORMAT_APP_EXI = 47, /**< application/exi */ + COAP_CONTENT_FORMAT_APP_JSON = 50, /**< application/json */ + COAP_CONTENT_FORMAT_APP_JSON_PATCH_JSON = 51, /**< application/json-patch+json */ + COAP_CONTENT_FORMAT_APP_MERGE_PATCH_JSON = 52, /**< application/merge-patch+json */ + COAP_CONTENT_FORMAT_APP_CBOR = 60 /**< application/cbor */ }; /* block option helper */ @@ -233,25 +266,32 @@ struct coap_observer { * @brief Representation of a CoAP Packet. */ struct coap_packet { - uint8_t *data; /* User allocated buffer */ - uint16_t offset; /* CoAP lib maintains offset while adding data */ - uint16_t max_len; /* Max CoAP packet data length */ - uint8_t hdr_len; /* CoAP header length */ - uint16_t opt_len; /* Total options length (delta + len + value) */ - uint16_t delta; /* Used for delta calculation in CoAP packet */ -#if defined(CONFIG_COAP_KEEP_USER_DATA) - void *user_data; /* Application specific user data */ + uint8_t *data; /**< User allocated buffer */ + uint16_t offset; /**< CoAP lib maintains offset while adding data */ + uint16_t max_len; /**< Max CoAP packet data length */ + uint8_t hdr_len; /**< CoAP header length */ + uint16_t opt_len; /**< Total options length (delta + len + value) */ + uint16_t delta; /**< Used for delta calculation in CoAP packet */ +#if defined(CONFIG_COAP_KEEP_USER_DATA) || defined(DOXGEN) + /** + * Application specific user data. + * Only available when @kconfig{CONFIG_COAP_KEEP_USER_DATA} is enabled. + */ + void *user_data; #endif }; +/** + * @brief Representation of a CoAP option. + */ struct coap_option { - uint16_t delta; + uint16_t delta; /**< Option delta */ #if defined(CONFIG_COAP_EXTENDED_OPTIONS_LEN) uint16_t len; uint8_t value[CONFIG_COAP_EXTENDED_OPTIONS_LEN_VALUE]; #else - uint8_t len; - uint8_t value[12]; + uint8_t len; /**< Option length */ + uint8_t value[12]; /**< Option value */ #endif }; @@ -271,13 +311,13 @@ typedef int (*coap_reply_t)(const struct coap_packet *response, * @brief Represents a request awaiting for an acknowledgment (ACK). */ struct coap_pending { - struct sockaddr addr; - int64_t t0; - uint32_t timeout; - uint16_t id; - uint8_t *data; - uint16_t len; - uint8_t retries; + struct sockaddr addr; /**< Remote address */ + int64_t t0; /**< Time when the request was sent */ + uint32_t timeout; /**< Timeout in ms */ + uint16_t id; /**< Message id */ + uint8_t *data; /**< User allocated buffer */ + uint16_t len; /**< Length of the CoAP packet */ + uint8_t retries; /**< Number of times the request has been sent */ }; /** @@ -557,13 +597,13 @@ int coap_handle_request(struct coap_packet *cpkt, * https://tools.ietf.org/html/rfc7959 */ enum coap_block_size { - COAP_BLOCK_16, - COAP_BLOCK_32, - COAP_BLOCK_64, - COAP_BLOCK_128, - COAP_BLOCK_256, - COAP_BLOCK_512, - COAP_BLOCK_1024, + COAP_BLOCK_16, /**< 16-byte block size */ + COAP_BLOCK_32, /**< 32-byte block size */ + COAP_BLOCK_64, /**< 64-byte block size */ + COAP_BLOCK_128, /**< 128-byte block size */ + COAP_BLOCK_256, /**< 256-byte block size */ + COAP_BLOCK_512, /**< 512-byte block size */ + COAP_BLOCK_1024, /**< 1024-byte block size */ }; /** @@ -713,6 +753,20 @@ int coap_get_option_int(const struct coap_packet *cpkt, uint16_t code); */ int coap_get_block1_option(const struct coap_packet *cpkt, bool *has_more, uint8_t *block_number); +/** + * @brief Get values from CoAP block2 option. + * + * Decode block number and block size from option. Ignore the has_more flag + * as it should always be zero on queries. + * + * @param cpkt Packet to be inspected + * @param block_number Is set to the number of the block + * + * @return Integer value of the block size in case of success + * or negative in case of error. + */ +int coap_get_block2_option(const struct coap_packet *cpkt, uint8_t *block_number); + /** * @brief Retrieves BLOCK{1,2} and SIZE{1,2} from @a cpkt and updates * @a ctx accordingly. diff --git a/include/zephyr/net/dhcpv6.h b/include/zephyr/net/dhcpv6.h new file mode 100644 index 00000000000..95b59f530b5 --- /dev/null +++ b/include/zephyr/net/dhcpv6.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** @file + * @brief DHCPv6 client + */ + +#ifndef ZEPHYR_INCLUDE_NET_DHCPV6_H_ +#define ZEPHYR_INCLUDE_NET_DHCPV6_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief DHCPv6 + * @defgroup dhcpv6 DHCPv6 + * @ingroup networking + * @{ + */ + +/** @cond INTERNAL_HIDDEN */ + +/** Current state of DHCPv6 client address/prefix negotiation. */ +enum net_dhcpv6_state { + NET_DHCPV6_DISABLED, + NET_DHCPV6_INIT, + NET_DHCPV6_SOLICITING, + NET_DHCPV6_REQUESTING, + NET_DHCPV6_CONFIRMING, + NET_DHCPV6_RENEWING, + NET_DHCPV6_REBINDING, + NET_DHCPV6_INFO_REQUESTING, + NET_DHCPV6_BOUND, +} __packed; + +#define DHCPV6_TID_SIZE 3 +#define DHCPV6_DUID_MAX_SIZE 20 + +struct net_dhcpv6_duid_raw { + uint16_t type; + uint8_t buf[DHCPV6_DUID_MAX_SIZE]; +} __packed; + +struct net_dhcpv6_duid_storage { + struct net_dhcpv6_duid_raw duid; + uint8_t length; +}; + +struct net_if; + +/** @endcond */ + +/** @brief DHCPv6 client configuration parameters. */ +struct net_dhcpv6_params { + bool request_addr : 1; /**< Request IPv6 address. */ + bool request_prefix : 1; /**< Request IPv6 prefix. */ +}; + +/** + * @brief Start DHCPv6 client on an iface + * + * @details Start DHCPv6 client on a given interface. DHCPv6 client will start + * negotiation for IPv6 address and/or prefix, depending on the configuration. + * Once the negotiation is complete, IPv6 address/prefix details will be added + * to the interface. + * + * @param iface A valid pointer to a network interface + * @param params DHCPv6 client configuration parameters. + */ +void net_dhcpv6_start(struct net_if *iface, struct net_dhcpv6_params *params); + +/** + * @brief Stop DHCPv6 client on an iface + * + * @details Stop DHCPv6 client on a given interface. DHCPv6 client + * will remove all configuration obtained from a DHCP server from the + * interface and stop any further negotiation with the server. + * + * @param iface A valid pointer to a network interface + */ +void net_dhcpv6_stop(struct net_if *iface); + +/** + * @brief Restart DHCPv6 client on an iface + * + * @details Restart DHCPv6 client on a given interface. DHCPv6 client + * will restart the state machine without any of the initial delays. + * + * @param iface A valid pointer to a network interface + */ +void net_dhcpv6_restart(struct net_if *iface); + +/** @cond INTERNAL_HIDDEN */ + +/** + * @brief DHCPv6 state name + * + * @internal + */ +const char *net_dhcpv6_state_name(enum net_dhcpv6_state state); + +/** @endcond */ + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_NET_DHCPV6_H_ */ diff --git a/include/zephyr/net/ieee802154_pkt.h b/include/zephyr/net/ieee802154_pkt.h index 23921467d96..a1dca472448 100644 --- a/include/zephyr/net/ieee802154_pkt.h +++ b/include/zephyr/net/ieee802154_pkt.h @@ -92,7 +92,7 @@ static inline void *net_pkt_cb(struct net_pkt *pkt); static inline struct net_pkt_cb_ieee802154 *net_pkt_cb_ieee802154(struct net_pkt *pkt) { - return net_pkt_cb(pkt); + return (struct net_pkt_cb_ieee802154 *)net_pkt_cb(pkt); }; static inline uint8_t net_pkt_ieee802154_lqi(struct net_pkt *pkt) diff --git a/include/zephyr/net/ieee802154_radio.h b/include/zephyr/net/ieee802154_radio.h index ae175ac8e85..861523cbf6a 100644 --- a/include/zephyr/net/ieee802154_radio.h +++ b/include/zephyr/net/ieee802154_radio.h @@ -224,11 +224,16 @@ struct ieee802154_filter { }; struct ieee802154_key { + /** Key material */ uint8_t *key_value; + /** Initial value of frame counter associated with the key, see section 9.4.3 */ uint32_t key_frame_counter; + /** Indicates if per-key frame counter should be used, see section 9.4.3 */ bool frame_counter_per_key; + /** Key Identifier Mode, see section 9.4.2.3, Table 9-7 */ uint8_t key_id_mode; - uint8_t key_index; + /** Key Identifier, see section 9.4.4 */ + uint8_t *key_id; }; /** IEEE802.15.4 Transmission mode. */ diff --git a/include/zephyr/net/lwm2m.h b/include/zephyr/net/lwm2m.h index e94297d3550..6262f6d103d 100644 --- a/include/zephyr/net/lwm2m.h +++ b/include/zephyr/net/lwm2m.h @@ -2073,6 +2073,7 @@ enum lwm2m_rd_client_event { LWM2M_RD_CLIENT_EVENT_ENGINE_SUSPENDED, LWM2M_RD_CLIENT_EVENT_NETWORK_ERROR, LWM2M_RD_CLIENT_EVENT_REG_UPDATE, + LWM2M_RD_CLIENT_EVENT_DEREGISTER, }; /** diff --git a/include/zephyr/net/mqtt.h b/include/zephyr/net/mqtt.h index 1980b95bbea..ecdefea120b 100644 --- a/include/zephyr/net/mqtt.h +++ b/include/zephyr/net/mqtt.h @@ -356,6 +356,9 @@ struct mqtt_sec_config { /** Indicates the list of security tags to be used for the session. */ sec_tag_t *sec_tag_list; + /** Indicates the preference for enabling TLS session caching. */ + int session_cache; + /** Peer hostname for ceritificate verification. * May be NULL to skip hostname verification. */ @@ -363,6 +366,9 @@ struct mqtt_sec_config { /** Indicates the preference for copying certificates to the heap. */ int cert_nocopy; + + /** Set socket to native TLS */ + bool set_native_tls; }; /** @brief MQTT transport type. */ diff --git a/include/zephyr/net/net_event.h b/include/zephyr/net/net_event.h index 819a5ae6639..34856aec6af 100644 --- a/include/zephyr/net/net_event.h +++ b/include/zephyr/net/net_event.h @@ -79,6 +79,9 @@ enum net_event_ipv6_cmd { NET_EVENT_IPV6_CMD_DAD_FAILED, NET_EVENT_IPV6_CMD_NBR_ADD, NET_EVENT_IPV6_CMD_NBR_DEL, + NET_EVENT_IPV6_CMD_DHCP_START, + NET_EVENT_IPV6_CMD_DHCP_BOUND, + NET_EVENT_IPV6_CMD_DHCP_STOP, }; #define NET_EVENT_IPV6_ADDR_ADD \ @@ -129,6 +132,15 @@ enum net_event_ipv6_cmd { #define NET_EVENT_IPV6_NBR_DEL \ (_NET_EVENT_IPV6_BASE | NET_EVENT_IPV6_CMD_NBR_DEL) +#define NET_EVENT_IPV6_DHCP_START \ + (_NET_EVENT_IPV4_BASE | NET_EVENT_IPV6_CMD_DHCP_START) + +#define NET_EVENT_IPV6_DHCP_BOUND \ + (_NET_EVENT_IPV4_BASE | NET_EVENT_IPV6_CMD_DHCP_BOUND) + +#define NET_EVENT_IPV6_DHCP_STOP \ + (_NET_EVENT_IPV4_BASE | NET_EVENT_IPV6_CMD_DHCP_STOP) + /* IPv4 Events*/ #define _NET_IPV4_LAYER NET_MGMT_LAYER_L3 #define _NET_IPV4_CORE_CODE 0x004 diff --git a/include/zephyr/net/net_if.h b/include/zephyr/net/net_if.h index cced3557b84..92a59ad3b73 100644 --- a/include/zephyr/net/net_if.h +++ b/include/zephyr/net/net_if.h @@ -33,6 +33,9 @@ #if defined(CONFIG_NET_DHCPV4) && defined(CONFIG_NET_NATIVE_IPV4) #include #endif +#if defined(CONFIG_NET_DHCPV6) && defined(CONFIG_NET_NATIVE_IPV6) +#include +#endif #if defined(CONFIG_NET_IPV4_AUTO) && defined(CONFIG_NET_NATIVE_IPV4) #include #endif @@ -209,6 +212,9 @@ enum net_if_flag { /** IPv6 Multicast Listener Discovery disabled. */ NET_IF_IPV6_NO_MLD, + /** Mutex locking on TX data path disabled on the interface. */ + NET_IF_NO_TX_LOCK, + /** @cond INTERNAL_HIDDEN */ /* Total number of flags - must be at the end of the enum */ NET_IF_NUM_FLAGS @@ -275,6 +281,69 @@ struct net_if_ipv6 { uint8_t hop_limit; }; +#if defined(CONFIG_NET_DHCPV6) && defined(CONFIG_NET_NATIVE_IPV6) +struct net_if_dhcpv6 { + /** Used for timer list. */ + sys_snode_t node; + + /** Generated Client ID. */ + struct net_dhcpv6_duid_storage clientid; + + /** Server ID of the selected server. */ + struct net_dhcpv6_duid_storage serverid; + + /** DHCPv6 client state. */ + enum net_dhcpv6_state state; + + /** DHCPv6 client configuration parameters. */ + struct net_dhcpv6_params params; + + /** Timeout for the next event, absolute time, milliseconds. */ + uint64_t timeout; + + /** Time of the current exchange start, absolute time, milliseconds */ + uint64_t exchange_start; + + /** Renewal time, absolute time, milliseconds. */ + uint64_t t1; + + /** Rebinding time, absolute time, milliseconds. */ + uint64_t t2; + + /** The time when the last lease expires (terminates rebinding, + * DHCPv6 RFC8415, ch. 18.2.5). Absolute time, milliseconds. + */ + uint64_t expire; + + /** Generated IAID for IA_NA. */ + uint32_t addr_iaid; + + /** Generated IAID for IA_PD. */ + uint32_t prefix_iaid; + + /** Retransmit timeout for the current message, milliseconds. */ + uint32_t retransmit_timeout; + + /** Current best server preference received. */ + int16_t server_preference; + + /** Retransmission counter. */ + uint8_t retransmissions; + + /** Transaction ID for current exchange. */ + uint8_t tid[DHCPV6_TID_SIZE]; + + /** Prefix length. */ + uint8_t prefix_len; + + /** Assigned IPv6 prefix. */ + struct in6_addr prefix; + + /** Assigned IPv6 address. */ + struct in6_addr addr; +}; +#endif /* defined(CONFIG_NET_DHCPV6) && defined(CONFIG_NET_NATIVE_IPV6) */ + /** @cond INTERNAL_HIDDEN */ #if defined(CONFIG_NET_NATIVE_IPV4) #define NET_IF_MAX_IPV4_ADDR CONFIG_NET_IF_UNICAST_IPV4_ADDR_COUNT @@ -413,6 +482,10 @@ struct net_if_config { struct net_if_dhcpv4 dhcpv4; #endif /* CONFIG_NET_DHCPV4 */ +#if defined(CONFIG_NET_DHCPV6) && defined(CONFIG_NET_NATIVE_IPV6) + struct net_if_dhcpv6 dhcpv6; +#endif /* CONFIG_NET_DHCPV6 */ + #if defined(CONFIG_NET_IPV4_AUTO) && defined(CONFIG_NET_NATIVE_IPV4) struct net_if_ipv4_autoconf ipv4auto; #endif /* CONFIG_NET_IPV4_AUTO */ @@ -543,6 +616,7 @@ struct net_if { #endif struct k_mutex lock; + struct k_mutex tx_lock; }; static inline void net_if_lock(struct net_if *iface) @@ -559,6 +633,31 @@ static inline void net_if_unlock(struct net_if *iface) k_mutex_unlock(&iface->lock); } +static inline bool net_if_flag_is_set(struct net_if *iface, + enum net_if_flag value); + +static inline void net_if_tx_lock(struct net_if *iface) +{ + NET_ASSERT(iface); + + if (net_if_flag_is_set(iface, NET_IF_NO_TX_LOCK)) { + return; + } + + (void)k_mutex_lock(&iface->tx_lock, K_FOREVER); +} + +static inline void net_if_tx_unlock(struct net_if *iface) +{ + NET_ASSERT(iface); + + if (net_if_flag_is_set(iface, NET_IF_NO_TX_LOCK)) { + return; + } + + k_mutex_unlock(&iface->tx_lock); +} + /** * @brief Set a value in network interface flags * diff --git a/include/zephyr/net/net_pkt.h b/include/zephyr/net/net_pkt.h index d4011d1f582..199f78d2361 100644 --- a/include/zephyr/net/net_pkt.h +++ b/include/zephyr/net/net_pkt.h @@ -1678,6 +1678,13 @@ int net_pkt_alloc_buffer_debug(struct net_pkt *pkt, net_pkt_alloc_buffer_debug(_pkt, _size, _proto, _timeout, \ __func__, __LINE__) +int net_pkt_alloc_buffer_raw_debug(struct net_pkt *pkt, size_t size, + k_timeout_t timeout, + const char *caller, int line); +#define net_pkt_alloc_buffer_raw(_pkt, _size, _timeout) \ + net_pkt_alloc_buffer_raw_debug(_pkt, _size, _timeout, \ + __func__, __LINE__) + struct net_pkt *net_pkt_alloc_with_buffer_debug(struct net_if *iface, size_t size, sa_family_t family, @@ -1792,6 +1799,24 @@ int net_pkt_alloc_buffer(struct net_pkt *pkt, k_timeout_t timeout); #endif +/** + * @brief Allocate buffer for a net_pkt, of specified size, w/o any additional + * preconditions + * + * @details: The actual buffer size may be larger than requested one if fixed + * size buffers are in use. + * + * @param pkt The network packet requiring buffer to be allocated. + * @param size The size of buffer being requested. + * @param timeout Maximum time to wait for an allocation. + * + * @return 0 on success, negative errno code otherwise. + */ +#if !defined(NET_PKT_DEBUG_ENABLED) +int net_pkt_alloc_buffer_raw(struct net_pkt *pkt, size_t size, + k_timeout_t timeout); +#endif + /** * @brief Allocate a network packet and buffer at once * diff --git a/include/zephyr/net/socket.h b/include/zephyr/net/socket.h index ff7b2cc2281..50070f0c7a5 100644 --- a/include/zephyr/net/socket.h +++ b/include/zephyr/net/socket.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #ifdef __cplusplus diff --git a/include/zephyr/net/socket_ncs.h b/include/zephyr/net/socket_ncs.h new file mode 100644 index 00000000000..a80e1ddc238 --- /dev/null +++ b/include/zephyr/net/socket_ncs.h @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2021 Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_NET_SOCKET_NCS_H_ +#define ZEPHYR_INCLUDE_NET_SOCKET_NCS_H_ + +/** + * @file + * @brief NCS specific additions to the BSD sockets API definitions + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* When CONFIG_NET_SOCKETS_OFFLOAD is enabled, offloaded sockets take precedence + * when creating a new socket. Combine this flag with a socket type when + * creating a socket, to enforce native socket creation (e. g. SOCK_STREAM | SOCK_NATIVE). + * If it's desired to create a native TLS socket, but still offload the + * underlying TCP/UDP socket, use e. g. SOCK_STREAM | SOCK_NATIVE_TLS. + */ +#define SOCK_NATIVE 0x80000000 +#define SOCK_NATIVE_TLS 0x40000000 + +/* NCS specific TLS level socket options */ + +/** Socket option to set DTLS handshake timeout, specifically for nRF sockets. + * The option accepts an integer, indicating the total handshake timeout, + * including retransmissions, in seconds. + * Accepted values for the option are: 1, 3, 7, 15, 31, 63, 123. + */ +#define TLS_DTLS_HANDSHAKE_TIMEO 18 + +/** Socket option to save DTLS connection, specifically for nRF sockets. + */ +#define TLS_DTLS_CONN_SAVE 19 + +/** Socket option to load DTLS connection, specifically for nRF sockets. + */ +#define TLS_DTLS_CONN_LOAD 20 + +/** Socket option to get result of latest TLS/DTLS completed handshakes end status, + * specifically for nRF sockets. + * The option accepts an integer, indicating the setting. + * Accepted vaules for the option are: 0 and 1. + */ +#define TLS_DTLS_HANDSHAKE_STATUS 21 + +/* Valid values for TLS_SESSION_CACHE option */ +#define TLS_SESSION_CACHE_DISABLED 0 /**< Disable TLS session caching. */ +#define TLS_SESSION_CACHE_ENABLED 1 /**< Enable TLS session caching. */ + +/* Valid values for TLS_DTLS_HANDSHAKE_TIMEO option */ +#define TLS_DTLS_HANDSHAKE_TIMEO_NONE 0 /**< No timeout */ +#define TLS_DTLS_HANDSHAKE_TIMEO_1S 1 /**< 1 second */ +#define TLS_DTLS_HANDSHAKE_TIMEO_3S 3 /**< 1s + 2s */ +#define TLS_DTLS_HANDSHAKE_TIMEO_7S 7 /**< 1s + 2s + 4s */ +#define TLS_DTLS_HANDSHAKE_TIMEO_15S 15 /**< 1s + 2s + 4s + 8s */ +#define TLS_DTLS_HANDSHAKE_TIMEO_31S 31 /**< 1s + 2s + 4s + 8s + 16s */ +#define TLS_DTLS_HANDSHAKE_TIMEO_63S 63 /**< 1s + 2s + 4s + 8s + 16s + 32s */ +#define TLS_DTLS_HANDSHAKE_TIMEO_123S 123 /**< 1s + 2s + 4s + 8s + 16s + 32s + 60s */ + +/* Valid values for TLS_DTLS_HANDSHAKE_STATUS option */ +#define TLS_DTLS_HANDSHAKE_STATUS_FULL 0 +#define TLS_DTLS_HANDSHAKE_STATUS_CACHED 1 + +/* NCS specific socket options */ + +/** sockopt: enable sending data as part of exceptional events */ +#define SO_EXCEPTIONAL_DATA 33 +/** sockopt: bind to PDN */ +#define SO_BINDTOPDN 40 +/** sockopt: Release Assistance Indication feature: This will indicate that the + * application will not send any more data. + */ +#define SO_RAI_NO_DATA 50 +/** sockopt: Release Assistance Indication feature: This will indicate that the + * next call to send/sendto will be the last one for some time. + */ +#define SO_RAI_LAST 51 +/** sockopt: Release Assistance Indication feature: This will indicate that + * after the next call to send/sendto, the application is expecting to receive + * one more data packet before this socket will not be used again for some time. + */ +#define SO_RAI_ONE_RESP 52 +/** sockopt: Release Assistance Indication feature: If a client application + * expects to use the socket more it can indicate that by setting this socket + * option before the next send call which will keep the network up longer. + */ +#define SO_RAI_ONGOING 53 +/** sockopt: Release Assistance Indication feature: If a server application + * expects to use the socket more it can indicate that by setting this socket + * option before the next send call. + */ +#define SO_RAI_WAIT_MORE 54 + +/* NCS specific IPPROTO_ALL level socket options */ + +/** IPv4 and IPv6 protocol level (pseudo-val) for nRF sockets. */ +#define IPPROTO_ALL 512 +/** sockopt: disable all replies to unexpected traffics */ +#define SO_SILENCE_ALL 30 + +/* NCS specific IPPROTO_IP level socket options */ + +/** sockopt: enable IPv4 ICMP replies */ +#define SO_IP_ECHO_REPLY 31 + +/* NCS specific IPPROTO_IPV6 level socket options */ + +/** sockopt: enable IPv6 ICMP replies */ +#define SO_IPV6_ECHO_REPLY 32 + +/* NCS specific TCP level socket options */ + +/** sockopt: Configurable TCP server session timeout in minutes. + * Range is 0 to 135. 0 is no timeout and 135 is 2 h 15 min. Default is 0 (no timeout). + */ +#define SO_TCP_SRV_SESSTIMEO 55 + +/* NCS specific gettaddrinfo() flags */ + +/** Assume `service` contains a Packet Data Network (PDN) ID. + * When specified together with the AI_NUMERICSERV flag, + * `service` shall be formatted as follows: "port:pdn_id" + * where "port" is the port number and "pdn_id" is the PDN ID. + * Example: "8080:1", port 8080 PDN ID 1. + * Example: "42:0", port 42 PDN ID 0. + */ +#define AI_PDNSERV 0x1000 + +/* NCS specific send() and sendto() flags */ + +/** Request a blocking send operation until the request is acknowledged. + * When used in send() or sendto(), the request will not return until the + * send operation is completed by lower layers, or until the timeout, given by the SO_SNDTIMEO + * socket option, is reached. Valid timeout values are 1 to 600 seconds. + */ +#define MSG_WAITACK 0x200 + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_NET_SOCKET_NCS_H_ */ diff --git a/include/zephyr/net/wifi.h b/include/zephyr/net/wifi.h index aa3096fc5ac..294fdc53c5d 100644 --- a/include/zephyr/net/wifi.h +++ b/include/zephyr/net/wifi.h @@ -52,30 +52,7 @@ enum wifi_security_type { }; /** Helper function to get user-friendly security type name. */ -static inline const char *wifi_security_txt(enum wifi_security_type security) -{ - switch (security) { - case WIFI_SECURITY_TYPE_NONE: - return "OPEN"; - case WIFI_SECURITY_TYPE_WEP: - return "WEP"; - case WIFI_SECURITY_TYPE_WPA_PSK: - return "WPA-PSK"; - case WIFI_SECURITY_TYPE_PSK: - return "WPA2-PSK"; - case WIFI_SECURITY_TYPE_PSK_SHA256: - return "WPA2-PSK-SHA256"; - case WIFI_SECURITY_TYPE_SAE: - return "WPA3-SAE"; - case WIFI_SECURITY_TYPE_WAPI: - return "WAPI"; - case WIFI_SECURITY_TYPE_EAP: - return "EAP"; - case WIFI_SECURITY_TYPE_UNKNOWN: - default: - return "UNKNOWN"; - } -} +const char * const wifi_security_txt(enum wifi_security_type security); /** IEEE 802.11w - Management frame protection. */ enum wifi_mfp_options { @@ -92,20 +69,7 @@ enum wifi_mfp_options { }; /** Helper function to get user-friendly MFP name.*/ -static inline const char *wifi_mfp_txt(enum wifi_mfp_options mfp) -{ - switch (mfp) { - case WIFI_MFP_DISABLE: - return "Disable"; - case WIFI_MFP_OPTIONAL: - return "Optional"; - case WIFI_MFP_REQUIRED: - return "Required"; - case WIFI_MFP_UNKNOWN: - default: - return "UNKNOWN"; - } -} +const char * const wifi_mfp_txt(enum wifi_mfp_options mfp); /** * @brief IEEE 802.11 operational frequency bands (not exhaustive). @@ -127,20 +91,7 @@ enum wifi_frequency_bands { }; /** Helper function to get user-friendly frequency band name. */ -static inline const char *wifi_band_txt(enum wifi_frequency_bands band) -{ - switch (band) { - case WIFI_FREQ_BAND_2_4_GHZ: - return "2.4GHz"; - case WIFI_FREQ_BAND_5_GHZ: - return "5GHz"; - case WIFI_FREQ_BAND_6_GHZ: - return "6GHz"; - case WIFI_FREQ_BAND_UNKNOWN: - default: - return "UNKNOWN"; - } -} +const char * const wifi_band_txt(enum wifi_frequency_bands band); #define WIFI_SSID_MAX_LEN 32 #define WIFI_PSK_MIN_LEN 8 @@ -148,6 +99,7 @@ static inline const char *wifi_band_txt(enum wifi_frequency_bands band) #define WIFI_SAE_PSWD_MAX_LEN 128 #define WIFI_MAC_ADDR_LEN 6 +#define WIFI_CHANNEL_MIN 1 #define WIFI_CHANNEL_MAX 233 #define WIFI_CHANNEL_ANY 255 @@ -183,34 +135,7 @@ enum wifi_iface_state { }; /** Helper function to get user-friendly interface state name. */ -static inline const char *wifi_state_txt(enum wifi_iface_state state) -{ - switch (state) { - case WIFI_STATE_DISCONNECTED: - return "DISCONNECTED"; - case WIFI_STATE_INACTIVE: - return "INACTIVE"; - case WIFI_STATE_INTERFACE_DISABLED: - return "INTERFACE_DISABLED"; - case WIFI_STATE_SCANNING: - return "SCANNING"; - case WIFI_STATE_AUTHENTICATING: - return "AUTHENTICATING"; - case WIFI_STATE_ASSOCIATING: - return "ASSOCIATING"; - case WIFI_STATE_ASSOCIATED: - return "ASSOCIATED"; - case WIFI_STATE_4WAY_HANDSHAKE: - return "4WAY_HANDSHAKE"; - case WIFI_STATE_GROUP_HANDSHAKE: - return "GROUP_HANDSHAKE"; - case WIFI_STATE_COMPLETED: - return "COMPLETED"; - case WIFI_STATE_UNKNOWN: - default: - return "UNKNOWN"; - } -} +const char * const wifi_state_txt(enum wifi_iface_state state); /** Wi-Fi interface modes. * @@ -236,26 +161,7 @@ enum wifi_iface_mode { }; /** Helper function to get user-friendly interface mode name. */ -static inline const char *wifi_mode_txt(enum wifi_iface_mode mode) -{ - switch (mode) { - case WIFI_MODE_INFRA: - return "STATION"; - case WIFI_MODE_IBSS: - return "ADHOC"; - case WIFI_MODE_AP: - return "ACCESS POINT"; - case WIFI_MODE_P2P_GO: - return "P2P GROUP OWNER"; - case WIFI_MODE_P2P_GROUP_FORMATION: - return "P2P GROUP FORMATION"; - case WIFI_MODE_MESH: - return "MESH"; - case WIFI_MODE_UNKNOWN: - default: - return "UNKNOWN"; - } -} +const char * const wifi_mode_txt(enum wifi_iface_mode mode); /** Wi-Fi link operating modes * @@ -287,32 +193,7 @@ enum wifi_link_mode { }; /** Helper function to get user-friendly link mode name. */ -static inline const char *wifi_link_mode_txt(enum wifi_link_mode link_mode) -{ - switch (link_mode) { - case WIFI_0: - return "WIFI 0 (802.11)"; - case WIFI_1: - return "WIFI 1 (802.11b)"; - case WIFI_2: - return "WIFI 2 (802.11a)"; - case WIFI_3: - return "WIFI 3 (802.11g)"; - case WIFI_4: - return "WIFI 4 (802.11n/HT)"; - case WIFI_5: - return "WIFI 5 (802.11ac/VHT)"; - case WIFI_6: - return "WIFI 6 (802.11ax/HE)"; - case WIFI_6E: - return "WIFI 6E (802.11ax 6GHz/HE)"; - case WIFI_7: - return "WIFI 7 (802.11be/EHT)"; - case WIFI_LINK_MODE_UNKNOWN: - default: - return "UNKNOWN"; - } -} +const char * const wifi_link_mode_txt(enum wifi_link_mode link_mode); /** Wi-Fi scanning types. */ enum wifi_scan_type { @@ -330,12 +211,8 @@ enum wifi_ps { WIFI_PS_ENABLED, }; -/** @cond INTERNAL_HIDDEN */ -static const char * const wifi_ps2str[] = { - [WIFI_PS_DISABLED] = "Power save disabled", - [WIFI_PS_ENABLED] = "Power save enabled", -}; -/** @endcond */ +/** Helper function to get user-friendly ps name. */ +const char * const wifi_ps_txt(enum wifi_ps ps_name); /** Wi-Fi power save modes. */ enum wifi_ps_mode { @@ -348,12 +225,40 @@ enum wifi_ps_mode { WIFI_PS_MODE_WMM, }; -/** @cond INTERNAL_HIDDEN */ -static const char * const wifi_ps_mode2str[] = { - [WIFI_PS_MODE_LEGACY] = "Legacy power save", - [WIFI_PS_MODE_WMM] = "WMM power save", +/** Helper function to get user-friendly ps mode name. */ +const char * const wifi_ps_mode_txt(enum wifi_ps_mode ps_mode); + +/* Interface index Min and Max values */ +#define WIFI_INTERFACE_INDEX_MIN 1 +#define WIFI_INTERFACE_INDEX_MAX 255 + +/** Wifi operational mode */ +enum wifi_operational_modes { + /** STA mode setting enable */ + WIFI_STA_MODE = BIT(0), + /** Monitor mode setting enable */ + WIFI_MONITOR_MODE = BIT(1), + /** TX injection mode setting enable */ + WIFI_TX_INJECTION_MODE = BIT(2), + /** Promiscuous mode setting enable */ + WIFI_PROMISCUOUS_MODE = BIT(3), + /** AP mode setting enable */ + WIFI_AP_MODE = BIT(4), + /** Softap mode setting enable */ + WIFI_SOFTAP_MODE = BIT(5), +}; + +/** Mode filter settings */ +enum wifi_filter { + /** Support management, data and control packet sniffing */ + WIFI_PACKET_FILTER_ALL = BIT(0), + /** Support only sniffing of management packets */ + WIFI_PACKET_FILTER_MGMT = BIT(1), + /** Support only sniffing of data packets */ + WIFI_PACKET_FILTER_DATA = BIT(2), + /** Support only sniffing of control packets */ + WIFI_PACKET_FILTER_CTRL = BIT(3), }; -/** @endcond */ /** Wi-Fi Target Wake Time (TWT) operations. */ enum wifi_twt_operation { @@ -363,12 +268,8 @@ enum wifi_twt_operation { WIFI_TWT_TEARDOWN, }; -/** @cond INTERNAL_HIDDEN */ -static const char * const wifi_twt_operation2str[] = { - [WIFI_TWT_SETUP] = "TWT setup", - [WIFI_TWT_TEARDOWN] = "TWT teardown", -}; -/** @endcond */ +/** Helper function to get user-friendly twt operation name. */ +const char * const wifi_twt_operation_txt(enum wifi_twt_operation twt_operation); /** Wi-Fi Target Wake Time (TWT) negotiation types. */ enum wifi_twt_negotiation_type { @@ -380,13 +281,8 @@ enum wifi_twt_negotiation_type { WIFI_TWT_WAKE_TBTT }; -/** @cond INTERNAL_HIDDEN */ -static const char * const wifi_twt_negotiation_type2str[] = { - [WIFI_TWT_INDIVIDUAL] = "TWT individual negotiation", - [WIFI_TWT_BROADCAST] = "TWT broadcast negotiation", - [WIFI_TWT_WAKE_TBTT] = "TWT wake TBTT negotiation", -}; -/** @endcond */ +/** Helper function to get user-friendly twt negotiation type name. */ +const char * const wifi_twt_negotiation_type_txt(enum wifi_twt_negotiation_type twt_negotiation); /** Wi-Fi Target Wake Time (TWT) setup commands. */ enum wifi_twt_setup_cmd { @@ -408,18 +304,8 @@ enum wifi_twt_setup_cmd { WIFI_TWT_SETUP_CMD_REJECT, }; -/** @cond INTERNAL_HIDDEN */ -static const char * const wifi_twt_setup_cmd2str[] = { - [WIFI_TWT_SETUP_CMD_REQUEST] = "TWT request", - [WIFI_TWT_SETUP_CMD_SUGGEST] = "TWT suggest", - [WIFI_TWT_SETUP_CMD_DEMAND] = "TWT demand", - [WIFI_TWT_SETUP_CMD_GROUPING] = "TWT grouping", - [WIFI_TWT_SETUP_CMD_ACCEPT] = "TWT accept", - [WIFI_TWT_SETUP_CMD_ALTERNATE] = "TWT alternate", - [WIFI_TWT_SETUP_CMD_DICTATE] = "TWT dictate", - [WIFI_TWT_SETUP_CMD_REJECT] = "TWT reject", -}; -/** @endcond */ +/** Helper function to get user-friendly twt setup cmd name. */ +const char * const wifi_twt_setup_cmd_txt(enum wifi_twt_setup_cmd twt_setup); /** Wi-Fi Target Wake Time (TWT) negotiation status. */ enum wifi_twt_setup_resp_status { @@ -510,12 +396,8 @@ enum wifi_ps_wakeup_mode { WIFI_PS_WAKEUP_MODE_LISTEN_INTERVAL, }; -/** @cond INTERNAL_HIDDEN */ -static const char * const wifi_ps_wakeup_mode2str[] = { - [WIFI_PS_WAKEUP_MODE_DTIM] = "PS wakeup mode DTIM", - [WIFI_PS_WAKEUP_MODE_LISTEN_INTERVAL] = "PS wakeup mode listen interval", -}; -/** @endcond */ +/** Helper function to get user-friendly ps wakeup mode name. */ +const char * const wifi_ps_wakeup_mode_txt(enum wifi_ps_wakeup_mode ps_wakeup_mode); /** Wi-Fi power save error codes. */ enum wifi_config_ps_param_fail_reason { diff --git a/include/zephyr/net/wifi_mgmt.h b/include/zephyr/net/wifi_mgmt.h index 0bdb5753531..1be98b927cb 100644 --- a/include/zephyr/net/wifi_mgmt.h +++ b/include/zephyr/net/wifi_mgmt.h @@ -41,6 +41,8 @@ extern "C" { #define WIFI_MGMT_SCAN_SSID_FILT_MAX 0 #endif /* CONFIG_WIFI_MGMT_SCAN_SSID_FILT_MAX */ +#define WIFI_MGMT_BAND_STR_SIZE_MAX 8 + /** Wi-Fi management commands */ enum net_request_wifi_cmd { /** Scan for Wi-Fi networks */ @@ -67,6 +69,12 @@ enum net_request_wifi_cmd { NET_REQUEST_WIFI_CMD_REG_DOMAIN, /** Set power save timeout */ NET_REQUEST_WIFI_CMD_PS_TIMEOUT, + /** Set or get Mode of operation */ + NET_REQUEST_WIFI_CMD_MODE, + /** Set or get packet filter setting for current mode */ + NET_REQUEST_WIFI_CMD_PACKET_FILTER, + /** Set or get Wi-Fi channel for Monitor or TX-Injection mode */ + NET_REQUEST_WIFI_CMD_CHANNEL, NET_REQUEST_WIFI_CMD_MAX }; @@ -129,6 +137,21 @@ NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_WIFI_REG_DOMAIN); NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_WIFI_PS_TIMEOUT); +#define NET_REQUEST_WIFI_MODE \ + (_NET_WIFI_BASE | NET_REQUEST_WIFI_CMD_MODE) + +NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_WIFI_MODE); + +#define NET_REQUEST_WIFI_PACKET_FILTER \ + (_NET_WIFI_BASE | NET_REQUEST_WIFI_CMD_PACKET_FILTER) + +NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_WIFI_PACKET_FILTER); + +#define NET_REQUEST_WIFI_CHANNEL \ + (_NET_WIFI_BASE | NET_REQUEST_WIFI_CMD_CHANNEL) + +NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_WIFI_CHANNEL); + /** Wi-Fi management events */ enum net_event_wifi_cmd { /** Scan results available */ @@ -282,9 +305,43 @@ struct wifi_connect_req_params { int timeout; }; +/** Wi-Fi connect result codes. To be overlaid on top of \ref wifi_status + * in the connect result event for detailed status. + */ +enum wifi_conn_status { + /** Connection successful */ + WIFI_STATUS_CONN_SUCCESS = 0, + /** Connection failed - generic failure */ + WIFI_STATUS_CONN_FAIL, + /** Connection failed - wrong password */ + WIFI_STATUS_CONN_WRONG_PASSWORD, + /** Connection timed out */ + WIFI_STATUS_CONN_TIMEOUT, + /** Connection failed - AP not found */ + WIFI_STATUS_CONN_AP_NOT_FOUND, +}; + +/** Wi-Fi disconnect reason codes. To be overlaid on top of \ref wifi_status + * in the disconnect result event for detailed reason. + */ +enum wifi_disconn_reason { + /** Unspecified reason */ + WIFI_REASON_DISCONN_UNSPECIFIED = 0, + /** Disconnected due to user request */ + WIFI_REASON_DISCONN_USER_REQUEST, + /** Disconnected due to AP leaving */ + WIFI_REASON_DISCONN_AP_LEAVING, + /** Disconnected due to inactivity */ + WIFI_REASON_DISCONN_INACTIVITY, +}; + /** Generic Wi-Fi status for commands and events */ struct wifi_status { - int status; + union { + int status; + enum wifi_conn_status conn_status; + enum wifi_disconn_reason disconn_reason; + }; }; /** Wi-Fi interface status */ @@ -473,6 +530,37 @@ union wifi_mgmt_events { struct wifi_twt_params twt_params; }; +/** Wi-Fi mode setup */ +struct wifi_mode_info { + /** Mode setting for a specific mode of operation */ + uint8_t mode; + /** Interface index */ + uint8_t if_index; + /** Get or set operation */ + enum wifi_mgmt_op oper; +}; + +/** Wi-Fi filter setting for monitor, prmoiscuous, TX-injection modes */ +struct wifi_filter_info { + /** Filter setting */ + uint8_t filter; + /** Interface index */ + uint8_t if_index; + /** Filter buffer size */ + uint16_t buffer_size; + /** Get or set operation */ + enum wifi_mgmt_op oper; +}; + +/** Wi-Fi channel setting for monitor and TX-injection modes */ +struct wifi_channel_info { + /** Channel value to set */ + uint16_t channel; + /** Interface index */ + uint8_t if_index; + /** Get or set operation */ + enum wifi_mgmt_op oper; +}; #include @@ -594,6 +682,30 @@ struct wifi_mgmt_ops { * @return 0 if ok, < 0 if error */ int (*reg_domain)(const struct device *dev, struct wifi_reg_domain *reg_domain); + /** Set or get packet filter settings for monitor and promiscuous modes + * + * @param dev Pointer to the device structure for the driver instance. + * @param packet filter settings + * + * @return 0 if ok, < 0 if error + */ + int (*filter)(const struct device *dev, struct wifi_filter_info *filter); + /** Set or get mode of operation + * + * @param dev Pointer to the device structure for the driver instance. + * @param mode settings + * + * @return 0 if ok, < 0 if error + */ + int (*mode)(const struct device *dev, struct wifi_mode_info *mode); + /** Set or get current channel of operation + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel settings + * + * @return 0 if ok, < 0 if error + */ + int (*channel)(const struct device *dev, struct wifi_channel_info *channel); }; /** Wi-Fi management offload API */ diff --git a/include/zephyr/net/zperf.h b/include/zephyr/net/zperf.h index 447ffec670c..fc809ae9278 100644 --- a/include/zephyr/net/zperf.h +++ b/include/zephyr/net/zperf.h @@ -37,11 +37,13 @@ struct zperf_upload_params { struct { uint8_t tos; int tcp_nodelay; + int priority; } options; }; struct zperf_download_params { uint16_t port; + struct sockaddr addr; }; struct zperf_results { diff --git a/include/zephyr/storage/flash_map.h b/include/zephyr/storage/flash_map.h index 380e58691e9..cc4a246105d 100644 --- a/include/zephyr/storage/flash_map.h +++ b/include/zephyr/storage/flash_map.h @@ -271,6 +271,10 @@ const char *flash_area_label(const struct flash_area *fa); */ uint8_t flash_area_erased_val(const struct flash_area *fa); +#if USE_PARTITION_MANAGER +#include +#else + #define FLASH_AREA_LABEL_EXISTS(label) __DEPRECATED_MACRO \ DT_HAS_FIXED_PARTITION_LABEL(label) @@ -343,6 +347,8 @@ uint8_t flash_area_erased_val(const struct flash_area *fa); #define FIXED_PARTITION_DEVICE(label) \ DEVICE_DT_GET(DT_MTD_FROM_FIXED_PARTITION(DT_NODELABEL(label))) +#endif /* USE_PARTITION_MANAGER */ + #ifdef __cplusplus } #endif diff --git a/include/zephyr/zbus/zbus.h b/include/zephyr/zbus/zbus.h index 466a2ad9dcf..8a4188aa821 100644 --- a/include/zephyr/zbus/zbus.h +++ b/include/zephyr/zbus/zbus.h @@ -92,7 +92,8 @@ struct zbus_channel { */ enum __packed zbus_observer_type { ZBUS_OBSERVER_LISTENER_TYPE, - ZBUS_OBSERVER_SUBSCRIBER_TYPE + ZBUS_OBSERVER_SUBSCRIBER_TYPE, + ZBUS_OBSERVER_MSG_SUBSCRIBER_TYPE, }; /** @@ -127,6 +128,13 @@ struct zbus_observer { /** Observer callback function. It turns the observer into a listener. */ void (*const callback)(const struct zbus_channel *chan); + +#if defined(CONFIG_ZBUS_MSG_SUBSCRIBER) || defined(__DOXYGEN__) + /** Observer message FIFO. It turns the observer into a message subscriber. It only + * exists if the @kconfig{CONFIG_ZBUS_MSG_SUBSCRIBER} is enabled. + */ + struct k_fifo *const message_fifo; +#endif /* CONFIG_ZBUS_MSG_SUBSCRIBER */ }; }; @@ -156,8 +164,10 @@ struct zbus_channel_observation { #if defined(CONFIG_ZBUS_CHANNEL_NAME) #define ZBUS_CHANNEL_NAME_INIT(_name) .name = #_name, +#define _ZBUS_CHAN_NAME(_chan) (_chan)->name #else #define ZBUS_CHANNEL_NAME_INIT(_name) +#define _ZBUS_CHAN_NAME(_chan) "" #endif #if defined(CONFIG_ZBUS_OBSERVER_NAME) @@ -379,6 +389,37 @@ k_timeout_t _zbus_timeout_remainder(uint64_t end_ticks); */ #define ZBUS_LISTENER_DEFINE(_name, _cb) ZBUS_LISTENER_DEFINE_WITH_ENABLE(_name, _cb, true) +/** + * @brief Define and initialize a message subscriber. + * + * This macro defines an observer of @ref ZBUS_OBSERVER_SUBSCRIBER_TYPE type. It defines a FIFO + * where the subscriber will receive the message asynchronously and initialize the @ref + * zbus_observer defining the subscriber. + * + * @param[in] _name The subscriber's name. + * @param[in] _enable The subscriber's initial state. + */ +#define ZBUS_MSG_SUBSCRIBER_DEFINE_WITH_ENABLE(_name, _enable) \ + static K_FIFO_DEFINE(_zbus_observer_fifo_##_name); \ + STRUCT_SECTION_ITERABLE(zbus_observer, _name) = { \ + ZBUS_OBSERVER_NAME_INIT(_name) /* Name field */ \ + .type = ZBUS_OBSERVER_MSG_SUBSCRIBER_TYPE, \ + .enabled = _enable, \ + .message_fifo = &_zbus_observer_fifo_##_name, \ + } + +/** + * @brief Define and initialize an enabled message subscriber. + * + * This macro defines an observer of message subscriber type. It defines a FIFO where the + * subscriber will receive the message asynchronously and initialize the @ref + * zbus_observer defining the subscriber. The message subscribers are defined in the enabled state + * with this macro. + + * + * @param[in] _name The subscriber's name. + */ +#define ZBUS_MSG_SUBSCRIBER_DEFINE(_name) ZBUS_MSG_SUBSCRIBER_DEFINE_WITH_ENABLE(_name, true) /** * * @brief Publish to a channel @@ -741,6 +782,31 @@ static inline const char *zbus_obs_name(const struct zbus_observer *obs) int zbus_sub_wait(const struct zbus_observer *sub, const struct zbus_channel **chan, k_timeout_t timeout); +#if defined(CONFIG_ZBUS_MSG_SUBSCRIBER) || defined(__DOXYGEN__) + +/** + * @brief Wait for a channel message. + * + * This routine makes the subscriber wait for the new message in case of channel publication. + * + * @param[in] sub The subscriber's reference. + * @param[out] chan The notification channel's reference. + * @param[out] msg A reference to a copy of the published message. + * @param[in] timeout Waiting period for a notification arrival, + * or one of the special values, K_NO_WAIT and K_FOREVER. + * + * @retval 0 Message received. + * @retval -EINVAL The observer is not a subscriber. + * @retval -ENOMSG Could not retrieve the net_buf from the subscriber FIFO. + * @retval -EILSEQ Received an invalid channel reference. + * @retval -EFAULT A parameter is incorrect, or the function context is invalid (inside an ISR). The + * function only returns this value when the @kconfig{CONFIG_ZBUS_ASSERT_MOCK} is enabled. + */ +int zbus_sub_wait_msg(const struct zbus_observer *sub, const struct zbus_channel **chan, void *msg, + k_timeout_t timeout); + +#endif /* CONFIG_ZBUS_MSG_SUBSCRIBER */ + /** * * @brief Iterate over channels. diff --git a/lib/libc/common/source/stdlib/malloc.c b/lib/libc/common/source/stdlib/malloc.c index 50c688fa7f2..47583982838 100644 --- a/lib/libc/common/source/stdlib/malloc.c +++ b/lib/libc/common/source/stdlib/malloc.c @@ -23,6 +23,20 @@ #include LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL); +#if USE_PARTITION_MANAGER + +#include + +#define RAM_SIZE PM_SRAM_SIZE +#define RAM_ADDR PM_SRAM_ADDRESS + +#else /* ! USE_PARTITION_MANAGER */ + +#define RAM_SIZE (KB((size_t) CONFIG_SRAM_SIZE)) +#define RAM_ADDR CONFIG_SRAM_BASE_ADDRESS + +#endif /* USE_PARTITION_MANAGER */ + #ifdef CONFIG_COMMON_LIBC_MALLOC #if (CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE != 0) @@ -104,8 +118,8 @@ static POOL_SECTION unsigned char __aligned(HEAP_ALIGN) malloc_arena[HEAP_SIZE]; extern char _heap_sentry[]; # define HEAP_SIZE ROUND_DOWN((POINTER_TO_UINT(_heap_sentry) - HEAP_BASE), HEAP_ALIGN) # else -# define HEAP_SIZE ROUND_DOWN((KB((size_t) CONFIG_SRAM_SIZE) - \ - ((size_t) HEAP_BASE - (size_t) CONFIG_SRAM_BASE_ADDRESS)), HEAP_ALIGN) +# define HEAP_SIZE ROUND_DOWN((RAM_SIZE - \ + ((size_t) HEAP_BASE - (size_t) RAM_ADDR)), HEAP_ALIGN) # endif /* else CONFIG_XTENSA */ # endif /* else CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE > 0 */ diff --git a/lib/os/Kconfig.heap b/lib/os/Kconfig.heap index 14fba87470b..1fa84ff41db 100644 --- a/lib/os/Kconfig.heap +++ b/lib/os/Kconfig.heap @@ -51,7 +51,7 @@ config HEAP_LISTENER choice prompt "Supported heap sizes" depends on !64BIT - default SYS_HEAP_SMALL_ONLY if (SRAM_SIZE <= 256) + default SYS_HEAP_SMALL_ONLY if (SRAM_SIZE <= 256) && !PARTITION_MANAGER_ENABLED default SYS_HEAP_AUTO help Heaps using reduced-size chunk headers can accommodate so called diff --git a/modules/Kconfig.mcuboot b/modules/Kconfig.mcuboot index 0bb47d8854c..8df4bde8829 100644 --- a/modules/Kconfig.mcuboot +++ b/modules/Kconfig.mcuboot @@ -116,7 +116,7 @@ config MCUBOOT_EXTRA_IMGTOOL_ARGS help When signing (CONFIG_MCUBOOT_SIGNATURE_KEY_FILE is a non-empty string) you can use this option to pass extra options to - imgtool. For example, you could set this to "--version 1.2". + imgtool. For example, you could set this to "--version 1.2". config MCUBOOT_GENERATE_UNSIGNED_IMAGE bool "Generate unsigned binary image bootable with MCUboot" @@ -136,6 +136,8 @@ config MCUBOOT_GENERATE_CONFIRMED_IMAGE The existence of bin and hex files depends on CONFIG_BUILD_OUTPUT_BIN and CONFIG_BUILD_OUTPUT_HEX. +menu "On board MCUboot operation mode" + choice MCUBOOT_BOOTLOADER_MODE prompt "Application assumed MCUboot mode of operation" default MCUBOOT_BOOTLOADER_MODE_SWAP_WITHOUT_SCRATCH # MCUBOOT_BOOTLOADER_MODE @@ -154,30 +156,91 @@ config MCUBOOT_BOOTLOADER_MODE_SINGLE_APP config MCUBOOT_BOOTLOADER_MODE_SWAP_WITHOUT_SCRATCH bool "MCUboot has been configured for swap without scratch operation" + select MCUBOOT_BOOTLOADER_MODE_HAS_NO_DOWNGRADE help MCUboot expects slot0_partition and slot1_partition to be present in DT and application will boot from slot0_partition. + MCUBOOT_BOOTLOADER_NO_DOWNGRADE should also be selected + if MCUboot has been built with MCUBOOT_DOWNGRADE_PREVENTION. config MCUBOOT_BOOTLOADER_MODE_SWAP_SCRATCH bool "MCUboot has been configured for swap using scratch operation" + select MCUBOOT_BOOTLOADER_MODE_HAS_NO_DOWNGRADE help MCUboot expects slot0_partition, slot1_partition and scratch_partition to be present in DT, and application will boot from slot0_partition. In this mode scratch_partition is used as temporary storage when MCUboot swaps application from the secondary slot to the primary slot. + MCUBOOT_BOOTLOADER_NO_DOWNGRADE should also be selected + if MCUboot has been built with MCUBOOT_DOWNGRADE_PREVENTION. + +config MCUBOOT_BOOTLOADER_MODE_OVERWRITE_ONLY + bool "MCUboot has been configured to just overwrite primary slot" + select MCUBOOT_BOOTLOADER_MODE_HAS_NO_DOWNGRADE + help + MCUboot will take contents of secondary slot of an image and will + overwrite primary slot with it. + In this mode it is not possible to revert back to previous version + as it is not stored in the secondary slot. + This mode supports MCUBOOT_BOOTLOADER_NO_DOWNGRADE which means + that the overwrite will not happen unless the version of secondary + slot is higher than the version in primary slot. config MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP bool "MCUboot has been configured for DirectXIP operation" + select MCUBOOT_BOOTLOADER_MODE_HAS_NO_DOWNGRADE + select MCUBOOT_BOOTLOADER_NO_DOWNGRADE help MCUboot expects slot0_partition and slot1_partition to exist in DT. In this mode MCUboot can boot from either partition and will select one with higher application image version, which usually means major.minor.patch triple, unless BOOT_VERSION_CMP_USE_BUILD_NUMBER is also selected that enables comparison of build number. + This option automatically selectes + MCUBOOT_BOOTLOADER_NO_DOWNGRADE as it is not possible + to swap back to older version of application. + +config MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP_WITH_REVERT + bool "MCUboot has been configured for DirectXIP with revert" + select MCUBOOT_BOOTUTIL_LIB_FOR_DIRECT_XIP + select MCUBOOT_BOOTLOADER_MODE_HAS_NO_DOWNGRADE + select MCUBOOT_BOOTLOADER_NO_DOWNGRADE + help + MCUboot expects slot0_partition and slot1_partition to exist in DT. + In this mode MCUboot will boot the application with the higher version + from either slot, as long as it has been marked to be boot + next time for test or permanently. In case when application is marked + for test it needs to confirm itself, on the first boot, or it will + be removed and MCUboot will revert to booting previously approved + application. + This mode does not allow freely switching between application + versions, as, once higher version application is approved, it is + not possible to select lower version for boot. + This mode selects MCUBOOT_BOOTLOADER_NO_DOWNGRADE as it is not possible + to downgrade running application, but note that MCUboot may do that + if application with higher version will not get confirmed. endchoice # MCUBOOT_BOOTLOADER_MODE +config MCUBOOT_BOOTLOADER_MODE_HAS_NO_DOWNGRADE + bool + help + Selected mode supports downgrade prevention, where you cannot switch to + an application with lower version than the currently running application. + +if MCUBOOT_BOOTLOADER_MODE_HAS_NO_DOWNGRADE +config MCUBOOT_BOOTLOADER_NO_DOWNGRADE + bool "MCUboot mode has downgrade prevention enabled" + help + Selected MCUboot mode has downgrade prevention enabled, where you are not + able to change back to image with lower version number. + This options should be selected when MCUboot has been built with + MCUBOOT_DOWNGRADE_PREVENTION option enabled. +endif + +endmenu # On board MCUboot operation mode + endif # BOOTLOADER_MCUBOOT menuconfig MCUBOOT_BOOTUTIL_LIB @@ -208,4 +271,13 @@ config BOOT_IMAGE_ACCESS_HOOKS It is up to the application project to add source file which implements hooks to the build. +if MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP_WITH_REVERT + +config MCUBOOT_BOOTUTIL_LIB_FOR_DIRECT_XIP + bool + help + Adds support for setting for test and confirming images + when bootloader is in DirectXIP-revert mode. +endif + endif # MCUBOOT_BOOTUTIL_LIB diff --git a/modules/hal_nordic/Kconfig b/modules/hal_nordic/Kconfig index f842d2cb646..44c12e88685 100644 --- a/modules/hal_nordic/Kconfig +++ b/modules/hal_nordic/Kconfig @@ -205,13 +205,39 @@ endif endmenu # NRF_802154_SER_HOST || NRF_802154_SER_RADIO +if NRF_802154_RADIO_DRIVER || NRF_802154_SERIALIZATION + config NRF_802154_CARRIER_FUNCTIONS bool "nRF 802.15.4 carrier functions" - depends on NRF_802154_RADIO_DRIVER || NRF_802154_SERIALIZATION help This option enables functions such as modulated carrier and continuous carrier. If this option is modified on a multicore SoC, its remote counterpart must be set to the exact same value. +choice NRF_802154_ASSERT_CHOICE + prompt "nRF 802.15.4 assert implementation" + default NRF_802154_ASSERT_ZEPHYR_MINIMAL + +config NRF_802154_ASSERT_ZEPHYR_MINIMAL + bool "nRF 802.15.4 minimal assertions" + help + This option provides minimal run-time checking of the nRF 802.15.4 Radio Driver's operation, + even if kernel-wide CONFIG_ASSERT is disabled. In case of an abnormal condition the function + `nrf_802154_assert_handler()` is called. File and line debug information are not provided + to save memory of the image file. Default implementation of the `nrf_802154_assert_handler` + involves a call to `k_panic`/`k_oops` and allows further tweaking of the behavior. + You can also provide your own implementation of `nrf_802154_assert_handler`. + +config NRF_802154_ASSERT_ZEPHYR + bool "nRF 802.15.4 Radio Driver assertions as Zephyr's standard __ASERT_NO_MSG" + help + The run-time checking of the nRF 802.15.4 Radio Driver depends fully on the configuration + of the `__ASSERT_NO_MSG` macro, including the ability to completely turn off the run-time + checking. + +endchoice # NRF_802154_ASSERT_CHOICE + +endif # NRF_802154_RADIO_DRIVER || NRF_802154_SERIALIZATION + endmenu # HAS_NORDIC_DRIVERS rsource "nrfx/Kconfig" diff --git a/modules/hal_nordic/nrf_802154/CMakeLists.txt b/modules/hal_nordic/nrf_802154/CMakeLists.txt index c338981b651..cd5ace0b278 100644 --- a/modules/hal_nordic/nrf_802154/CMakeLists.txt +++ b/modules/hal_nordic/nrf_802154/CMakeLists.txt @@ -95,6 +95,11 @@ endif() if (CONFIG_NRF_802154_RADIO_DRIVER OR CONFIG_NRF_802154_SERIALIZATION) target_compile_definitions(zephyr-802154-interface INTERFACE NRF_802154_ENERGY_DETECTED_VERSION=1) + if (CONFIG_NRF_802154_ASSERT_ZEPHYR OR CONFIG_NRF_802154_ASSERT_ZEPHYR_MINIMAL) + target_include_directories(zephyr-802154-interface INTERFACE include) + target_compile_definitions(zephyr-802154-interface INTERFACE NRF_802154_PLATFORM_ASSERT_INCLUDE=\"nrf_802154_assert_zephyr.h\") + target_sources(nrf-802154-platform PRIVATE nrf_802154_assert_handler.c) + endif() endif() set(NRF52_SERIES ${CONFIG_SOC_SERIES_NRF52X}) diff --git a/modules/hal_nordic/nrf_802154/include/nrf_802154_assert_zephyr.h b/modules/hal_nordic/nrf_802154/include/nrf_802154_assert_zephyr.h new file mode 100644 index 00000000000..ecd09de609a --- /dev/null +++ b/modules/hal_nordic/nrf_802154/include/nrf_802154_assert_zephyr.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2023, Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef NRF_802154_ASSERT_ZEPHYR_H__ +#define NRF_802154_ASSERT_ZEPHYR_H__ + +#if defined(CONFIG_NRF_802154_ASSERT_ZEPHYR) + +#include + +#define NRF_802154_ASSERT(condition) __ASSERT_NO_MSG(condition) + +#elif defined(CONFIG_NRF_802154_ASSERT_ZEPHYR_MINIMAL) + +extern void nrf_802154_assert_handler(void); + +#define NRF_802154_ASSERT(condition) \ + do { \ + if (!(condition)) { \ + nrf_802154_assert_handler(); \ + } \ + } while (0) + +#endif /* CONFIG_NRF_802154_ASSERT_ZEPHYR_MINIMAL */ + +#endif /* NRF_802154_ASSERT_ZEPHYR_H__*/ diff --git a/modules/hal_nordic/nrf_802154/nrf_802154_assert_handler.c b/modules/hal_nordic/nrf_802154/nrf_802154_assert_handler.c new file mode 100644 index 00000000000..14d964724c6 --- /dev/null +++ b/modules/hal_nordic/nrf_802154/nrf_802154_assert_handler.c @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "nrf_802154_assert_zephyr.h" + +#if defined(CONFIG_NRF_802154_ASSERT_ZEPHYR_MINIMAL) + +__weak void nrf_802154_assert_handler(void) +{ +#ifdef CONFIG_USERSPACE + /* User threads aren't allowed to induce kernel panics; generate + * an oops instead. + */ + if (k_is_user_context()) { + k_oops(); + } +#endif + + k_panic(); +} + +#endif /* CONFIG_NRF_802154_ASSERT_ZEPHYR_MINIMAL */ diff --git a/modules/hal_nordic/nrf_802154/serialization/platform/nrf_802154_spinel_backend_ipc.c b/modules/hal_nordic/nrf_802154/serialization/platform/nrf_802154_spinel_backend_ipc.c index 06ad1f003e6..b2629eef67b 100644 --- a/modules/hal_nordic/nrf_802154/serialization/platform/nrf_802154_spinel_backend_ipc.c +++ b/modules/hal_nordic/nrf_802154/serialization/platform/nrf_802154_spinel_backend_ipc.c @@ -9,6 +9,7 @@ #include #include +#include "nrf_802154.h" #include "nrf_802154_spinel_backend_callouts.h" #include "nrf_802154_serialization_error.h" #include "../../spinel_base/spinel.h" @@ -71,9 +72,11 @@ nrf_802154_ser_err_t nrf_802154_backend_init(void) } /* Send packet thread details */ -#define RING_BUFFER_LEN 16 #define SEND_THREAD_STACK_SIZE 1024 +/* Make the ring buffer long enough to hold all notifications that the driver can produce */ +#define RING_BUFFER_LEN (NRF_802154_MAX_PENDING_NOTIFICATIONS + 1) + static K_SEM_DEFINE(send_sem, 0, RING_BUFFER_LEN); K_THREAD_STACK_DEFINE(send_thread_stack, SEND_THREAD_STACK_SIZE); struct k_thread send_thread_data; diff --git a/modules/hal_nordic/nrfx/CMakeLists.txt b/modules/hal_nordic/nrfx/CMakeLists.txt index 5842ae45ad5..ca325c31770 100644 --- a/modules/hal_nordic/nrfx/CMakeLists.txt +++ b/modules/hal_nordic/nrfx/CMakeLists.txt @@ -109,3 +109,21 @@ zephyr_library_sources_ifdef(CONFIG_NRFX_WDT ${SRC_DIR}/nrfx_wdt.c) if(CONFIG_NRFX_TWI OR CONFIG_NRFX_TWIM) zephyr_library_sources(${SRC_DIR}/nrfx_twi_twim.c) endif() + +# Inject HAL "CONFIG_NFCT_PINS_AS_GPIOS" definition if user requests to +# configure the NFCT pins as GPIOS. Do the same with "CONFIG_GPIO_AS_PINRESET" +# to configure the reset GPIO as nRESET. This way, the HAL will take care of +# doing the proper configuration sequence during system init + +dt_nodelabel(uicr_path NODELABEL "uicr") +if(DEFINED uicr_path) + dt_prop(nfct_pins_as_gpios PATH ${uicr_path} PROPERTY "nfct-pins-as-gpios") + if(${nfct_pins_as_gpios}) + zephyr_library_compile_definitions(CONFIG_NFCT_PINS_AS_GPIOS) + endif() + + dt_prop(gpio_as_nreset PATH ${uicr_path} PROPERTY "gpio-as-nreset") + if(${gpio_as_nreset}) + zephyr_library_compile_definitions(CONFIG_GPIO_AS_PINRESET) + endif() +endif() diff --git a/modules/hal_nordic/nrfx/nrfx_config.h b/modules/hal_nordic/nrfx/nrfx_config.h index 4b7cabd7a98..29051ccf754 100644 --- a/modules/hal_nordic/nrfx/nrfx_config.h +++ b/modules/hal_nordic/nrfx/nrfx_config.h @@ -117,6 +117,12 @@ #ifdef CONFIG_NRFX_GPIOTE #define NRFX_GPIOTE_ENABLED 1 +#if (defined(CONFIG_SOC_SERIES_NRF91X) || defined(CONFIG_SOC_SERIES_NRF53X)) \ + && defined(NRF_TRUSTZONE_NONSECURE) +#define NRFX_GPIOTE1_ENABLED 1 +#else +#define NRFX_GPIOTE0_ENABLED 1 +#endif #endif #ifdef CONFIG_NRFX_GPIOTE_LOG #define NRFX_GPIOTE_CONFIG_LOG_ENABLED 1 diff --git a/modules/mbedtls/CMakeLists.txt b/modules/mbedtls/CMakeLists.txt index f061605c027..c1c8e076b83 100644 --- a/modules/mbedtls/CMakeLists.txt +++ b/modules/mbedtls/CMakeLists.txt @@ -76,7 +76,6 @@ zephyr_interface_library_named(mbedTLS) ${ZEPHYR_CURRENT_MODULE_DIR}/library/mps_reader.c ${ZEPHYR_CURRENT_MODULE_DIR}/library/mps_trace.c ${ZEPHYR_CURRENT_MODULE_DIR}/library/poly1305.c - ${ZEPHYR_CURRENT_MODULE_DIR}/library/psa_util.c ${ZEPHYR_CURRENT_MODULE_DIR}/library/ripemd160.c ${ZEPHYR_CURRENT_MODULE_DIR}/library/rsa_alt_helpers.c ${ZEPHYR_CURRENT_MODULE_DIR}/library/rsa.c @@ -89,6 +88,10 @@ zephyr_interface_library_named(mbedTLS) zephyr_init.c ) + if (NOT DEFINED ZEPHYR_NRF_MODULE_DIR) + list(APPEND mbedtls_base_src ${ZEPHYR_CURRENT_MODULE_DIR}/library/psa_util.c) + endif() + zephyr_library_sources(${mbedtls_base_src}) zephyr_library_sources_ifdef(CONFIG_MBEDTLS_DEBUG debug.c) diff --git a/modules/mbedtls/Kconfig b/modules/mbedtls/Kconfig index 67277fb24c4..2a05ee28d36 100644 --- a/modules/mbedtls/Kconfig +++ b/modules/mbedtls/Kconfig @@ -5,6 +5,7 @@ config ZEPHYR_MBEDTLS_MODULE bool + config MBEDTLS_PROMPTLESS bool help @@ -13,7 +14,6 @@ config MBEDTLS_PROMPTLESS mbed TLS menu prompt and instead handle the selection of MBEDTLS from dependent sub-configurations and thus prevent stuck symbol behavior. - menuconfig MBEDTLS bool "mbed TLS Support" if !MBEDTLS_PROMPTLESS help @@ -27,6 +27,7 @@ choice MBEDTLS_IMPLEMENTATION config MBEDTLS_BUILTIN bool "Use Zephyr in-tree mbedTLS version" + depends on ! DISABLE_MBEDTLS_BUILTIN help Link with mbedTLS sources included with Zephyr distribution. Included mbedTLS version is well integrated with and supported @@ -40,6 +41,11 @@ config MBEDTLS_LIBRARY endchoice +# subsystems cannot deselect MBEDTLS_BUILTIN, but they can select +# DISABLE_MBEDTLS_BUILTIN. +config DISABLE_MBEDTLS_BUILTIN + bool + config CUSTOM_MBEDTLS_CFG_FILE bool "Custom mbed TLS configuration file" help @@ -233,3 +239,6 @@ config APP_LINK_WITH_MBEDTLS issues for 'app'. endif # MBEDTLS + +# Add PSA configurations +rsource "Kconfig.psa" diff --git a/modules/mbedtls/Kconfig.psa b/modules/mbedtls/Kconfig.psa new file mode 100644 index 00000000000..42d2a48825b --- /dev/null +++ b/modules/mbedtls/Kconfig.psa @@ -0,0 +1,693 @@ +# +# Copyright (c) 2022 Nordic Semiconductor +# +# SPDX-License-Identifier: Apache-2.0 +# +menu "PSA RNG support" + +config PSA_WANT_GENERATE_RANDOM + bool + prompt "PSA RNG support" + help + Provide random number generator (RNG) support. + +config PSA_WANT_ALG_CTR_DRBG + bool + prompt "PSA RNG using CTR-DRBG as PRNG" + help + Provide random number generator (RNG) using CTR-DRBG as the + pseudo-random number generator (PRNG), seeded by a true random + number generator (TRNG). + +config PSA_WANT_ALG_HMAC_DRBG + bool + prompt "PSA RNG using HMAC-DRBG as PRNG" + help + Provide random number generator (RNG) using HMAC-DRBG as the + pseudo-random number generator (PRNG), seeded by a true random + number generator (TRNG). + +endmenu # RNG support + +menu "PSA key type support" + +config PSA_HAS_KEY_SUPPORT + bool + default y + depends on PSA_WANT_KEY_TYPE_DERIVE || \ + PSA_WANT_KEY_TYPE_HMAC || \ + PSA_WANT_KEY_TYPE_RAW_DATA || \ + PSA_WANT_KEY_TYPE_PASSWORD || \ + PSA_WANT_KEY_TYPE_PASSWORD_HASH || \ + PSA_WANT_KEY_TYPE_PEPPER || \ + PSA_WANT_KEY_TYPE_AES || \ + PSA_WANT_KEY_TYPE_ARIA || \ + PSA_WANT_KEY_TYPE_DES || \ + PSA_WANT_KEY_TYPE_CAMELLIA || \ + PSA_WANT_KEY_TYPE_SM4 || \ + PSA_WANT_KEY_TYPE_ARC4 || \ + PSA_WANT_KEY_TYPE_CHACHA20 || \ + PSA_WANT_KEY_TYPE_ECC_KEY_PAIR || \ + PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY || \ + PSA_WANT_KEY_TYPE_RSA_KEY_PAIR || \ + PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY || \ + PSA_WANT_KEY_TYPE_DH_KEY_PAIR || \ + PSA_WANT_KEY_TYPE_DH_PUBLIC_KEY + +config PSA_WANT_KEY_TYPE_DERIVE + bool "PSA derive key type support" + help + This key type is for high-entropy secrets only. + For low-entropy secrets, password key type should be used instead. + +config PSA_WANT_KEY_TYPE_RAW_DATA + bool "PSA raw data key type support" + help + A "key" of this type cannot be used for any cryptographic operation. + Applications can use this type to store arbitrary data in the keystore. + +config PSA_WANT_KEY_TYPE_HMAC + bool "PSA HMAC key type support" + help + HMAC key. + +config PSA_WANT_KEY_TYPE_PASSWORD + bool "PSA password key type support" + help + A low-entropy secret for password hashing or key derivation. + +config PSA_WANT_KEY_TYPE_PASSWORD_HASH + bool "PSA password hash key type support" + help + A secret value that can be used to verify a password hash. + +config PSA_WANT_KEY_TYPE_PEPPER + bool "PSA pepper key type support" + help + A secret value that can be used when computing a password hash. + +config PSA_WANT_KEY_TYPE_AES + bool "PSA AES key type support" + help + Key for cipher, AEAD or MAC algorithm based on the AES block cipher. + +config PSA_WANT_KEY_TYPE_ARIA + bool "PSA ARIA key type support" + +config PSA_WANT_KEY_TYPE_DES + bool "PSA DES key type support (weak)" + help + Warning: Single DES and 2-key 3DES are weak and strongly deprecated + and are only recommended for decrypting legacy data. + 3-key 3DES is weak and deprecated and is only recommended for use in + legacy protocols. + +config PSA_WANT_KEY_TYPE_CAMELLIA + bool "PSA CAMELLIA key type support" + +config PSA_WANT_KEY_TYPE_SM4 + bool "PSA SM4 key type support" + +config PSA_WANT_KEY_TYPE_ARC4 + bool "PSA ARC4 key type support (weak)" + help + Warning: The ARC4 cipher is weak and deprecated and is only + recommended for use in legacy protocols. + +config PSA_WANT_KEY_TYPE_CHACHA20 + bool "PSA ChaCha20 key type support" + default y + depends on PSA_WANT_ALG_CHACHA20_POLY1305 || \ + PSA_WANT_ALG_STREAM_CIPHER + help + Key for the ChaCha20 stream cipher or the ChaCha20-Poly1305 AEAD algorithm. + +config PSA_WANT_KEY_TYPE_ECC_KEY_PAIR + bool "PSA ECC key pair support" + select PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY + help + Elliptic curve key pair: both the private and public key. + +config PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY + bool "PSA ECC public key support" + help + Elliptic curve public key. + +config PSA_WANT_KEY_TYPE_RSA_KEY_PAIR + bool "PSA RSA key pair type support" + select PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY + help + RSA key pair: both the private and public key. + +config PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY + bool "PSA RSA public key support" + help + RSA public key. + +config PSA_WANT_KEY_TYPE_DH_KEY_PAIR + bool "PSA DH key pair type support" + select PSA_WANT_KEY_TYPE_DH_PUBLIC_KEY + help + Finite-field Diffie-Hellman key pair: both the private key and public key. + +config PSA_WANT_KEY_TYPE_DH_PUBLIC_KEY + bool "PSA DH public key support" + help + Finite-field Diffie-Hellman public key. + +endmenu # PSA Key type support + +menu "PSA AEAD support" + +config PSA_HAS_AEAD_SUPPORT + bool + default y + depends on PSA_WANT_ALG_CCM || \ + PSA_WANT_ALG_GCM || \ + PSA_WANT_ALG_CHACHA20_POLY1305 + help + Prompt-less configuration that states that AEAD is supported. + +config PSA_WANT_ALG_CCM + bool + prompt "PSA CCM support" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_GCM + bool + prompt "PSA GCM support" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_CHACHA20_POLY1305 + bool + prompt "PSA ChaCha20-Poly1305 support" if !PSA_PROMPTLESS + +endmenu # PSA AEAD support + + +menu "PSA MAC support" + +config PSA_HAS_MAC_SUPPORT + bool + default y + depends on PSA_WANT_ALG_CBC_MAC || \ + PSA_WANT_ALG_CMAC || \ + PSA_WANT_ALG_HMAC + help + Prompt-less configuration that states that MAC is supported. + +config PSA_WANT_ALG_CBC_MAC + bool + prompt "PSA CBC-MAC support" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_CMAC + bool + prompt "PSA CMAC support" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_HMAC + bool + prompt "PSA HMAC support" if !PSA_PROMPTLESS + +endmenu # PSA MAC support + + +menu "PSA Hash support" + +config PSA_HAS_HASH_SUPPORT + bool + default y + depends on PSA_WANT_ALG_SHA_1 || \ + PSA_WANT_ALG_SHA_224 || \ + PSA_WANT_ALG_SHA_256 || \ + PSA_WANT_ALG_SHA_384 || \ + PSA_WANT_ALG_SHA_512 || \ + PSA_WANT_ALG_SHA_512_224 || \ + PSA_WANT_ALG_SHA_512_256 || \ + PSA_WANT_ALG_SHA3_224 || \ + PSA_WANT_ALG_SHA3_256 || \ + PSA_WANT_ALG_SHA3_384 || \ + PSA_WANT_ALG_SHA3_512 || \ + PSA_WANT_ALG_SM3 || \ + PSA_WANT_ALG_SHAKE256_512 || \ + PSA_WANT_ALG_RIPEMD160 || \ + PSA_WANT_ALG_MD2 || \ + PSA_WANT_ALG_MD4 || \ + PSA_WANT_ALG_MD5 + help + Prompt-less configuration that states that hash is supported. + +config PSA_WANT_ALG_SHA_1 + bool + prompt "PSA SHA-1 support (weak)" if !PSA_PROMPTLESS + help + Warning: The SHA-1 hash is weak and deprecated and is only recommended + for use in legacy protocols. + +config PSA_WANT_ALG_SHA_224 + bool + prompt "PSA SHA-224 support" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_SHA_256 + bool + prompt "PSA SHA-256 support" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_SHA_384 + bool + prompt "PSA SHA-384 support" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_SHA_512 + bool + prompt "PSA SHA-512 support" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_SHA_512_224 + bool + prompt "PSA SHA-512/224 support" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_SHA_512_256 + bool + prompt "PSA SHA-512/256 support" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_SHA3_224 + bool + prompt "PSA SHA3-224 support" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_SHA3_256 + bool + prompt "PSA SHA3-256 support" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_SHA3_384 + bool + prompt "PSA SHA3-384 support" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_SHA3_512 + bool + prompt "PSA SHA3-512 support" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_SM3 + bool + prompt "PSA SM3 support" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_SHAKE256_512 + bool + prompt "PSA SHAKE256 512 bits support" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_RIPEMD160 + bool + prompt "PSA RIPEMD-160 support" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_MD2 + bool + prompt "PSA MD2 support (weak)" if !PSA_PROMPTLESS + help + Warning: The MD2 hash is weak and deprecated and is only recommended + for use in legacy protocols. + +config PSA_WANT_ALG_MD4 + bool + prompt "PSA MD4 support (weak)" if !PSA_PROMPTLESS + help + Warning: The MD4 hash is weak and deprecated and is only recommended + for use in legacy protocols. + +config PSA_WANT_ALG_MD5 + bool + prompt "PSA MD5 support (weak)" if !PSA_PROMPTLESS + help + Warning: The MD5 hash is weak and deprecated and is only recommended + for use in legacy protocols. + +endmenu # PSA Hash support + +menu "PSA Cipher support" + +config PSA_HAS_CIPHER_SUPPORT + bool + default y + depends on PSA_WANT_ALG_ECB_NO_PADDING || \ + PSA_WANT_ALG_CBC_NO_PADDING || \ + PSA_WANT_ALG_CBC_PKCS7 || \ + PSA_WANT_ALG_CCM_STAR_NO_TAG || \ + PSA_WANT_ALG_CFB || \ + PSA_WANT_ALG_CTR || \ + PSA_WANT_ALG_OFB || \ + PSA_WANT_ALG_XTS || \ + PSA_WANT_ALG_STREAM_CIPHER + help + Prompt-less configuration that states that cipher is supported. + +config PSA_WANT_ALG_ECB_NO_PADDING + bool + prompt "PSA ECB block cipher mode support (with no padding)" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_CBC_NO_PADDING + bool + prompt "PSA CBC block cipher mode support (with no padding)" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_CBC_PKCS7 + bool + prompt "PSA CBC block cipher mode support (with PKCS#7 padding)" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_CFB + bool + prompt "PSA stream cipher using CFB block cipher mode support" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_CTR + bool + prompt "PSA stream cipher using CTR block cipher mode support" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_OFB + bool + prompt "PSA stream cipher using OFB block cipher mode support" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_XTS + bool + prompt "PSA XTS block cipher mode support" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_CCM_STAR_NO_TAG + bool + prompt "PSA CCM* with no tag support" if !PSA_PROMPTLESS + help + Unauthenticated version of CCM. Uses the cipher API instead of the AEAD API. + +config PSA_WANT_ALG_STREAM_CIPHER + bool + prompt "PSA stream cipher support" if !PSA_PROMPTLESS + +endmenu # PSA Cipher Support + +menu "PSA Key agreement support" + +config PSA_HAS_KEY_AGREEMENT + bool + default y + depends on PSA_WANT_ALG_ECDH + depends on PSA_WANT_ALG_FFDH + help + Promt-less configuration that states that key agreement is supported. + +config PSA_WANT_ALG_ECDH + bool + prompt "PSA ECDH support" if !PSA_PROMPTLESS + + +config PSA_WANT_ALG_FFDH + bool + prompt "PSA FFDH support" if !PSA_PROMPTLESS + +endmenu # PSA Key agreement support + +menu "PSA Key derivation support" + +config PSA_HAS_KEY_DERIVATION + bool + default y + depends on PSA_WANT_ALG_HKDF || \ + PSA_WANT_ALG_HKDF_EXPAND || \ + PSA_WANT_ALG_HKDF_EXTRACT || \ + PSA_WANT_ALG_PBKDF2_HMAC || \ + PSA_WANT_ALG_PBKDF2_AES_CMAC_PRF_128 || \ + PSA_WANT_ALG_TLS12_PRF || \ + PSA_WANT_ALG_TLS12_PSK_TO_MS || \ + PSA_WANT_ALG_TLS12_ECJPAKE_TO_PMS + help + Prompt-less configuration that states that key derivation is supported. + +config PSA_WANT_ALG_HKDF + bool + prompt "PSA HKDF support" if !PSA_PROMPTLESS + depends on PSA_WANT_ALG_HMAC + +config PSA_WANT_ALG_HKDF_EXTRACT + bool + prompt "PSA HKDF extract support" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_HKDF_EXPAND + bool + prompt "PSA HKDF expand support" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_PBKDF2_HMAC + bool + prompt "PSA PBKDF2 HMAC support" if !PSA_PROMPTLESS + depends on PSA_WANT_ALG_HMAC + +config PSA_WANT_ALG_PBKDF2_AES_CMAC_PRF_128 + bool + prompt "PSA PBKDF2-AES-CMAC-PRF-128 support" if !PSA_PROMPTLESS + depends on PSA_WANT_ALG_CMAC + +config PSA_WANT_ALG_TLS12_PRF + bool + prompt "PSA PRF support (TLS1.2)" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_TLS12_PSK_TO_MS + bool + prompt "PSA TLS 1.2 PSK to MS support" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_TLS12_ECJPAKE_TO_PMS + bool + prompt "PSA TLS 1.2 EC J-PAKE to PMS support" if !PSA_PROMPTLESS + +endmenu # PSA Key derivation support + + +menu "PSA Asymmetric support" + +config PSA_HAS_ASYM_ENCRYPT_SUPPORT + bool + default y + depends on PSA_WANT_ALG_RSA_OAEP || \ + PSA_WANT_ALG_RSA_PKCS1V15_CRYPT + help + Prompt-less configuration that states that asymmetric encryption + is supported. + + +config PSA_HAS_ASYM_SIGN_SUPPORT + bool + default y + depends on PSA_WANT_ALG_DETERMINISTIC_ECDSA || \ + PSA_WANT_ALG_ECDSA || \ + PSA_WANT_ALG_ECDSA_ANY || \ + PSA_WANT_ALG_PURE_EDDSA || \ + PSA_WANT_ALG_ED25519PH || \ + PSA_WANT_ALG_ED448PH || \ + PSA_WANT_ALG_RSA_PKCS1V15_SIGN || \ + PSA_WANT_ALG_RSA_PKCS1V15_SIGN_RAW || \ + PSA_WANT_ALG_RSA_PSS || \ + PSA_WANT_ALG_RSA_PSS_ANY_SALT + help + Prompt-less configuration that states that asymmetric signing + is supported. + +config PSA_WANT_ALG_ECDSA + bool + prompt "PSA ECDSA support" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_ECDSA_ANY + bool + prompt "PSA ECDSA support, without hashing" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_DETERMINISTIC_ECDSA + bool + prompt "PSA ECDSA support (deterministic mode)" if !PSA_PROMPTLESS + +menu "Elliptic Curve type support" + +config PSA_WANT_ECC_BRAINPOOL_P_R1_160 + bool + prompt "PSA ECC BrainpoolP160r1 support (weak)" if !PSA_PROMPTLESS + help + Warning: The 160-bit curve brainpoolP160r1 is weak and deprecated and + is only recommended for use in legacy protocols. + +config PSA_WANT_ECC_BRAINPOOL_P_R1_192 + bool + prompt "PSA ECC BrainpoolP192r1 support" if !PSA_PROMPTLESS + +config PSA_WANT_ECC_BRAINPOOL_P_R1_224 + bool + prompt "PSA ECC BrainpoolP224r1 support" if !PSA_PROMPTLESS + +config PSA_WANT_ECC_BRAINPOOL_P_R1_256 + bool + prompt "PSA ECC BrainpoolP256r1 support" if !PSA_PROMPTLESS + +config PSA_WANT_ECC_BRAINPOOL_P_R1_320 + bool + prompt "PSA ECC BrainpoolP320r1 support" if !PSA_PROMPTLESS + +config PSA_WANT_ECC_BRAINPOOL_P_R1_384 + bool + prompt "PSA ECC BrainpoolP384r1 support" if !PSA_PROMPTLESS + +config PSA_WANT_ECC_BRAINPOOL_P_R1_512 + bool + prompt "PSA ECC BrainpoolP512r1 support" if !PSA_PROMPTLESS + +config PSA_WANT_ECC_MONTGOMERY_255 + bool + prompt "PSA ECC Curve25519 (X25519) support" if !PSA_PROMPTLESS + +config PSA_WANT_ECC_MONTGOMERY_448 + bool + prompt "PSA ECC Curve448 (X448) support" if !PSA_PROMPTLESS + +config PSA_WANT_ECC_TWISTED_EDWARDS_255 + bool + prompt "PSA ECC Edwards25519 (Ed25519) support" if !PSA_PROMPTLESS + +config PSA_WANT_ECC_TWISTED_EDWARDS_448 + bool + prompt "PSA ECC Edwards448 (Ed448) support" if !PSA_PROMPTLESS + +config PSA_WANT_ECC_SECP_K1_192 + bool + prompt "PSA ECC secp192k1 support" if !PSA_PROMPTLESS + +config PSA_WANT_ECC_SECP_K1_224 + bool + prompt "PSA ECC secp224k1 support" if !PSA_PROMPTLESS + +config PSA_WANT_ECC_SECP_K1_256 + bool + prompt "PSA ECC secp256k1 support" if !PSA_PROMPTLESS + +config PSA_WANT_ECC_SECP_R1_192 + bool + prompt "PSA ECC secp192r1 support" if !PSA_PROMPTLESS + +config PSA_WANT_ECC_SECP_R1_224 + bool + prompt "PSA ECC secp224r1 support" if !PSA_PROMPTLESS + +config PSA_WANT_ECC_SECP_R1_256 + bool + prompt "PSA ECC secp256r1 support" if !PSA_PROMPTLESS + +config PSA_WANT_ECC_SECP_R1_384 + bool + prompt "PSA ECC secp384r1 support" if !PSA_PROMPTLESS + +config PSA_WANT_ECC_SECP_R1_521 + bool + prompt "PSA ECC secp521r1 support" if !PSA_PROMPTLESS + +config PSA_WANT_ECC_SECP_R2_160 + bool + prompt "PSA ECC secp160r2 support (weak)" if !PSA_PROMPTLESS + help + Warning: his family of curves is weak and deprecated. + +config PSA_WANT_ECC_SECT_K1_163 + bool + prompt "PSA ECC sect163k1 support (weak)" if !PSA_PROMPTLESS + help + Warning: The 163-bit curve sect163k1 is weak and deprecated and is + only recommended for use in legacy protocols. + +config PSA_WANT_ECC_SECT_K1_233 + bool + prompt "PSA ECC sect233k1 support" if !PSA_PROMPTLESS + +config PSA_WANT_ECC_SECT_K1_239 + bool + prompt "PSA ECC sect239k1 support" if !PSA_PROMPTLESS + +config PSA_WANT_ECC_SECT_K1_283 + bool + prompt "PSA ECC sect283k1 support" if !PSA_PROMPTLESS + +config PSA_WANT_ECC_SECT_K1_409 + bool + prompt "PSA ECC sect409k1 support" if !PSA_PROMPTLESS + +config PSA_WANT_ECC_SECT_K1_571 + bool + prompt "PSA ECC sect571k1 support" if !PSA_PROMPTLESS + +config PSA_WANT_ECC_SECT_R1_163 + bool + prompt "PSA ECC sect163r1 support (weak)" if !PSA_PROMPTLESS + help + Warning: The 163-bit curve sect163r1 is weak and deprecated and is + only recommended for use in legacy protocols. + +config PSA_WANT_ECC_SECT_R1_233 + bool + prompt "PSA ECC sect233r1 support" if !PSA_PROMPTLESS + +config PSA_WANT_ECC_SECT_R1_283 + bool + prompt "PSA ECC sect283r1 support" if !PSA_PROMPTLESS + +config PSA_WANT_ECC_SECT_R1_409 + bool + prompt "PSA ECC sect409r1 support" if !PSA_PROMPTLESS + +config PSA_WANT_ECC_SECT_R1_571 + bool + prompt "PSA ECC sect571r1 support" if !PSA_PROMPTLESS + +config PSA_WANT_ECC_SECT_R2_163 + bool + prompt "PSA ECC sect163r2 support (weak)" if !PSA_PROMPTLESS + help + Warning: The 163-bit curve sect163r2 is weak and deprecated and is + only recommended for use in legacy protocols. + +config PSA_WANT_ECC_FRP_V1_256 + bool + prompt "PSA ECC FRP256v1 support" if !PSA_PROMPTLESS + +endmenu # Elliptic Curve type support + +config PSA_WANT_ALG_RSA_OAEP + bool + prompt "PSA RSA OAEP asymmetric encryption support" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_RSA_PKCS1V15_CRYPT + bool + prompt "PSA RSA PKCS#1 v1.5 asymmetric encryption support" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_RSA_PKCS1V15_SIGN + bool + prompt "PSA RSA PKCS#1 v1.5 message signature support, with hashing" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_RSA_PKCS1V15_SIGN_RAW + bool + prompt "PSA RSA raw PKCS#1 v1.5 message signature support, without hashing)" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_RSA_PSS + bool + prompt "PSA RSA PSS message signature support" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_RSA_PSS_ANY_SALT + bool + prompt "PSA RSA PSS message signature support, any salt length" if !PSA_PROMPTLESS + +endmenu # PSA Asymmetric support + +config PSA_WANT_ALG_JPAKE + bool + prompt "PSA EC J-PAKE support" if !PSA_PROMPTLESS + select EXPERIMENTAL + +config PSA_WANT_ALG_SPAKE2P + bool + prompt "PSA SPAKE2+ support" if !PSA_PROMPTLESS + select EXPERIMENTAL + +config PSA_WANT_ALG_SRP_6 + bool + prompt "PSA SRP-6 support" if !PSA_PROMPTLESS + select EXPERIMENTAL + +config PSA_WANT_ALG_PURE_EDDSA + bool + prompt "PSA PURE_EDDSA support" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_ED25519PH + bool + prompt "PSA ED25519PH support" if !PSA_PROMPTLESS + +config PSA_WANT_ALG_ED448PH + bool + prompt "PSA ED448PH support" if !PSA_PROMPTLESS diff --git a/modules/mbedtls/Kconfig.tls-generic b/modules/mbedtls/Kconfig.tls-generic index 3c0e17c0fe3..d629c1b1d2a 100644 --- a/modules/mbedtls/Kconfig.tls-generic +++ b/modules/mbedtls/Kconfig.tls-generic @@ -9,6 +9,8 @@ menu "TLS configuration" menu "Supported TLS version" +if !(NRF_SECURITY || NORDIC_SECURITY_BACKEND) + config MBEDTLS_TLS_VERSION_1_0 bool "Support for TLS 1.0" select MBEDTLS_CIPHER @@ -33,6 +35,8 @@ config MBEDTLS_DTLS bool "Support for DTLS" depends on MBEDTLS_TLS_VERSION_1_1 || MBEDTLS_TLS_VERSION_1_2 +endif + config MBEDTLS_SSL_EXPORT_KEYS bool "Support for exporting SSL key block and master secret" depends on MBEDTLS_TLS_VERSION_1_0 || MBEDTLS_TLS_VERSION_1_1 || MBEDTLS_TLS_VERSION_1_2 @@ -47,6 +51,8 @@ menu "Ciphersuite configuration" comment "Supported key exchange modes" +if !(NRF_SECURITY || NORDIC_SECURITY_BACKEND) + config MBEDTLS_KEY_EXCHANGE_ALL_ENABLED bool "All available ciphersuite modes" select MBEDTLS_KEY_EXCHANGE_PSK_ENABLED @@ -81,6 +87,8 @@ config MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED || \ MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED +endif + config MBEDTLS_PSK_MAX_LEN int "Max size of TLS pre-shared keys" default 32 @@ -88,6 +96,8 @@ config MBEDTLS_PSK_MAX_LEN help Max size of TLS pre-shared keys, in bytes. +if !(NRF_SECURITY || NORDIC_SECURITY_BACKEND) + config MBEDTLS_KEY_EXCHANGE_RSA_ENABLED bool "RSA-only based ciphersuite modes" default y if !NET_L2_OPENTHREAD @@ -202,6 +212,7 @@ config MBEDTLS_ECP_NIST_OPTIM bool "NSIT curves optimization" endif +endif # !(NRF_SECURITY || NORDIC_SECURITY_BACKEND) comment "Supported hash" @@ -226,6 +237,8 @@ config MBEDTLS_HASH_SHA512_ENABLED comment "Supported cipher modes" +if !(NRF_SECURITY || NORDIC_SECURITY_BACKEND) + config MBEDTLS_CIPHER_ALL_ENABLED bool "All available ciphers" select MBEDTLS_CIPHER_AES_ENABLED @@ -299,8 +312,12 @@ config MBEDTLS_CHACHAPOLY_AEAD_ENABLED bool "ChaCha20-Poly1305 AEAD algorithm" depends on MBEDTLS_CIPHER_CHACHA20_ENABLED || MBEDTLS_MAC_POLY1305_ENABLED +endif + comment "Supported message authentication methods" +if !(NRF_SECURITY || NORDIC_SECURITY_BACKEND) + config MBEDTLS_MAC_ALL_ENABLED bool "All available MAC methods" select MBEDTLS_MAC_MD4_ENABLED @@ -349,10 +366,14 @@ config MBEDTLS_MAC_CMAC_ENABLED bool "CMAC (Cipher-based Message Authentication Code) mode for block ciphers." depends on MBEDTLS_CIPHER_AES_ENABLED || MBEDTLS_CIPHER_DES_ENABLED +endif + endmenu comment "Random number generators" +if !(NRF_SECURITY || NORDIC_SECURITY_BACKEND) + config MBEDTLS_CTR_DRBG_ENABLED bool "CTR_DRBG AES-256-based random generator" depends on MBEDTLS_CIPHER_AES_ENABLED @@ -362,14 +383,20 @@ config MBEDTLS_HMAC_DRBG_ENABLED bool "HMAC_DRBG random generator" select MBEDTLS_MD +endif + comment "Other configurations" config MBEDTLS_CIPHER bool "generic cipher layer." +if !(NRF_SECURITY || NORDIC_SECURITY_BACKEND) + config MBEDTLS_MD bool "generic message digest layer." +endif + config MBEDTLS_GENPRIME_ENABLED bool "prime-number generation code." @@ -387,11 +414,15 @@ config MBEDTLS_HAVE_ASM of asymmetric cryptography, however this might have an impact on the code size. +if !(NRF_SECURITY || NORDIC_SECURITY_BACKEND) + config MBEDTLS_ENTROPY_ENABLED bool "MbedTLS generic entropy pool" depends on MBEDTLS_MAC_SHA256_ENABLED || MBEDTLS_MAC_SHA384_ENABLED || MBEDTLS_MAC_SHA512_ENABLED default y if MBEDTLS_ZEPHYR_ENTROPY +endif + config MBEDTLS_OPENTHREAD_OPTIMIZATIONS_ENABLED bool "MbedTLS optimizations for OpenThread" depends on NET_L2_OPENTHREAD diff --git a/modules/mbedtls/configs/config-tls-generic.h b/modules/mbedtls/configs/config-tls-generic.h index 2268571abe0..7b437c23dcb 100644 --- a/modules/mbedtls/configs/config-tls-generic.h +++ b/modules/mbedtls/configs/config-tls-generic.h @@ -500,4 +500,8 @@ #include "mbedtls/check_config.h" #endif +#if defined(CONFIG_NRF_CC3XX_PLATFORM) +#define MBEDTLS_PLATFORM_ZEROIZE_ALT +#endif + #endif /* MBEDTLS_CONFIG_H */ diff --git a/modules/mbedtls/zephyr_init.c b/modules/mbedtls/zephyr_init.c index 70f7783e2ee..eec9d991c9b 100644 --- a/modules/mbedtls/zephyr_init.c +++ b/modules/mbedtls/zephyr_init.c @@ -45,7 +45,7 @@ static void init_heap(void) #define init_heap(...) #endif /* CONFIG_MBEDTLS_ENABLE_HEAP && MBEDTLS_MEMORY_BUFFER_ALLOC_C */ -#if defined(CONFIG_MBEDTLS_ZEPHYR_ENTROPY) +#if defined(CONFIG_MBEDTLS_ZEPHYR_ENTROPY) && !defined(CONFIG_NRF_CC3XX_PLATFORM) static const struct device *const entropy_dev = DEVICE_DT_GET_OR_NULL(DT_CHOSEN(zephyr_entropy)); diff --git a/modules/openthread/CMakeLists.txt b/modules/openthread/CMakeLists.txt index 67acbe00dec..3796ec8f177 100644 --- a/modules/openthread/CMakeLists.txt +++ b/modules/openthread/CMakeLists.txt @@ -58,6 +58,12 @@ else() set(OT_BACKBONE_ROUTER_MULTICAST_ROUTING OFF CACHE BOOL "Enable BBR MR support" FORCE) endif() +if(CONFIG_OPENTHREAD_BLE_TCAT) + set(OT_BLE_TCAT ON CACHE BOOL "Enable BLE TCAT support" FORCE) +else() + set(OT_BLE_TCAT OFF CACHE BOOL "Enable BLE TCAT support" FORCE) +endif() + if(CONFIG_OPENTHREAD_BORDER_AGENT) set(OT_BORDER_AGENT ON CACHE BOOL "Enable Border Agent" FORCE) else() @@ -148,12 +154,24 @@ else() set(OT_CSL_RECEIVER OFF CACHE BOOL "Enable CSL receiver feature for Thread 1.2" FORCE) endif() +if(CONFIG_OPENTHREAD_CSL_RECEIVER_LOCAL_TIME_SYNC) + set(OT_CSL_RECEIVER_LOCAL_TIME_SYNC ON CACHE BOOL "Use local time for CSL sync" FORCE) +else() + set(OT_CSL_RECEIVER_LOCAL_TIME_SYNC OFF CACHE BOOL "Use local time for CSL sync" FORCE) +endif() + if(CONFIG_OPENTHREAD_DATASET_UPDATER) set(OT_DATASET_UPDATER ON CACHE BOOL "Enable Dataset updater" FORCE) else() set(OT_DATASET_UPDATER OFF CACHE BOOL "Enable Dataset updater" FORCE) endif() +if(CONFIG_OPENTHREAD_DEVICE_PROP_LEADER_WEIGHT) + set(OT_DEVICE_PROP_LEADER_WEIGHT ON CACHE BOOL "Enable device props for leader weight" FORCE) +else() + set(OT_DEVICE_PROP_LEADER_WEIGHT OFF CACHE BOOL "Enable device props for leader weight" FORCE) +endif() + if(CONFIG_OPENTHREAD_DHCP6_CLIENT) set(OT_DHCP6_CLIENT ON CACHE BOOL "Enable DHCPv6 Client" FORCE) else() @@ -274,6 +292,12 @@ else() set(OT_LINK_METRICS_INITIATOR OFF CACHE BOOL "Enable Link Metrics initiator for Thread 1.2" FORCE) endif() +if(CONFIG_OPENTHREAD_LINK_METRICS_MANAGER) + set(OT_LINK_METRICS_MANAGER ON CACHE BOOL "Enable Link Metrics manager for Thread 1.2" FORCE) +else() + set(OT_LINK_METRICS_MANAGER OFF CACHE BOOL "Enable Link Metrics manager for Thread 1.2" FORCE) +endif() + if(CONFIG_OPENTHREAD_LINK_METRICS_SUBJECT) set(OT_LINK_METRICS_SUBJECT ON CACHE BOOL "Enable Link Metrics subject for Thread 1.2" FORCE) else() @@ -316,6 +340,12 @@ else() set(OT_MLR OFF CACHE BOOL "Enable Multicast Listener Registration feature for Thread 1.2" FORCE) endif() +if(CONFIG_OPENTHREAD_MULTIPAN_RCP) + set(OT_MULTIPAN_RCP ON CACHE BOOL "Enable Multi-PAN RCP" FORCE) +else() + set(OT_MULTIPAN_RCP OFF CACHE BOOL "Enable Multi-PAN RCP" FORCE) +endif() + if(CONFIG_OPENTHREAD_MULTIPLE_INSTANCE) set(OT_MULTIPLE_INSTANCE ON CACHE BOOL "Enable multiple instances" FORCE) else() diff --git a/modules/openthread/Kconfig.features b/modules/openthread/Kconfig.features index 61b0efc921b..2be5332cde3 100644 --- a/modules/openthread/Kconfig.features +++ b/modules/openthread/Kconfig.features @@ -39,6 +39,10 @@ config OPENTHREAD_BACKBONE_ROUTER_DUA_NDPROXYING config OPENTHREAD_BACKBONE_ROUTER_MULTICAST_ROUTING bool "BBR MR support" +config OPENTHREAD_BLE_TCAT + bool "BLE TCAT support" + select EXPERIMENTAL + config OPENTHREAD_BORDER_AGENT bool "Border Agent support" @@ -92,6 +96,23 @@ config OPENTHREAD_CSL_RECEIVER help Enable CSL Receiver support for Thread 1.2 +config OPENTHREAD_CSL_RECEIVER_LOCAL_TIME_SYNC + bool "Use local time for CSL synchronization" + help + Use host time rather than radio platform time to track elapsed time + since last CSL synchronization. This reduces the usage of radio API + calls, and it is useful for platforms in which those are costly. + +config OPENTHREAD_DEVICE_PROP_LEADER_WEIGHT + bool "Device props for leader weight" + default n if (OPENTHREAD_THREAD_VERSION_1_1 || \ + OPENTHREAD_THREAD_VERSION_1_2 || \ + OPENTHREAD_THREAD_VERSION_1_3) + default y + help + Enable the device properties which are then used to determine and set + the Leader Weight. + config OPENTHREAD_DATASET_UPDATER bool "Dataset updater" @@ -165,6 +186,9 @@ config OPENTHREAD_LEGACY config OPENTHREAD_LINK_METRICS_INITIATOR bool "Link Metrics initiator" +config OPENTHREAD_LINK_METRICS_MANAGER + bool "Link Metrics manager" + config OPENTHREAD_LINK_METRICS_SUBJECT bool "Link Metrics subject" @@ -194,6 +218,9 @@ config OPENTHREAD_MLR help Enable Multicast Listener Registration support for Thread 1.2 +config OPENTHREAD_MULTIPAN_RCP + bool "OpenThread multipan rcp" + config OPENTHREAD_MULTIPLE_INSTANCE bool "OpenThread multiple instances" diff --git a/modules/openthread/Kconfig.thread b/modules/openthread/Kconfig.thread index 73c547919a7..57a8de5d324 100644 --- a/modules/openthread/Kconfig.thread +++ b/modules/openthread/Kconfig.thread @@ -182,3 +182,15 @@ config OPENTHREAD_DEFAULT_TX_POWER default 0 help Set the default TX output power [dBm] in radio driver for OpenThread purpose. + +config OPENTHREAD_BLE_TCAT_THREAD_STACK_SIZE + int "Openthread default TCAT stack size" + default 4200 + help + Openthread default TCAT stack size. + +config OPENTHREAD_BLE_TCAT_RING_BUF_SIZE + int "Openthread BLE ringbuffer size" + default 512 + help + Openthread BLE TCAT ringbuffer size. diff --git a/modules/openthread/platform/CMakeLists.txt b/modules/openthread/platform/CMakeLists.txt index d363bcda7df..542aa5186ea 100644 --- a/modules/openthread/platform/CMakeLists.txt +++ b/modules/openthread/platform/CMakeLists.txt @@ -10,6 +10,7 @@ zephyr_library_sources( spi.c ) +zephyr_library_sources_ifdef(CONFIG_OPENTHREAD_BLE_TCAT ble.c) zephyr_library_sources_ifdef(CONFIG_OPENTHREAD_DIAG diag.c) zephyr_library_sources_ifdef(CONFIG_OPENTHREAD_COPROCESSOR uart.c) zephyr_library_sources_ifdef(CONFIG_OPENTHREAD_CRYPTO_PSA crypto_psa.c) diff --git a/modules/openthread/platform/ble.c b/modules/openthread/platform/ble.c new file mode 100644 index 00000000000..7b41556b617 --- /dev/null +++ b/modules/openthread/platform/ble.c @@ -0,0 +1,458 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* Zephyr OpenThread integration Library */ +#include + +/* OpenThread BLE driver API */ +#include + +/* Zephyr Logging */ + +#define LOG_MODULE_NAME net_openthread_tcat +#define LOG_LEVEL CONFIG_OPENTHREAD_LOG_LEVEL + +LOG_MODULE_REGISTER(LOG_MODULE_NAME); + +#define DEVICE_NAME CONFIG_BT_DEVICE_NAME +#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1) + +/* BLE connection constants as defined in thread specification. */ +#define TOBLE_SERVICE_UUID 0xfffb +#define RX_CHARACTERISTIC_UUID \ + BT_UUID_128_ENCODE(0x6bd10d8b, 0x85a7, 0x4e5a, 0xba2d, 0xc83558a5f220) +#define TX_CHARACTERISTIC_UUID \ + BT_UUID_128_ENCODE(0x7fddf61f, 0x280a, 0x4773, 0xb448, 0xba1b8fe0dd69) + +#define BT_UUID_TCAT_SERVICE BT_UUID_DECLARE_16(TOBLE_SERVICE_UUID) +#define BT_UUID_TCAT_SERVICE_RX BT_UUID_DECLARE_128(RX_CHARACTERISTIC_UUID) +#define BT_UUID_TCAT_SERVICE_TX BT_UUID_DECLARE_128(TX_CHARACTERISTIC_UUID) + +#define PLAT_BLE_THREAD_DEALY 500 +#define PLAT_BLE_MSG_DATA_MAX CONFIG_BT_L2CAP_TX_MTU /* must match the maximum MTU size used */ + +#define PLAT_BLE_MSG_CONNECT (PLAT_BLE_MSG_DATA_MAX + 1U) +#define PLAT_BLE_MSG_DISCONNECT (PLAT_BLE_MSG_CONNECT + 1U) + +/* Zephyr Kernel Objects */ + +static void ot_plat_ble_thread(void *, void *, void *); +static uint8_t ot_plat_ble_msg_buf[PLAT_BLE_MSG_DATA_MAX]; + +static K_SEM_DEFINE(ot_plat_ble_init_semaphore, 0, 1); +static K_SEM_DEFINE(ot_plat_ble_event_semaphore, 0, K_SEM_MAX_LIMIT); +RING_BUF_DECLARE(ot_plat_ble_ring_buf, CONFIG_OPENTHREAD_BLE_TCAT_RING_BUF_SIZE); +static K_THREAD_DEFINE(ot_plat_ble_tid, CONFIG_OPENTHREAD_BLE_TCAT_THREAD_STACK_SIZE, + ot_plat_ble_thread, NULL, NULL, NULL, 5, 0, PLAT_BLE_THREAD_DEALY); + +/* OpenThread Objects */ + +static otInstance *ble_openthread_instance; + +/* BLE service Objects */ + +/* forward declaration for callback functions */ +static ssize_t on_receive(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf, + uint16_t len, uint16_t offset, uint8_t flags); +static void on_cccd_changed(const struct bt_gatt_attr *attr, uint16_t value); + +/* Service Declaration and Registration */ +BT_GATT_SERVICE_DEFINE(my_service, BT_GATT_PRIMARY_SERVICE(BT_UUID_TCAT_SERVICE), + BT_GATT_CHARACTERISTIC(BT_UUID_TCAT_SERVICE_RX, + BT_GATT_CHRC_WRITE | BT_GATT_CHRC_WRITE_WITHOUT_RESP, + BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, NULL, + on_receive, NULL), + BT_GATT_CHARACTERISTIC(BT_UUID_TCAT_SERVICE_TX, BT_GATT_CHRC_NOTIFY, + BT_GATT_PERM_READ, NULL, NULL, NULL), + BT_GATT_CCC(on_cccd_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),); + +/* Zephyr BLE Objects */ + +/* forward declaration for callback functions */ +static void connected(struct bt_conn *conn, uint8_t err); +static void disconnected(struct bt_conn *conn, uint8_t reason); +static bool le_param_req(struct bt_conn *conn, struct bt_le_conn_param *param); +static void le_param_updated(struct bt_conn *conn, uint16_t interval, uint16_t latency, + uint16_t timeout); + +static struct bt_conn *ot_plat_ble_connection; + +static struct bt_conn_cb conn_callbacks = {.connected = connected, + .disconnected = disconnected, + .le_param_req = le_param_req, + .le_param_updated = le_param_updated}; + +static const struct bt_data ad[] = { + BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), + BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN), +}; + +static const struct bt_data sd[] = { + BT_DATA_BYTES(BT_DATA_UUID16_ALL, BT_UUID_16_ENCODE(TOBLE_SERVICE_UUID)), +}; + +/* Zephyr BLE Message Queue and Thread */ + +static bool ot_plat_ble_queue_msg(const uint8_t *aData, uint16_t aLen, int8_t aRssi) +{ + otError error = OT_ERROR_NONE; + uint16_t len = 0; + + if (aLen <= PLAT_BLE_MSG_DATA_MAX && aData == NULL) { + return OT_ERROR_INVALID_ARGS; + } + + k_sched_lock(); + + len = sizeof(aLen) + sizeof(aRssi) + ((aLen <= PLAT_BLE_MSG_DATA_MAX) ? aLen : 0); + + if (ring_buf_space_get(&ot_plat_ble_ring_buf) >= len) { + ring_buf_put(&ot_plat_ble_ring_buf, (uint8_t *)&aLen, sizeof(aLen)); + ring_buf_put(&ot_plat_ble_ring_buf, &aRssi, sizeof(aRssi)); + if (aLen <= PLAT_BLE_MSG_DATA_MAX) { + ring_buf_put(&ot_plat_ble_ring_buf, aData, aLen); + } + k_sem_give(&ot_plat_ble_event_semaphore); + } else { + error = OT_ERROR_NO_BUFS; + } + + k_sched_unlock(); + + return error; +} + +static void ot_plat_ble_thread(void *unused1, void *unused2, void *unused3) +{ + ARG_UNUSED(unused1); + ARG_UNUSED(unused2); + ARG_UNUSED(unused3); + + uint16_t len; + int8_t rssi; + otBleRadioPacket my_packet; + + LOG_INF("%s started", __func__); + + while (1) { + k_sem_take(&ot_plat_ble_event_semaphore, K_FOREVER); + ring_buf_get(&ot_plat_ble_ring_buf, (uint8_t *)&len, sizeof(len)); + ring_buf_get(&ot_plat_ble_ring_buf, &rssi, sizeof(rssi)); + if (len <= PLAT_BLE_MSG_DATA_MAX) { + ring_buf_get(&ot_plat_ble_ring_buf, ot_plat_ble_msg_buf, len); + } + + openthread_api_mutex_lock(openthread_get_default_context()); + + if (len <= PLAT_BLE_MSG_DATA_MAX) { + /* The packet parameter in otPlatBleGattServerOnWriteRequest is not const. + * Re-write all members. + */ + my_packet.mValue = ot_plat_ble_msg_buf; + my_packet.mPower = rssi; + my_packet.mLength = len; + otPlatBleGattServerOnWriteRequest(ble_openthread_instance, 0, &my_packet); + } else if (len == PLAT_BLE_MSG_CONNECT) { + otPlatBleGapOnConnected(ble_openthread_instance, 0); + } else if (len == PLAT_BLE_MSG_DISCONNECT) { + otPlatBleGapOnDisconnected(ble_openthread_instance, 0); + } + openthread_api_mutex_unlock(openthread_get_default_context()); + } +} + +/* Zephyr BLE service callbacks */ + +/* This function is called whenever the RX Characteristic has been written to by a Client */ +static ssize_t on_receive(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf, + uint16_t len, uint16_t offset, uint8_t flags) +{ + LOG_DBG("Received data, handle %" PRIu16 ", len %" PRIu16, attr->handle, len); + + otError error = ot_plat_ble_queue_msg(buf, len, 0); + + if (error != OT_ERROR_NONE) { + LOG_WRN("Error queuing message: %s", otThreadErrorToString(error)); + } + + return len; +} + +/* This function is called whenever a Notification has been sent by the TX Characteristic */ +static void on_sent(struct bt_conn *conn, void *user_data) +{ + ARG_UNUSED(conn); + ARG_UNUSED(user_data); + + LOG_DBG("Data sent"); +} + +/* This function is called whenever the CCCD register has been changed by the client */ +void on_cccd_changed(const struct bt_gatt_attr *attr, uint16_t value) +{ + uint16_t mtu; + otError error = OT_ERROR_NONE; + + ARG_UNUSED(attr); + + switch (value) { + case BT_GATT_CCC_NOTIFY: + + error = ot_plat_ble_queue_msg(NULL, PLAT_BLE_MSG_CONNECT, 0); + if (error != OT_ERROR_NONE) { + LOG_WRN("Error queuing message: %s", otThreadErrorToString(error)); + } + + error = otPlatBleGattMtuGet(ble_openthread_instance, &mtu); + if (error != OT_ERROR_NONE) { + LOG_WRN("Error retrieving mtu: %s", otThreadErrorToString(error)); + } + + LOG_INF("CCCD update (mtu=%" PRIu16 ")!", mtu); + + break; + + default: + break; + } +} + +otError otPlatBleGattServerIndicate(otInstance *aInstance, uint16_t aHandle, + const otBleRadioPacket *aPacket) +{ + ARG_UNUSED(aInstance); + + /* TO DO change to indications. */ + const struct bt_gatt_attr *attr = &my_service.attrs[3]; + + struct bt_gatt_notify_params params = {.uuid = BT_UUID_TCAT_SERVICE_TX, + .attr = attr, + .data = aPacket->mValue, + .len = aPacket->mLength, + .func = on_sent}; + + LOG_DBG("Send data, handle %d, len %d", attr->handle, aPacket->mLength); + + /* Only one connection supported */ + if (aHandle != 0) { + return OT_ERROR_INVALID_ARGS; + } + + if (ot_plat_ble_connection == NULL) { + return OT_ERROR_INVALID_STATE; + } + + /* Check whether notifications are enabled or not */ + if (bt_gatt_is_subscribed(ot_plat_ble_connection, attr, BT_GATT_CCC_NOTIFY)) { + if (bt_gatt_notify_cb(ot_plat_ble_connection, ¶ms)) { + LOG_WRN("Error, unable to send notification"); + return OT_ERROR_INVALID_ARGS; + } + } else { + LOG_WRN("Warning, notification not enabled on the selected attribute"); + return OT_ERROR_INVALID_STATE; + } + + return OT_ERROR_NONE; +} + +otError otPlatBleGattMtuGet(otInstance *aInstance, uint16_t *aMtu) +{ + ARG_UNUSED(aInstance); + + if (ot_plat_ble_connection == NULL) { + return OT_ERROR_FAILED; + } + + if (aMtu != NULL) { + *aMtu = bt_gatt_get_mtu(ot_plat_ble_connection); + } + + return OT_ERROR_NONE; +} + +otError otPlatBleGapDisconnect(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + if (ot_plat_ble_connection == NULL) { + return OT_ERROR_INVALID_STATE; + } + + if (bt_conn_disconnect(ot_plat_ble_connection, BT_HCI_ERR_REMOTE_USER_TERM_CONN)) { + return OT_ERROR_INVALID_STATE; + } + + return OT_ERROR_NONE; +} + +/* Zephyr BLE callbacks */ + +static void connected(struct bt_conn *conn, uint8_t err) +{ + struct bt_conn_info info; + char addr[BT_ADDR_LE_STR_LEN]; + uint16_t mtu; + otError error = OT_ERROR_NONE; + + ot_plat_ble_connection = bt_conn_ref(conn); + + if (err) { + LOG_WRN("Connection failed (err %u)", err); + return; + } else if (bt_conn_get_info(conn, &info)) { + LOG_WRN("Could not parse connection info"); + } else { + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + + error = otPlatBleGattMtuGet(ble_openthread_instance, &mtu); + if (error != OT_ERROR_NONE) { + LOG_WRN("Error retrieving mtu: %s", otThreadErrorToString(error)); + } + + LOG_INF("Connection established (mtu=%" PRIu16 ")!", mtu); + } +} + +static void disconnected(struct bt_conn *conn, uint8_t reason) +{ + otError error = OT_ERROR_NONE; + + LOG_INF("Disconnected (reason %" PRIu8 ")", reason); + + if (ot_plat_ble_connection) { + bt_conn_unref(ot_plat_ble_connection); + ot_plat_ble_connection = NULL; + + error = ot_plat_ble_queue_msg(NULL, PLAT_BLE_MSG_DISCONNECT, 0); + if (error != OT_ERROR_NONE) { + LOG_WRN("Error queuing message: %s", otThreadErrorToString(error)); + } + } +} + +static bool le_param_req(struct bt_conn *conn, struct bt_le_conn_param *param) +{ + return true; +} + +static void le_param_updated(struct bt_conn *conn, uint16_t interval, uint16_t latency, + uint16_t timeout) +{ + struct bt_conn_info info; + char addr[BT_ADDR_LE_STR_LEN]; + uint16_t mtu; + otError error = OT_ERROR_NONE; + + if (bt_conn_get_info(conn, &info)) { + LOG_INF("Could not parse connection info"); + } else { + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + + error = otPlatBleGattMtuGet(ble_openthread_instance, &mtu); + + if (error != OT_ERROR_NONE) { + LOG_WRN("Error retrieving mtu: %s", otThreadErrorToString(error)); + } + + LOG_INF("Connection parameters updated (mtu=%" PRIu16 ")!", mtu); + } +} + +static void bt_ready(int err) +{ + if (err) { + LOG_WRN("BLE init failed with error code %d", err); + return; + } + + bt_conn_cb_register(&conn_callbacks); + k_sem_give(&ot_plat_ble_init_semaphore); /* BLE stack up an running */ +} + +otError otPlatBleGapAdvStart(otInstance *aInstance, uint16_t aInterval) +{ + ARG_UNUSED(aInstance); + ARG_UNUSED(aInterval); + + /* TO DO advertisement format change */ + int err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd)); + + if (err != 0 && err != -EALREADY) { + LOG_WRN("Advertising failed to start (err %d)", err); + return OT_ERROR_INVALID_STATE; + } + + LOG_INF("Advertising successfully started"); + + return OT_ERROR_NONE; +} + +otError otPlatBleGapAdvStop(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + int err = bt_le_adv_stop(); + + if (err != 0 && err != -EALREADY) { + LOG_WRN("Advertisement failed to stop (err %d)", err); + return OT_ERROR_FAILED; + } + return OT_ERROR_NONE; +} + +/* Zephyr BLE initialization */ + +otError otPlatBleEnable(otInstance *aInstance) +{ + int err; + + ble_openthread_instance = aInstance; + err = bt_enable(bt_ready); + + if (err != 0 && err != -EALREADY) { + LOG_WRN("BLE enable failed with error code %d", err); + return OT_ERROR_FAILED; + } else if (err == -EALREADY) { + err = k_sem_take(&ot_plat_ble_init_semaphore, K_MSEC(500)); + return OT_ERROR_NONE; + } + + err = k_sem_take(&ot_plat_ble_init_semaphore, K_MSEC(500)); + + if (!err) { + LOG_INF("Bluetooth initialized"); + } else { + LOG_INF("BLE initialization did not complete in time"); + return OT_ERROR_FAILED; + } + + return OT_ERROR_NONE; +} + +otError otPlatBleDisable(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + /* This function intentionally does nothing since disabling advertisement disables BLE + * stack. + */ + return OT_ERROR_NONE; +} diff --git a/modules/openthread/platform/crypto_psa.c b/modules/openthread/platform/crypto_psa.c index e61177ef4c4..e5b234ce030 100644 --- a/modules/openthread/platform/crypto_psa.c +++ b/modules/openthread/platform/crypto_psa.c @@ -137,9 +137,8 @@ void otPlatCryptoInit(void) * PSA with emulated TFM, Settings have to be initialized at the end of otPlatCryptoInit(), * to be available before storing Network Key. */ - int err = settings_subsys_init(); - - __ASSERT(!err, "Failed to initialize settings"); + __ASSERT_EVAL((void) settings_subsys_init(), int err = settings_subsys_init(), + !err, "Failed to initialize settings"); #endif } diff --git a/modules/openthread/platform/radio.c b/modules/openthread/platform/radio.c index 94b79ddfed6..e64e47a5fa9 100644 --- a/modules/openthread/platform/radio.c +++ b/modules/openthread/platform/radio.c @@ -58,8 +58,6 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_OPENTHREAD_L2_LOG_LEVEL); #define CHANNEL_COUNT OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MAX - OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MIN + 1 -#define PHY_SHR_DURATION 160 /* duration of SHR in us */ - enum pending_events { PENDING_EVENT_FRAME_TO_SEND, /* There is a tx frame to send */ PENDING_EVENT_FRAME_RECEIVED, /* Radio has received new frame */ @@ -397,7 +395,7 @@ void transmit_message(struct k_work *tx_job) (sTransmitFrame.mInfo.mTxInfo.mTxDelay != 0)) { #if defined(CONFIG_NET_PKT_TXTIME) uint32_t tx_at = sTransmitFrame.mInfo.mTxInfo.mTxDelayBaseTime + - sTransmitFrame.mInfo.mTxInfo.mTxDelay + PHY_SHR_DURATION; + sTransmitFrame.mInfo.mTxInfo.mTxDelay; struct net_ptp_time timestamp = ns_to_net_ptp_time(convert_32bit_us_wrapped_to_64bit_ns(tx_at)); net_pkt_set_timestamp(tx_pkt, ×tamp); @@ -1183,20 +1181,14 @@ void otPlatRadioSetMacKey(otInstance *aInstance, uint8_t aKeyIdMode, uint8_t aKe struct ieee802154_key keys[] = { { .key_id_mode = key_id_mode, - .key_index = aKeyId == 1 ? 0x80 : aKeyId - 1, - .key_value = (uint8_t *)aPrevKey->mKeyMaterial.mKey.m8, .frame_counter_per_key = false, }, { .key_id_mode = key_id_mode, - .key_index = aKeyId, - .key_value = (uint8_t *)aCurrKey->mKeyMaterial.mKey.m8, .frame_counter_per_key = false, }, { .key_id_mode = key_id_mode, - .key_index = aKeyId == 0x80 ? 1 : aKeyId + 1, - .key_value = (uint8_t *)aNextKey->mKeyMaterial.mKey.m8, .frame_counter_per_key = false, }, { @@ -1210,9 +1202,24 @@ void otPlatRadioSetMacKey(otInstance *aInstance, uint8_t aKeyIdMode, uint8_t aKe }, }; - /* aKeyId in range: (1, 0x80) means valid keys - * aKeyId == 0 is used only to clear keys for stack reset in RCP - */ + if (key_id_mode == 1) { + /* aKeyId in range: (1, 0x80) means valid keys */ + uint8_t prev_key_id = aKeyId == 1 ? 0x80 : aKeyId - 1; + uint8_t next_key_id = aKeyId == 0x80 ? 1 : aKeyId + 1; + + keys[0].key_id = &prev_key_id; + keys[0].key_value = (uint8_t *)aPrevKey->mKeyMaterial.mKey.m8; + + keys[1].key_id = &aKeyId; + keys[1].key_value = (uint8_t *)aCurrKey->mKeyMaterial.mKey.m8; + + keys[2].key_id = &next_key_id; + keys[2].key_value = (uint8_t *)aNextKey->mKeyMaterial.mKey.m8; + } else { + /* aKeyId == 0 is used only to clear keys for stack reset in RCP */ + __ASSERT_NO_MSG((key_id_mode == 0) && (aKeyId == 0)); + } + struct ieee802154_config config = { .mac_keys = aKeyId == 0 ? clear_keys : keys, }; diff --git a/modules/openthread/platform/uart.c b/modules/openthread/platform/uart.c index dd62d36ee11..ebf156ded15 100644 --- a/modules/openthread/platform/uart.c +++ b/modules/openthread/platform/uart.c @@ -170,7 +170,6 @@ otError otPlatUartEnable(void) if (DT_NODE_HAS_COMPAT(DT_CHOSEN(zephyr_ot_uart), zephyr_cdc_acm_uart)) { int ret; - uint32_t dtr = 0U; ret = usb_enable(NULL); if (ret != 0 && ret != -EALREADY) { @@ -178,20 +177,6 @@ otError otPlatUartEnable(void) return OT_ERROR_FAILED; } - LOG_INF("Waiting for host to be ready to communicate"); - - /* Data Terminal Ready - check if host is ready to communicate */ - while (!dtr) { - ret = uart_line_ctrl_get(ot_uart.dev, - UART_LINE_CTRL_DTR, &dtr); - if (ret) { - LOG_ERR("Failed to get Data Terminal Ready line state: %d", - ret); - continue; - } - k_msleep(100); - } - /* Data Carrier Detect Modem - mark connection as established */ (void)uart_line_ctrl_set(ot_uart.dev, UART_LINE_CTRL_DCD, 1); /* Data Set Ready - the NCP SoC is ready to communicate */ diff --git a/modules/trusted-firmware-m/CMakeLists.txt b/modules/trusted-firmware-m/CMakeLists.txt index 0c01858f34d..ad1109d849b 100644 --- a/modules/trusted-firmware-m/CMakeLists.txt +++ b/modules/trusted-firmware-m/CMakeLists.txt @@ -95,12 +95,37 @@ if (CONFIG_BUILD_WITH_TFM) list(APPEND TFM_CMAKE_ARGS -DMCUBOOT_IMAGE_NUMBER=${CONFIG_TFM_MCUBOOT_IMAGE_NUMBER}) endif() + if (CONFIG_TFM_DUMMY_PROVISIONING) + list(APPEND TFM_CMAKE_ARGS -DTFM_DUMMY_PROVISIONING=ON) + else() + list(APPEND TFM_CMAKE_ARGS -DTFM_DUMMY_PROVISIONING=OFF) + endif() + if (CONFIG_TFM_EXCEPTION_INFO_DUMP) list(APPEND TFM_CMAKE_ARGS -DTFM_EXCEPTION_INFO_DUMP=ON) else() list(APPEND TFM_CMAKE_ARGS -DTFM_EXCEPTION_INFO_DUMP=OFF) endif() + if (CONFIG_TFM_BL2) + if (CONFIG_TFM_BL2_LOG_LEVEL_DEBUG) + set(TFM_BL2_LOG_LEVEL "DEBUG") + elseif (CONFIG_TFM_BL2_LOG_LEVEL_INFO) + set(TFM_BL2_LOG_LEVEL "INFO") + elseif (CONFIG_TFM_BL2_LOG_LEVEL_WARNING) + set(TFM_BL2_LOG_LEVEL "WARNING") + elseif (CONFIG_TFM_BL2_LOG_LEVEL_ERROR) + set(TFM_BL2_LOG_LEVEL "ERROR") + elseif (CONFIG_TFM_BL2_LOG_LEVEL_OFF OR CONFIG_TFM_LOG_LEVEL_SILENCE) + set(TFM_BL2_LOG_LEVEL "OFF") + endif() + + if (DEFINED TFM_BL2_LOG_LEVEL) + # BL2 uses MCUBOOT_LOG_LEVEL configuration + list(APPEND TFM_CMAKE_ARGS -DMCUBOOT_LOG_LEVEL=${TFM_BL2_LOG_LEVEL}) + endif() + endif() + if (CONFIG_TFM_PARTITION_LOG_LEVEL_DEBUG) set(TFM_PARTITION_LOG_LEVEL "TFM_PARTITION_LOG_LEVEL_DEBUG") elseif (CONFIG_TFM_PARTITION_LOG_LEVEL_INFO) @@ -416,8 +441,8 @@ if (CONFIG_BUILD_WITH_TFM) else() zephyr_library_link_libraries( - ${TFM_API_NS_PATH} ${PLATFORM_NS_FILE} + ${TFM_API_NS_PATH} ) endif() @@ -560,4 +585,13 @@ if (CONFIG_BUILD_WITH_TFM) ${MERGED_FILE} ) endif() -endif() + + if(CONFIG_TFM_DUMMY_PROVISIONING) + message(WARNING + "TFM_DUMMY_PROVISIONING is enabled: + The device will be provisioned using dummy keys and is NOT secure! + This is not suitable for production" + ) + endif() + +endif() # CONFIG_BUILD_WITH_TFM diff --git a/modules/trusted-firmware-m/Kconfig.tfm b/modules/trusted-firmware-m/Kconfig.tfm index a7276d79374..dc2ab24b17d 100644 --- a/modules/trusted-firmware-m/Kconfig.tfm +++ b/modules/trusted-firmware-m/Kconfig.tfm @@ -9,9 +9,6 @@ config ZEPHYR_TRUSTED_FIRMWARE_M_MODULE config TFM_BOARD string - default "nordic_nrf/nrf9160dk_nrf9160" if BOARD_NRF9160DK_NRF9160_NS - default "nordic_nrf/nrf9161dk_nrf9161" if BOARD_NRF9161DK_NRF9161_NS - default "nordic_nrf/nrf5340dk_nrf5340_cpuapp" if BOARD_NRF5340DK_NRF5340_CPUAPP_NS default "nxp/lpcxpresso55s69" if BOARD_LPCXPRESSO55S69_CPU0 default "arm/mps2/an521" if BOARD_MPS2_AN521_CPU0_NS default "arm/mps3/an547" if BOARD_MPS3_AN547 @@ -21,6 +18,9 @@ config TFM_BOARD default "arm/musca_b1" if BOARD_MUSCA_B1 default "arm/musca_s1" if BOARD_MUSCA_S1 default "lairdconnectivity/bl5340_dvk_cpuapp" if BOARD_BL5340_DVK_CPUAPP_NS + default "${ZEPHYR_BASE}/modules/trusted-firmware-m/nordic_nrf/nrf9160" if SOC_NRF9160 + default "${ZEPHYR_BASE}/modules/trusted-firmware-m/nordic_nrf/nrf9120" if SOC_NRF9120 + default "${ZEPHYR_BASE}/modules/trusted-firmware-m/nordic_nrf/nrf5340_cpuapp" if SOC_NRF5340_CPUAPP help The board name used for building TFM. Building with TFM requires that TFM has been ported to the given board/SoC. @@ -177,6 +177,25 @@ config TFM_PARTITION_PLATFORM_CUSTOM_REBOOT Instead the application will have to override the weak ARM implementation of sys_arch_reset(). +config TFM_DUMMY_PROVISIONING + bool "Provision with dummy values. NOT to be used in production" + select TFM_INITIAL_ATTESTATION_KEY + default y + help + If this option is enabled (as it is by default), a set of dummy + keys / data will be provisioned. The dummy IAK matches the IAK tested + by the TF-M tests, and the dummy bl2 ROTPKs match the dummy bl2 keys + used by default. + This option MUST not be used in production hardware, as the keys are + insecure. + +config TFM_INITIAL_ATTESTATION_KEY + bool + help + Hidden option to mark that the TF-M platform has an initial + attestation key, which is a requirement for the Initial Attestation + partition. + config TFM_BL2_NOT_SUPPORTED bool help @@ -397,6 +416,21 @@ config ROM_START_OFFSET needs to be updated if TF-M switches to use a different header size for BL2. +choice TFM_BL2_LOG_LEVEL + prompt "BL2 Log Level" if !TFM_LOG_LEVEL_SILENCE + default TFM_BL2_LOG_LEVEL_INFO + config TFM_BL2_LOG_LEVEL_DEBUG + bool "Debug" + config TFM_BL2_LOG_LEVEL_INFO + bool "Info" + config TFM_BL2_LOG_LEVEL_WARNING + bool "Warning" + config TFM_BL2_LOG_LEVEL_ERROR + bool "Error" + config TFM_BL2_LOG_LEVEL_OFF + bool "Off" +endchoice + endif # !TFM_BL2 # Option to instruct flashing a merged binary consisting of BL2 (optionally), @@ -431,6 +465,7 @@ endchoice config TFM_EXCEPTION_INFO_DUMP bool "TF-M exception info dump" + default y help On fatal errors in the secure firmware, capture info about the exception. Print the info if the SPM log level is sufficient. diff --git a/modules/trusted-firmware-m/Kconfig.tfm.crypto_modules b/modules/trusted-firmware-m/Kconfig.tfm.crypto_modules index 1d70a2c44d2..02d3580c22f 100644 --- a/modules/trusted-firmware-m/Kconfig.tfm.crypto_modules +++ b/modules/trusted-firmware-m/Kconfig.tfm.crypto_modules @@ -10,6 +10,7 @@ if TFM_PARTITION_CRYPTO config TFM_CRYPTO_RNG_MODULE_ENABLED bool "Random number generator crypto module" default y + depends on PSA_WANT_GENERATE_RANDOM && NRF_SECURITY help Enables the random number generator module within the crypto partition. Unset this option if 'psa_generate_random' is not used. @@ -17,6 +18,7 @@ config TFM_CRYPTO_RNG_MODULE_ENABLED config TFM_CRYPTO_KEY_MODULE_ENABLED bool "KEY crypto module" default y + depends on PSA_HAS_KEY_SUPPORT && NRF_SECURITY help Enables the KEY crypto module within the crypto partition. Unset this option if the functionality provided by 'crypto_key_management.c' @@ -25,6 +27,7 @@ config TFM_CRYPTO_KEY_MODULE_ENABLED config TFM_CRYPTO_AEAD_MODULE_ENABLED bool "AEAD crypto module" default y + depends on PSA_HAS_AEAD_SUPPORT && NRF_SECURITY help Enables the AEAD crypto module within the crypto partition. Unset this option if the functionality provided by 'crypto_aead.c' @@ -33,6 +36,7 @@ config TFM_CRYPTO_AEAD_MODULE_ENABLED config TFM_CRYPTO_MAC_MODULE_ENABLED bool "MAC crypto module" default y + depends on PSA_HAS_MAC_SUPPORT && NRF_SECURITY help Enables the MAC crypto module within the crypto partition. Unset this option if the functionality provided by 'crypto_mac.c' @@ -41,6 +45,7 @@ config TFM_CRYPTO_MAC_MODULE_ENABLED config TFM_CRYPTO_HASH_MODULE_ENABLED bool "HASH crypto module" default y + depends on PSA_HAS_HASH_SUPPORT && NRF_SECURITY help Enables the HASH crypto module within the crypto partition. Unset this option if the functionality provided by 'crypto_hash.c' @@ -49,6 +54,7 @@ config TFM_CRYPTO_HASH_MODULE_ENABLED config TFM_CRYPTO_CIPHER_MODULE_ENABLED bool "CIPHER crypto module" default y + depends on PSA_HAS_CIPHER_SUPPORT && NRF_SECURITY help Enables the CIPHER crypto module within the crypto partition. Unset this option if the functionality provided by 'crypto_cipher.c' @@ -57,6 +63,7 @@ config TFM_CRYPTO_CIPHER_MODULE_ENABLED config TFM_CRYPTO_ASYM_ENCRYPT_MODULE_ENABLED bool "ASYM ENCRYPT crypto module" default y + depends on PSA_HAS_ASYM_ENCRYPT_SUPPORT && NRF_SECURITY help Enables the ASYM ENCRYPT crypto module within the crypto partition. Unset this option if the encrypt functionality provided by 'crypto_asymmetric.c' @@ -65,6 +72,7 @@ config TFM_CRYPTO_ASYM_ENCRYPT_MODULE_ENABLED config TFM_CRYPTO_ASYM_SIGN_MODULE_ENABLED bool "ASYM SIGN crypto module" default y + depends on PSA_HAS_ASYM_SIGN_SUPPORT && NRF_SECURITY help Enables the ASYM SIGN crypto module within the crypto partition. Unset this option if the sign functionality provided by 'crypto_asymmetric.c' @@ -73,10 +81,12 @@ config TFM_CRYPTO_ASYM_SIGN_MODULE_ENABLED config TFM_CRYPTO_KEY_DERIVATION_MODULE_ENABLED bool "KEY DERIVATION crypto module" default y + depends on (PSA_HAS_KEY_DERIVATION || PSA_HAS_KEY_AGREEMENT) && NRF_SECURITY help Enables the KEY_DERIVATION crypto module within the crypto partition. Unset this option if the functionality provided by 'crypto_key_derivation.c' is not used. + Note that key agreement is under key derivation in the current implementation. endif # TFM_PARTITION_CRYPTO diff --git a/modules/trusted-firmware-m/Kconfig.tfm.partitions b/modules/trusted-firmware-m/Kconfig.tfm.partitions index cd9aaadb1ec..67b46f5328b 100644 --- a/modules/trusted-firmware-m/Kconfig.tfm.partitions +++ b/modules/trusted-firmware-m/Kconfig.tfm.partitions @@ -44,6 +44,7 @@ config TFM_PARTITION_CRYPTO config TFM_PARTITION_INITIAL_ATTESTATION bool "Secure partition 'Initial Attestation'" depends on TFM_PARTITION_CRYPTO + depends on TFM_INITIAL_ATTESTATION_KEY default n help Setting this option will cause '-DTFM_PARTITION_INITIAL_ATTESTATION' diff --git a/modules/trusted-firmware-m/nordic_nrf/CMakeLists.txt b/modules/trusted-firmware-m/nordic_nrf/CMakeLists.txt new file mode 100644 index 00000000000..41dca2f15a9 --- /dev/null +++ b/modules/trusted-firmware-m/nordic_nrf/CMakeLists.txt @@ -0,0 +1,67 @@ +# +# Copyright (c) 2023, Nordic Semiconductor ASA. +# +# SPDX-License-Identifier: Apache-2.0 +# + +cmake_policy(SET CMP0076 NEW) +set(CMAKE_CURRENT_SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}) + +set(partition_includes + ${Trusted\ Firmware\ M_SOURCE_DIR}/platform/ext/target/nordic_nrf/common/${NRF_SOC_VARIANT}/partition + ${CMAKE_BINARY_DIR}/../zephyr/include/generated +) + +set(board_includes + ${CMAKE_BINARY_DIR}/../zephyr/misc/generated/syscalls_links/include + ${ZEPHYR_BASE}/include +) + +target_include_directories(platform_region_defs + INTERFACE + ${partition_includes} +) + + +target_sources(platform_s + PRIVATE + $<$:${CMAKE_CURRENT_SOURCE_DIR}/src/tfm_platform_system.c> +) + +target_include_directories(platform_s + PUBLIC + services/include + include + ${partition_includes} + ${board_includes} +) + +target_include_directories(platform_ns + PUBLIC + include + include/util + ${partition_includes} + ${board_includes} +) + +if(BL2) + target_include_directories(platform_bl2 + PUBLIC + include + include/util + ${partition_includes} + ${board_includes} + ) +endif() + +if (TFM_PARTITION_PLATFORM) +install(FILES include/tfm_ioctl_api.h + DESTINATION ${TFM_INSTALL_PATH}/interface/include) +endif() + +#========================= tfm_spm ============================================# + +target_sources(tfm_spm + PRIVATE + src/tfm_hal_platform.c +) diff --git a/modules/trusted-firmware-m/nordic_nrf/include/RTE_Device.h b/modules/trusted-firmware-m/nordic_nrf/include/RTE_Device.h new file mode 100644 index 00000000000..ca886583fa9 --- /dev/null +++ b/modules/trusted-firmware-m/nordic_nrf/include/RTE_Device.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __RTE_DEVICE_H +#define __RTE_DEVICE_H + +#include + +/* ARRAY_SIZE causes a conflict as it is defined both by TF-M and indirectly by devicetree.h */ +#undef ARRAY_SIZE +#include + +#define UART_PIN_INIT(node_id, prop, idx) \ + DT_PROP_BY_IDX(node_id, prop, idx), + +/* Configuration settings for Driver_USART0. */ +#if DOMAIN_NS == 1U + +#define RTE_USART0 1 + +#define RTE_USART0_PINS \ +{ \ + DT_FOREACH_CHILD_VARGS( \ + DT_PINCTRL_BY_NAME(DT_NODELABEL(uart0), default, 0), \ + DT_FOREACH_PROP_ELEM, psels, UART_PIN_INIT \ + ) \ +} + +#endif + +/* Configuration settings for Driver_USART1. */ +#if DT_PINCTRL_HAS_NAME(DT_NODELABEL(uart1), default) && DOMAIN_NS != 1U + +#define RTE_USART1 1 + +#define RTE_USART1_PINS \ +{ \ + DT_FOREACH_CHILD_VARGS( \ + DT_PINCTRL_BY_NAME(DT_NODELABEL(uart1), default, 0), \ + DT_FOREACH_PROP_ELEM, psels, UART_PIN_INIT \ + ) \ +} + +#endif + +/* Configuration settings for Driver_FLASH0. */ +#define RTE_FLASH0 1 + +#endif /* __RTE_DEVICE_H */ diff --git a/modules/trusted-firmware-m/nordic_nrf/include/device_cfg.h b/modules/trusted-firmware-m/nordic_nrf/include/device_cfg.h new file mode 100644 index 00000000000..9b766a35eb9 --- /dev/null +++ b/modules/trusted-firmware-m/nordic_nrf/include/device_cfg.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef DEVICE_CFG_H__ +#define DEVICE_CFG_H__ + +#include + +/* ARRAY_SIZE causes a conflict as it is defined both by TF-M and indirectly by devicetree.h */ +#undef ARRAY_SIZE +#include + +#if DOMAIN_NS == 1U +#define TFM_UART uart0 +#endif + +#if DOMAIN_NS != 1U +#define TFM_UART uart1 +#endif + +#define DEFAULT_UART_BAUDRATE DT_PROP_OR(DT_NODELABEL(TFM_UART), current_speed, 115200) + +#if DT_PROP(DT_NODELABEL(TFM_UART), hw_flow_control) +#define DEFAULT_UART_CONTROL ARM_USART_FLOW_CONTROL_RTS_CTS +#else +#define DEFAULT_UART_CONTROL 0 +#endif + +#endif /* DEVICE_CFG_H__ */ diff --git a/modules/trusted-firmware-m/nordic_nrf/include/tfm_ioctl_api.h b/modules/trusted-firmware-m/nordic_nrf/include/tfm_ioctl_api.h new file mode 100644 index 00000000000..c6c36ee927f --- /dev/null +++ b/modules/trusted-firmware-m/nordic_nrf/include/tfm_ioctl_api.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef TFM_IOCTL_API_H__ +#define TFM_IOCTL_API_H__ + +#include +#include +#include +#include + +/* Include core IOCTL services */ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Board specific IOCTL services can be added here */ + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* TFM_IOCTL_API_H__ */ diff --git a/modules/trusted-firmware-m/nordic_nrf/include/tfm_peripherals_config.h b/modules/trusted-firmware-m/nordic_nrf/include/tfm_peripherals_config.h new file mode 100644 index 00000000000..577b583ad03 --- /dev/null +++ b/modules/trusted-firmware-m/nordic_nrf/include/tfm_peripherals_config.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef TFM_PERIPHERALS_CONFIG_H__ +#define TFM_PERIPHERALS_CONFIG_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef SECURE_UART1 +#define TFM_PERIPHERAL_UARTE1_SECURE 1 +#endif + +#if TEST_NS_SLIH_IRQ || TEST_NS_FLIH_IRQ +#define TFM_PERIPHERAL_TIMER0_SECURE 1 +#endif + +#ifdef PSA_API_TEST_IPC +#define TFM_PERIPHERAL_EGU5_SECURE 1 + +#define TFM_PERIPHERAL_WDT_SECURE 1 +#endif + +#if defined(NRF91_SERIES) + #include +#elif defined(NRF5340_XXAA_APPLICATION) + #include +#else + #error "Unknown device." +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* TFM_PERIPHERAL_CONFIG_H__ */ diff --git a/modules/trusted-firmware-m/nordic_nrf/include/tfm_read_ranges.h b/modules/trusted-firmware-m/nordic_nrf/include/tfm_read_ranges.h new file mode 100644 index 00000000000..43468c7fa31 --- /dev/null +++ b/modules/trusted-firmware-m/nordic_nrf/include/tfm_read_ranges.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef TFM_READ_RANGES_H__ +#define TFM_READ_RANGES_H__ + +#include + +#include + +#ifdef NRF_FICR_S_BASE + +#define FICR_BASE NRF_FICR_S_BASE + +#define FICR_INFO_ADDR (FICR_BASE + offsetof(NRF_FICR_Type, INFO)) +#define FICR_INFO_SIZE (sizeof(FICR_INFO_Type)) + +#if defined(FICR_NFC_TAGHEADER0_MFGID_Msk) +#define FICR_NFC_ADDR (FICR_BASE + offsetof(NRF_FICR_Type, NFC)) +#define FICR_NFC_SIZE (sizeof(FICR_NFC_Type)) +#endif + +#if defined(FICR_XOSC32MTRIM_SLOPE_Msk) +#define FICR_XOSC32MTRIM_ADDR (FICR_BASE + offsetof(NRF_FICR_Type, XOSC32MTRIM)) +#define FICR_XOSC32MTRIM_SIZE (sizeof(uint32_t)) +#endif + +/* Used by nrf_erratas.h */ +#define FICR_RESTRICTED_ADDR (FICR_BASE + 0x130) +#define FICR_RESTRICTED_SIZE 0x8 + +#if defined(FICR_SIPINFO_PARTNO_PARTNO_Pos) +#define FICR_SIPINFO_ADDR (FICR_BASE + offsetof(NRF_FICR_Type, SIPINFO)) +#define FICR_SIPINFO_SIZE (sizeof(FICR_SIPINFO_Type)) +#endif + +#endif /* NRF_FICR_S_BASE */ + +static const struct tfm_read_service_range ranges[] = { +#if defined(FICR_INFO_ADDR) + { .start = FICR_INFO_ADDR, .size = FICR_INFO_SIZE }, +#endif +#if defined(FICR_NFC_ADDR) + { .start = FICR_NFC_ADDR, .size = FICR_NFC_SIZE }, +#endif +#if defined(FICR_RESTRICTED_ADDR) + { .start = FICR_RESTRICTED_ADDR, .size = FICR_RESTRICTED_SIZE }, +#endif +#if defined(FICR_XOSC32MTRIM_ADDR) + { .start = FICR_XOSC32MTRIM_ADDR, .size = FICR_XOSC32MTRIM_SIZE }, +#endif +#if defined(FICR_SIPINFO_ADDR) + { .start = FICR_SIPINFO_ADDR, .size = FICR_SIPINFO_SIZE }, +#endif +}; + +#endif /* TFM_READ_RANGES_H__ */ diff --git a/modules/trusted-firmware-m/nordic_nrf/include/util/array.h b/modules/trusted-firmware-m/nordic_nrf/include/util/array.h new file mode 100644 index 00000000000..dc9a1f3dddf --- /dev/null +++ b/modules/trusted-firmware-m/nordic_nrf/include/util/array.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ARRAY_H__ +#define __ARRAY_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifdef __cplusplus +} +#endif + +#endif /* __ARRAY_H__ */ diff --git a/modules/trusted-firmware-m/nordic_nrf/nrf5340_cpuapp/CMakeLists.txt b/modules/trusted-firmware-m/nordic_nrf/nrf5340_cpuapp/CMakeLists.txt new file mode 100644 index 00000000000..279ea385996 --- /dev/null +++ b/modules/trusted-firmware-m/nordic_nrf/nrf5340_cpuapp/CMakeLists.txt @@ -0,0 +1,12 @@ +# +# Copyright (c) 2023, Nordic Semiconductor ASA. +# +# SPDX-License-Identifier: Apache-2.0 +# + +set(NRF_BOARD_SELECTED True) +set(NRF_SOC_VARIANT nrf5340) + +add_subdirectory(${Trusted\ Firmware\ M_SOURCE_DIR}/platform/ext/target/nordic_nrf/common/nrf5340 nrf5340) + +add_subdirectory(.. common) diff --git a/modules/trusted-firmware-m/nordic_nrf/nrf5340_cpuapp/config.cmake b/modules/trusted-firmware-m/nordic_nrf/nrf5340_cpuapp/config.cmake new file mode 100644 index 00000000000..b3e5d74181c --- /dev/null +++ b/modules/trusted-firmware-m/nordic_nrf/nrf5340_cpuapp/config.cmake @@ -0,0 +1,8 @@ +# +# Copyright (c) 2023, Nordic Semiconductor ASA. +# +# SPDX-License-Identifier: Apache-2.0 +# + +set(PLATFORM_PATH platform/ext/target/nordic_nrf/) +include(${PLATFORM_PATH}/common/nrf5340/config.cmake) diff --git a/modules/trusted-firmware-m/nordic_nrf/nrf5340_cpuapp/preload.cmake b/modules/trusted-firmware-m/nordic_nrf/nrf5340_cpuapp/preload.cmake new file mode 100644 index 00000000000..d9bd226eb65 --- /dev/null +++ b/modules/trusted-firmware-m/nordic_nrf/nrf5340_cpuapp/preload.cmake @@ -0,0 +1,7 @@ +# +# Copyright (c) 2023, Nordic Semiconductor ASA. +# +# SPDX-License-Identifier: Apache-2.0 +# + +include(platform/ext/target/nordic_nrf/common/nrf5340/preload.cmake) diff --git a/modules/trusted-firmware-m/nordic_nrf/nrf9120/CMakeLists.txt b/modules/trusted-firmware-m/nordic_nrf/nrf9120/CMakeLists.txt new file mode 100644 index 00000000000..a84c6fd9fd5 --- /dev/null +++ b/modules/trusted-firmware-m/nordic_nrf/nrf9120/CMakeLists.txt @@ -0,0 +1,12 @@ +# +# Copyright (c) 2023, Nordic Semiconductor ASA. +# +# SPDX-License-Identifier: Apache-2.0 +# + +set(NRF_BOARD_SELECTED True) +set(NRF_SOC_VARIANT nrf91) + +add_subdirectory(${Trusted\ Firmware\ M_SOURCE_DIR}/platform/ext/target/nordic_nrf/common/nrf91 nrf91) + +add_subdirectory(.. common) diff --git a/modules/trusted-firmware-m/nordic_nrf/nrf9120/config.cmake b/modules/trusted-firmware-m/nordic_nrf/nrf9120/config.cmake new file mode 100644 index 00000000000..3f58e7b89eb --- /dev/null +++ b/modules/trusted-firmware-m/nordic_nrf/nrf9120/config.cmake @@ -0,0 +1,8 @@ +# +# Copyright (c) 2023, Nordic Semiconductor ASA. +# +# SPDX-License-Identifier: Apache-2.0 +# + +set(PLATFORM_PATH platform/ext/target/nordic_nrf/) +include(${PLATFORM_PATH}/common/nrf91/config.cmake) diff --git a/modules/trusted-firmware-m/nordic_nrf/nrf9120/preload.cmake b/modules/trusted-firmware-m/nordic_nrf/nrf9120/preload.cmake new file mode 100644 index 00000000000..4b3c6ee79ab --- /dev/null +++ b/modules/trusted-firmware-m/nordic_nrf/nrf9120/preload.cmake @@ -0,0 +1,7 @@ +# +# Copyright (c) 2023, Nordic Semiconductor ASA. +# +# SPDX-License-Identifier: Apache-2.0 +# + +include(platform/ext/target/nordic_nrf/common/nrf9120/preload.cmake) diff --git a/modules/trusted-firmware-m/nordic_nrf/nrf9160/CMakeLists.txt b/modules/trusted-firmware-m/nordic_nrf/nrf9160/CMakeLists.txt new file mode 100644 index 00000000000..a84c6fd9fd5 --- /dev/null +++ b/modules/trusted-firmware-m/nordic_nrf/nrf9160/CMakeLists.txt @@ -0,0 +1,12 @@ +# +# Copyright (c) 2023, Nordic Semiconductor ASA. +# +# SPDX-License-Identifier: Apache-2.0 +# + +set(NRF_BOARD_SELECTED True) +set(NRF_SOC_VARIANT nrf91) + +add_subdirectory(${Trusted\ Firmware\ M_SOURCE_DIR}/platform/ext/target/nordic_nrf/common/nrf91 nrf91) + +add_subdirectory(.. common) diff --git a/modules/trusted-firmware-m/nordic_nrf/nrf9160/config.cmake b/modules/trusted-firmware-m/nordic_nrf/nrf9160/config.cmake new file mode 100644 index 00000000000..3f58e7b89eb --- /dev/null +++ b/modules/trusted-firmware-m/nordic_nrf/nrf9160/config.cmake @@ -0,0 +1,8 @@ +# +# Copyright (c) 2023, Nordic Semiconductor ASA. +# +# SPDX-License-Identifier: Apache-2.0 +# + +set(PLATFORM_PATH platform/ext/target/nordic_nrf/) +include(${PLATFORM_PATH}/common/nrf91/config.cmake) diff --git a/modules/trusted-firmware-m/nordic_nrf/nrf9160/preload.cmake b/modules/trusted-firmware-m/nordic_nrf/nrf9160/preload.cmake new file mode 100644 index 00000000000..364480a6f7f --- /dev/null +++ b/modules/trusted-firmware-m/nordic_nrf/nrf9160/preload.cmake @@ -0,0 +1,7 @@ +# +# Copyright (c) 2023, Nordic Semiconductor ASA. +# +# SPDX-License-Identifier: Apache-2.0 +# + +include(platform/ext/target/nordic_nrf/common/nrf9160/preload.cmake) diff --git a/modules/trusted-firmware-m/nordic_nrf/src/tfm_hal_platform.c b/modules/trusted-firmware-m/nordic_nrf/src/tfm_hal_platform.c new file mode 100644 index 00000000000..508c1945910 --- /dev/null +++ b/modules/trusted-firmware-m/nordic_nrf/src/tfm_hal_platform.c @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "tfm_hal_defs.h" +#include "tfm_hal_platform_common.h" + +enum tfm_hal_status_t tfm_hal_platform_init(void) +{ + return tfm_hal_platform_common_init(); +} diff --git a/modules/trusted-firmware-m/nordic_nrf/src/tfm_platform_system.c b/modules/trusted-firmware-m/nordic_nrf/src/tfm_platform_system.c new file mode 100644 index 00000000000..b96e1fe1188 --- /dev/null +++ b/modules/trusted-firmware-m/nordic_nrf/src/tfm_platform_system.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "platform/include/tfm_platform_system.h" +#include "cmsis.h" +#include "tfm_platform_hal_ioctl.h" +#include "tfm_ioctl_core_api.h" + +void tfm_platform_hal_system_reset(void) +{ + /* Reset the system */ + NVIC_SystemReset(); +} + +enum tfm_platform_err_t tfm_platform_hal_ioctl(tfm_platform_ioctl_req_t request, + psa_invec *in_vec, + psa_outvec *out_vec) +{ + /* Core IOCTL services */ + switch (request) { + case TFM_PLATFORM_IOCTL_READ_SERVICE: + return tfm_platform_hal_read_service(in_vec, out_vec); +#if defined(GPIO_PIN_CNF_MCUSEL_Msk) + case TFM_PLATFORM_IOCTL_GPIO_SERVICE: + return tfm_platform_hal_gpio_service(in_vec, out_vec); +#endif /* defined(GPIO_PIN_CNF_MCUSEL_Msk) */ + + + /* Board specific IOCTL services */ + + /* Not a supported IOCTL service.*/ + default: + return TFM_PLATFORM_ERR_NOT_SUPPORTED; + } +} diff --git a/samples/bluetooth/beacon/prj-coex.conf b/samples/bluetooth/beacon/prj-coex.conf index b16487f3999..eb9cd4bb16e 100644 --- a/samples/bluetooth/beacon/prj-coex.conf +++ b/samples/bluetooth/beacon/prj-coex.conf @@ -3,5 +3,4 @@ CONFIG_LOG=y CONFIG_BT_DEVICE_NAME="Test beacon" CONFIG_BT_LL_SW_SPLIT=y -CONFIG_BT_CTLR_COEX_DRIVERS=y CONFIG_BT_CTLR_COEX_TICKER=y diff --git a/samples/bluetooth/broadcaster_multiple/src/broadcaster_multiple.c b/samples/bluetooth/broadcaster_multiple/src/broadcaster_multiple.c index 56cc210de78..30fe35d3dec 100644 --- a/samples/bluetooth/broadcaster_multiple/src/broadcaster_multiple.c +++ b/samples/bluetooth/broadcaster_multiple/src/broadcaster_multiple.c @@ -47,11 +47,18 @@ BT_AD_DATA_FORMAT_LEN_SIZE - \ BT_AD_DATA_FORMAT_TYPE_SIZE - \ BT_DEVICE_NAME_AD_DATA_LEN))) - +/* + * Datalength is an integer, so BT_MFG_DATA_LEN can not be larger than 255. + * To ensure that we need to chain PDUs we therefore add manufacturer data + * twice when chaining is enabled + */ static uint8_t mfg_data[BT_MFG_DATA_LEN] = { 0xFF, 0xFF, }; static const struct bt_data ad[] = { BT_DATA(BT_DATA_MANUFACTURER_DATA, mfg_data, sizeof(mfg_data)), +#if defined(CONFIG_BT_CTLR_ADV_DATA_CHAIN) + BT_DATA(BT_DATA_MANUFACTURER_DATA, mfg_data, sizeof(mfg_data)), +#endif }; static struct bt_le_ext_adv *adv[CONFIG_BT_EXT_ADV_MAX_ADV_SET]; diff --git a/samples/bluetooth/hci_pwr_ctrl/child_image/hci_rpmsg.conf b/samples/bluetooth/hci_pwr_ctrl/child_image/hci_rpmsg.conf new file mode 100644 index 00000000000..e6749ae6399 --- /dev/null +++ b/samples/bluetooth/hci_pwr_ctrl/child_image/hci_rpmsg.conf @@ -0,0 +1 @@ +CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL=y diff --git a/samples/bluetooth/hci_pwr_ctrl/sysbuild/hci_rpmsg.conf b/samples/bluetooth/hci_pwr_ctrl/sysbuild/hci_rpmsg.conf new file mode 100644 index 00000000000..e6749ae6399 --- /dev/null +++ b/samples/bluetooth/hci_pwr_ctrl/sysbuild/hci_rpmsg.conf @@ -0,0 +1 @@ +CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL=y diff --git a/samples/bluetooth/hci_rpmsg/nrf5340_cpunet_iso-bt_ll_sw_split.conf b/samples/bluetooth/hci_rpmsg/nrf5340_cpunet_iso-bt_ll_sw_split.conf index bb95b6ea74e..e9e5ac63483 100644 --- a/samples/bluetooth/hci_rpmsg/nrf5340_cpunet_iso-bt_ll_sw_split.conf +++ b/samples/bluetooth/hci_rpmsg/nrf5340_cpunet_iso-bt_ll_sw_split.conf @@ -112,3 +112,6 @@ CONFIG_BT_CTLR_ISOAL_SOURCES=2 # ISO Receptions CONFIG_BT_CTLR_ISO_RX_BUFFERS=8 CONFIG_BT_CTLR_ISOAL_SINKS=2 + +# Tx Power Dynamic Control +CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL=y diff --git a/samples/bluetooth/hci_rpmsg/sample.yaml b/samples/bluetooth/hci_rpmsg/sample.yaml index 3330002a59d..7cecb12be67 100644 --- a/samples/bluetooth/hci_rpmsg/sample.yaml +++ b/samples/bluetooth/hci_rpmsg/sample.yaml @@ -82,6 +82,16 @@ tests: platform_allow: nrf5340dk_nrf5340_cpunet integration_platforms: - nrf5340dk_nrf5340_cpunet + sample.bluetooth.hci_rpmsg.df.no_phy_coded.bt_ll_sw_split: + harness: bluetooth + tags: bluetooth + extra_args: + - CONF_FILE="nrf5340_cpunet_df-bt_ll_sw_split.conf" + - DTC_OVERLAY_FILE="nrf5340_cpunet_df-bt_ll_sw_split.overlay" + - CONFIG_BT_CTLR_PHY_CODED=n + platform_allow: nrf5340dk_nrf5340_cpunet + integration_platforms: + - nrf5340dk_nrf5340_cpunet sample.bluetooth.hci_rpmsg.mesh.bt_ll_sw_split: harness: bluetooth tags: bluetooth diff --git a/samples/bluetooth/hci_uart/overlay-all-bt_ll_sw_split.conf b/samples/bluetooth/hci_uart/overlay-all-bt_ll_sw_split.conf index 1f976f75f22..3774532c424 100644 --- a/samples/bluetooth/hci_uart/overlay-all-bt_ll_sw_split.conf +++ b/samples/bluetooth/hci_uart/overlay-all-bt_ll_sw_split.conf @@ -104,3 +104,6 @@ CONFIG_BT_CTLR_ISOAL_SOURCES=2 # ISO Receptions CONFIG_BT_CTLR_ISO_RX_BUFFERS=8 CONFIG_BT_CTLR_ISOAL_SINKS=2 + +# Tx Power Dynamic Control +CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL=y diff --git a/samples/bluetooth/hci_uart_async/CMakeLists.txt b/samples/bluetooth/hci_uart_async/CMakeLists.txt new file mode 100644 index 00000000000..fe45a9cc371 --- /dev/null +++ b/samples/bluetooth/hci_uart_async/CMakeLists.txt @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +project(hci_uart_async) +target_sources(app PRIVATE + src/hci_uart_async.c + src/main.c +) diff --git a/samples/bluetooth/hci_uart_async/README.rst b/samples/bluetooth/hci_uart_async/README.rst new file mode 100644 index 00000000000..75bf586868d --- /dev/null +++ b/samples/bluetooth/hci_uart_async/README.rst @@ -0,0 +1,158 @@ +.. _bluetooth-hci-uart-async-sample: + +Bluetooth: HCI UART based on ASYNC UART +####################################### + +Expose a Zephyr Bluetooth Controller over a standard Bluetooth HCI UART interface. + +This sample performs the same basic function as the HCI UART sample, but it uses the UART_ASYNC_API +instead of UART_INTERRUPT_DRIVEN API. Not all boards implement both UART APIs, so the board support +of the HCI UART sample may be different. + +Requirements +************ + +* A board with BLE support + +Default UART settings +********************* + +By default the controller builds use the following settings: + +* Baudrate: 1Mbit/s +* 8 bits, no parity, 1 stop bit +* Hardware Flow Control (RTS/CTS) enabled + +Building and Running +******************** + +This sample can be found under :zephyr_file:`samples/bluetooth/hci_uart_async` +in the Zephyr tree and is built as a standard Zephyr application. + +Using the controller with emulators and BlueZ +********************************************* + +The instructions below show how to use a Nordic nRF5x device as a Zephyr BLE +controller and expose it to Linux's BlueZ. + +First, make sure you have a recent BlueZ version installed by following the +instructions in the :ref:`bluetooth_bluez` section. + +Now build and flash the sample for the Nordic nRF5x board of your choice. +All of the Nordic Development Kits come with a Segger IC that provides a +debugger interface and a CDC ACM serial port bridge. More information can be +found in :ref:`nordic_segger`. + +For example, to build for the nRF52832 Development Kit: + +.. zephyr-app-commands:: + :zephyr-app: samples/bluetooth/hci_uart_async + :board: nrf52dk_nrf52832 + :goals: build flash + +.. _bluetooth-hci-uart-async-qemu-posix: + +Using the controller with QEMU and Native POSIX +=============================================== + +In order to use the HCI UART controller with QEMU or Native POSIX you will need +to attach it to the Linux Host first. To do so simply build the sample and +connect the UART to the Linux machine, and then attach it with this command: + +.. code-block:: console + + sudo btattach -B /dev/ttyACM0 -S 1000000 -R + +.. note:: + Depending on the serial port you are using you will need to modify the + ``/dev/ttyACM0`` string to point to the serial device your controller is + connected to. + +.. note:: + The ``-R`` flag passed to ``btattach`` instructs the kernel to avoid + interacting with the controller and instead just be aware of it in order + to proxy it to QEMU later. + +If you are running :file:`btmon` you should see a brief log showing how the +Linux kernel identifies the attached controller. + +Once the controller is attached follow the instructions in the +:ref:`bluetooth_qemu_posix` section to use QEMU with it. + +.. _bluetooth-hci-uart-async-bluez: + +Using the controller with BlueZ +=============================== + +In order to use the HCI UART controller with BlueZ you will need to attach it +to the Linux Host first. To do so simply build the sample and connect the +UART to the Linux machine, and then attach it with this command: + +.. code-block:: console + + sudo btattach -B /dev/ttyACM0 -S 1000000 + +.. note:: + Depending on the serial port you are using you will need to modify the + ``/dev/ttyACM0`` string to point to the serial device your controller is + connected to. + +If you are running :file:`btmon` you should see a comprehensive log showing how +BlueZ loads and initializes the attached controller. + +Once the controller is attached follow the instructions in the +:ref:`bluetooth_ctlr_bluez` section to use BlueZ with it. + +Debugging the controller +======================== + +The sample can be debugged using RTT since the UART is reserved used by this +application. To enable debug over RTT the debug configuration file can be used. + +.. code-block:: console + + west build samples/bluetooth/hci_uart_async -- -DEXTRA_CONFIG='debug.mixin.conf' + +Then attach RTT as described here: :ref:`Using Segger J-Link ` + +Using the controller with the Zephyr host +========================================= + +This describes how to hook up a board running this sample to a board running +an application that uses the Zephyr host. + +On the controller side, the `zephyr,bt-c2h-uart` DTS property (in the `chosen` +block) is used to select which uart device to use. For example if we want to +keep the console logs, we can keep console on uart0 and the HCI on uart1 like +so: + +.. code-block:: dts + + / { + chosen { + zephyr,console = &uart0; + zephyr,shell-uart = &uart0; + zephyr,bt-c2h-uart = &uart1; + }; + }; + +On the host application, some config options need to be used to select the H4 +driver instead of the built-in controller: + +.. code-block:: kconfig + + CONFIG_BT_HCI=y + CONFIG_BT_CTLR=n + CONFIG_BT_H4=y + +Similarly, the `zephyr,bt-uart` DTS property selects which uart to use: + +.. code-block:: dts + + / { + chosen { + zephyr,console = &uart0; + zephyr,shell-uart = &uart0; + zephyr,bt-uart = &uart1; + }; + }; diff --git a/samples/bluetooth/hci_uart_async/app.overlay b/samples/bluetooth/hci_uart_async/app.overlay new file mode 100644 index 00000000000..e9a282ad78c --- /dev/null +++ b/samples/bluetooth/hci_uart_async/app.overlay @@ -0,0 +1,31 @@ +/* This is the default app device tree overlay. This file is ignored if + * there is a board-specific overlay in `./boards`. + * + * Most boards define a convenient `&uart0`. It's used here to make the + * sample 'just work' automatically for those boards. + */ + +bt_c2h_uart: &uart0 { + status = "okay"; + current-speed = <1000000>; + hw-flow-control; +}; + +/ { + chosen { + zephyr,bt-c2h-uart = &bt_c2h_uart; + }; +}; + +/* Some boards are by default assigning the &uart0 to these other + * functions. Removing the assignments will ensure a compilation error + * instead of accidental interference. + */ +/ { + chosen { + /delete-property/ zephyr,console; + /delete-property/ zephyr,shell-uart; + /delete-property/ zephyr,uart-mcumgr; + /delete-property/ zephyr,bt-mon-uart; + }; +}; diff --git a/samples/bluetooth/hci_uart_async/debug.mixin.conf b/samples/bluetooth/hci_uart_async/debug.mixin.conf new file mode 100644 index 00000000000..3255df74018 --- /dev/null +++ b/samples/bluetooth/hci_uart_async/debug.mixin.conf @@ -0,0 +1,15 @@ +CONFIG_ASSERT_ON_ERRORS=y +CONFIG_ASSERT=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_OPTIMIZATIONS=y +CONFIG_DEBUG_THREAD_INFO=y + +# Enable RTT console +CONFIG_RTT_CONSOLE=y + +CONFIG_LOG=y +CONFIG_LOG_BUFFER_SIZE=10000 + +# This outputs all HCI traffic to a separate RTT channel. Use `btmon +# --jlink` to read it out. Add `--priority 7` for debug logs. +CONFIG_BT_DEBUG_MONITOR_RTT=y diff --git a/samples/bluetooth/hci_uart_async/prj.conf b/samples/bluetooth/hci_uart_async/prj.conf new file mode 100644 index 00000000000..1de1a46535e --- /dev/null +++ b/samples/bluetooth/hci_uart_async/prj.conf @@ -0,0 +1,25 @@ +# hci_uart_async +CONFIG_SERIAL=y +CONFIG_UART_ASYNC_API=y + +# hci_raw (dependency of hci_uart) +CONFIG_BT=y +CONFIG_BT_HCI_RAW=y +CONFIG_BT_HCI_RAW_H4=y +CONFIG_BT_HCI_RAW_H4_ENABLE=y + +# Controller configuration. Modify these for your application's needs. +CONFIG_BT_MAX_CONN=16 +CONFIG_BT_BUF_ACL_RX_SIZE=255 +CONFIG_BT_BUF_CMD_TX_SIZE=255 +CONFIG_BT_BUF_EVT_DISCARDABLE_SIZE=255 + +# Send an initial HCI_Command_Complete event on boot without waiting for +# HCI_Reset. Make sure to use the same value for this setting in your +# host application. +#CONFIG_BT_WAIT_NOP=y + +# See `overlay.app`. The 'zephyr,console' chosen node is deleted there +# in case it has a interfering default. Those same boards set this +# config and it must be undone or the build will fail. +CONFIG_UART_CONSOLE=n diff --git a/samples/bluetooth/hci_uart_async/sample.yaml b/samples/bluetooth/hci_uart_async/sample.yaml new file mode 100644 index 00000000000..d0db2b90385 --- /dev/null +++ b/samples/bluetooth/hci_uart_async/sample.yaml @@ -0,0 +1,19 @@ +sample: + name: Bluetooth HCI UART Async + description: + This sample is a batteries-included example of a Bluetooth HCI UART + connectivity chip. + + It demonstrates a possible implementation of an HCI UART (H4) + interface on top of Zephyr's Bluetooth Raw API, and how to expose it + over a UART. + + This implementation is based on the Zephyr Asynchoronous UART API. +tests: + sample.bluetooth.hci_uart_async.nrf5: + harness: bluetooth + platform_allow: + - nrf52dk_nrf52832 + tags: + - uart + - bluetooth diff --git a/samples/bluetooth/hci_uart_async/src/hci_uart_async.c b/samples/bluetooth/hci_uart_async/src/hci_uart_async.c new file mode 100644 index 00000000000..4d276e42378 --- /dev/null +++ b/samples/bluetooth/hci_uart_async/src/hci_uart_async.c @@ -0,0 +1,403 @@ +/* Copyright (c) 2023 Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(hci_uart_async, LOG_LEVEL_DBG); + +static const struct device *const hci_uart_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_bt_c2h_uart)); + +static K_THREAD_STACK_DEFINE(h2c_thread_stack, CONFIG_BT_HCI_TX_STACK_SIZE); +static struct k_thread h2c_thread; + +enum h4_type { + H4_CMD = 0x01, + H4_ACL = 0x02, + H4_SCO = 0x03, + H4_EVT = 0x04, + H4_ISO = 0x05, +}; + +struct k_poll_signal uart_h2c_rx_sig; +struct k_poll_signal uart_c2h_tx_sig; + +static K_FIFO_DEFINE(c2h_queue); + +/** Send raw data on c2h UART. + * + * Blocks until completion. Not thread-safe. + * + * @retval 0 on success + * @retval -EBUSY Another transmission is in progress. This a + * thread-safety violation. + * @retval -errno @ref uart_tx error. + */ +static int uart_c2h_tx(const uint8_t *data, size_t size) +{ + int err; + struct k_poll_signal *sig = &uart_c2h_tx_sig; + struct k_poll_event done[] = { + K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_SIGNAL, K_POLL_MODE_NOTIFY_ONLY, sig), + }; + + k_poll_signal_reset(sig); + err = uart_tx(hci_uart_dev, data, size, SYS_FOREVER_US); + + if (err) { + LOG_ERR("uart c2h tx: err %d", err); + return err; + } + + err = k_poll(done, ARRAY_SIZE(done), K_FOREVER); + __ASSERT_NO_MSG(err == 0); + + return 0; +} + +/* Function expects that type is validated and only CMD, ISO or ACL will be used. */ +static uint32_t hci_payload_size(const uint8_t *hdr_buf, enum h4_type type) +{ + switch (type) { + case H4_CMD: + return ((const struct bt_hci_cmd_hdr *)hdr_buf)->param_len; + case H4_ACL: + return sys_le16_to_cpu(((const struct bt_hci_acl_hdr *)hdr_buf)->len); + case H4_ISO: + return bt_iso_hdr_len( + sys_le16_to_cpu(((const struct bt_hci_iso_hdr *)hdr_buf)->len)); + default: + LOG_ERR("Invalid type: %u", type); + return 0; + } +} + +static uint8_t hci_hdr_size(enum h4_type type) +{ + switch (type) { + case H4_CMD: + return sizeof(struct bt_hci_cmd_hdr); + case H4_ACL: + return sizeof(struct bt_hci_acl_hdr); + case H4_ISO: + return sizeof(struct bt_hci_iso_hdr); + default: + LOG_ERR("Unexpected h4 type: %u", type); + return 0; + } +} + +/** Send raw data on c2h UART. + * + * Blocks until either @p size has been received or special UART + * condition occurs on the UART RX line, like an UART break or parity + * error. + * + * Not thread-safe. + * + * @retval 0 on success + * @retval -EBUSY Another transmission is in progress. This a + * thread-safety violation. + * @retval -errno @ref uart_rx_enable error. + * @retval +stop_reason Special condition @ref uart_rx_stop_reason. + */ +static int uart_h2c_rx(uint8_t *dst, size_t size) +{ + int err; + struct k_poll_signal *sig = &uart_h2c_rx_sig; + struct k_poll_event done[] = { + K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_SIGNAL, K_POLL_MODE_NOTIFY_ONLY, sig), + }; + + k_poll_signal_reset(sig); + err = uart_rx_enable(hci_uart_dev, dst, size, SYS_FOREVER_US); + + if (err) { + LOG_ERR("uart h2c rx: err %d", err); + return err; + } + + k_poll(done, ARRAY_SIZE(done), K_FOREVER); + return sig->result; +} + +/** Inject a HCI EVT Hardware error into the c2h packet stream. + * + * This uses `bt_recv`, just as if the controller is sending the error. + */ +static void send_hw_error(void) +{ + const uint8_t err_code = 0; + const uint8_t hci_evt_hw_err[] = {BT_HCI_EVT_HARDWARE_ERROR, + sizeof(struct bt_hci_evt_hardware_error), err_code}; + + struct net_buf *buf = bt_buf_get_rx(BT_BUF_EVT, K_FOREVER); + + net_buf_add_mem(buf, hci_evt_hw_err, sizeof(hci_evt_hw_err)); + + /* Inject the message into the c2h queue. */ + bt_recv(buf); + + /* The c2h thread will send the message at some point. The host + * will receive it and reset the controller. + */ +} + +static void recover_sync_by_reset_pattern(void) +{ + /* { H4_CMD, le_16(HCI_CMD_OP_RESET), len=0 } */ + const uint8_t h4_cmd_reset[] = {0x01, 0x03, 0x0C, 0x00}; + const uint32_t reset_pattern = sys_get_be32(h4_cmd_reset); + int err; + struct net_buf *h2c_cmd_reset; + uint32_t shift_register = 0; + + LOG_DBG("Looking for reset pattern"); + + while (shift_register != reset_pattern) { + uint8_t read_byte; + + uart_h2c_rx(&read_byte, sizeof(uint8_t)); + LOG_DBG("h2c: 0x%02x", read_byte); + shift_register = (shift_register * 0x100) + read_byte; + } + + LOG_DBG("Pattern found"); + h2c_cmd_reset = bt_buf_get_tx(BT_BUF_H4, K_FOREVER, h4_cmd_reset, sizeof(h4_cmd_reset)); + LOG_DBG("Fowarding reset"); + + err = bt_send(h2c_cmd_reset); + __ASSERT(!err, "Failed to send reset: %d", err); +} + +static void h2c_h4_transport(void) +{ + /* When entering this function, the h2c stream should be + * 'synchronized'. I.e. The stream should be at a H4 packet + * boundary. + * + * This function returns to signal a desynchronization. + * When this happens, the caller should resynchronize before + * entering this function again. It's up to the caller to decide + * how to resynchronize. + */ + + for (;;) { + int err; + struct net_buf *buf; + uint8_t h4_type; + uint8_t hdr_size; + uint8_t *hdr_buf; + uint16_t payload_size; + + LOG_DBG("h2c: listening"); + + /* Read H4 type. */ + err = uart_h2c_rx(&h4_type, sizeof(uint8_t)); + + if (err) { + return; + } + LOG_DBG("h2c: h4_type %d", h4_type); + + /* Allocate buf. */ + buf = bt_buf_get_tx(BT_BUF_H4, K_FOREVER, &h4_type, sizeof(h4_type)); + LOG_DBG("h2c: buf %p", buf); + + if (!buf) { + /* `h4_type` was invalid. */ + __ASSERT_NO_MSG(hci_hdr_size(h4_type) == 0); + + LOG_WRN("bt_buf_get_tx failed h4_type %d", h4_type); + return; + } + + /* Read HCI header. */ + hdr_size = hci_hdr_size(h4_type); + hdr_buf = net_buf_add(buf, hdr_size); + + err = uart_h2c_rx(hdr_buf, hdr_size); + if (err) { + net_buf_unref(buf); + return; + } + LOG_HEXDUMP_DBG(hdr_buf, hdr_size, "h2c: hci hdr"); + + /* Read HCI payload. */ + payload_size = hci_payload_size(hdr_buf, h4_type); + + LOG_DBG("h2c: payload_size %u", payload_size); + + if (payload_size <= net_buf_tailroom(buf)) { + uint8_t *payload_dst = net_buf_add(buf, payload_size); + + err = uart_h2c_rx(payload_dst, payload_size); + if (err) { + net_buf_unref(buf); + return; + } + LOG_HEXDUMP_DBG(payload_dst, payload_size, "h2c: hci payload"); + } else { + /* Discard oversize packet. */ + uint8_t *discard_dst; + uint16_t discard_size; + + LOG_WRN("h2c: Discarding oversize h4_type %d payload_size %d.", h4_type, + payload_size); + + /* Reset `buf` so all of it is available. */ + net_buf_reset(buf); + discard_dst = net_buf_tail(buf); + discard_size = net_buf_max_len(buf); + + while (payload_size) { + uint16_t read_size = MIN(payload_size, discard_size); + + err = uart_h2c_rx(discard_dst, read_size); + if (err) { + net_buf_unref(buf); + return; + } + + payload_size -= read_size; + } + + net_buf_unref(buf); + buf = NULL; + } + + LOG_DBG("h2c: packet done"); + + /* Route buf to Controller. */ + if (buf) { + err = bt_send(buf); + if (err) { + /* This is not a transport error. */ + LOG_ERR("bt_send err %d", err); + net_buf_unref(buf); + buf = NULL; + } + } + + k_yield(); + } +} + +static void h2c_thread_entry(void *p1, void *p2, void *p3) +{ + k_thread_name_set(k_current_get(), "HCI TX (h2c)"); + + for (;;) { + LOG_DBG("Synchronized"); + h2c_h4_transport(); + LOG_WRN("Desynchronized"); + send_hw_error(); + recover_sync_by_reset_pattern(); + } +} + +void callback(const struct device *dev, struct uart_event *evt, void *user_data) +{ + ARG_UNUSED(user_data); + + if (evt->type == UART_RX_DISABLED) { + (void)k_poll_signal_raise(&uart_h2c_rx_sig, 0); + } else if (evt->type == UART_RX_STOPPED) { + (void)k_poll_signal_raise(&uart_h2c_rx_sig, evt->data.rx_stop.reason); + } else if (evt->type == UART_TX_DONE) { + (void)k_poll_signal_raise(&uart_c2h_tx_sig, 0); + } +} + +static int hci_uart_init(void) +{ + int err; + + k_poll_signal_init(&uart_h2c_rx_sig); + k_poll_signal_init(&uart_c2h_tx_sig); + + LOG_DBG(""); + + if (!device_is_ready(hci_uart_dev)) { + LOG_ERR("HCI UART %s is not ready", hci_uart_dev->name); + return -EINVAL; + } + + BUILD_ASSERT(IS_ENABLED(CONFIG_UART_ASYNC_API)); + err = uart_callback_set(hci_uart_dev, callback, NULL); + + /* Note: Asserts if CONFIG_UART_ASYNC_API is not enabled for `hci_uart_dev`. */ + __ASSERT(!err, "err %d", err); + + return 0; +} + +SYS_INIT(hci_uart_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEVICE); + +const struct { + uint8_t h4; + struct bt_hci_evt_hdr hdr; + struct bt_hci_evt_cmd_complete cc; +} __packed cc_evt = { + .h4 = H4_EVT, + .hdr = {.evt = BT_HCI_EVT_CMD_COMPLETE, .len = sizeof(struct bt_hci_evt_cmd_complete)}, + .cc = {.ncmd = 1, .opcode = sys_cpu_to_le16(BT_OP_NOP)}, +}; + +static void c2h_thread_entry(void) +{ + k_thread_name_set(k_current_get(), "HCI RX (c2h)"); + + if (IS_ENABLED(CONFIG_BT_WAIT_NOP)) { + uart_c2h_tx((char *)&cc_evt, sizeof(cc_evt)); + } + + for (;;) { + struct net_buf *buf; + + buf = net_buf_get(&c2h_queue, K_FOREVER); + uart_c2h_tx(buf->data, buf->len); + net_buf_unref(buf); + } +} + +void hci_uart_main(void) +{ + int err; + + err = bt_enable_raw(&c2h_queue); + __ASSERT_NO_MSG(!err); + + /* TX thread. */ + k_thread_create(&h2c_thread, h2c_thread_stack, K_THREAD_STACK_SIZEOF(h2c_thread_stack), + h2c_thread_entry, NULL, NULL, NULL, K_PRIO_COOP(7), 0, K_NO_WAIT); + + /* Reuse current thread as RX thread. */ + c2h_thread_entry(); +} diff --git a/samples/bluetooth/hci_uart_async/src/main.c b/samples/bluetooth/hci_uart_async/src/main.c new file mode 100644 index 00000000000..2d38d83e45c --- /dev/null +++ b/samples/bluetooth/hci_uart_async/src/main.c @@ -0,0 +1,11 @@ +/* Copyright (c) 2023 Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + */ + +extern int hci_uart_main(void); + +int main(void) +{ + hci_uart_main(); + return 0; +} diff --git a/samples/bluetooth/mesh/boards/nrf5340dk_nrf5340_cpuapp_ns.conf b/samples/bluetooth/mesh/boards/nrf5340dk_nrf5340_cpuapp_ns.conf index c638a292c91..4693e4d1f78 100644 --- a/samples/bluetooth/mesh/boards/nrf5340dk_nrf5340_cpuapp_ns.conf +++ b/samples/bluetooth/mesh/boards/nrf5340dk_nrf5340_cpuapp_ns.conf @@ -1,3 +1,8 @@ +# The option adds TinyCrypt based bt_rand. +CONFIG_BT_HOST_CRYPTO=n +# The option adds GATT caching feature that is based on TinyCrypt. +CONFIG_BT_GATT_CACHING=n + # Known issue: non secure platforms do not work with settings subsystem. CONFIG_SETTINGS=n CONFIG_BT_SETTINGS=n diff --git a/samples/bluetooth/mesh_demo/boards/nrf5340dk_nrf5340_cpuapp_ns.conf b/samples/bluetooth/mesh_demo/boards/nrf5340dk_nrf5340_cpuapp_ns.conf index c638a292c91..4693e4d1f78 100644 --- a/samples/bluetooth/mesh_demo/boards/nrf5340dk_nrf5340_cpuapp_ns.conf +++ b/samples/bluetooth/mesh_demo/boards/nrf5340dk_nrf5340_cpuapp_ns.conf @@ -1,3 +1,8 @@ +# The option adds TinyCrypt based bt_rand. +CONFIG_BT_HOST_CRYPTO=n +# The option adds GATT caching feature that is based on TinyCrypt. +CONFIG_BT_GATT_CACHING=n + # Known issue: non secure platforms do not work with settings subsystem. CONFIG_SETTINGS=n CONFIG_BT_SETTINGS=n diff --git a/samples/bluetooth/mesh_provisioner/boards/nrf5340dk_nrf5340_cpuapp_ns.conf b/samples/bluetooth/mesh_provisioner/boards/nrf5340dk_nrf5340_cpuapp_ns.conf index c638a292c91..c3d134592fc 100644 --- a/samples/bluetooth/mesh_provisioner/boards/nrf5340dk_nrf5340_cpuapp_ns.conf +++ b/samples/bluetooth/mesh_provisioner/boards/nrf5340dk_nrf5340_cpuapp_ns.conf @@ -1,3 +1,6 @@ +# The option adds TinyCrypt based bt_rand. +CONFIG_BT_HOST_CRYPTO=n + # Known issue: non secure platforms do not work with settings subsystem. CONFIG_SETTINGS=n CONFIG_BT_SETTINGS=n diff --git a/samples/boards/nrf/nrfx_prs/src/main.c b/samples/boards/nrf/nrfx_prs/src/main.c index 618c0718143..934bbbe834d 100644 --- a/samples/boards/nrf/nrfx_prs/src/main.c +++ b/samples/boards/nrf/nrfx_prs/src/main.c @@ -191,7 +191,7 @@ static bool spim_transfer(const uint8_t *tx_data, size_t tx_data_len, static void uarte_handler(const nrfx_uarte_event_t *p_event, void *p_context) { if (p_event->type == NRFX_UARTE_EVT_RX_DONE) { - received = p_event->data.rx.bytes; + received = p_event->data.rx.length; k_sem_give(&transfer_finished); } else if (p_event->type == NRFX_UARTE_EVT_ERROR) { received = 0; diff --git a/samples/drivers/soc_flash_nrf/README.rst b/samples/drivers/soc_flash_nrf/README.rst index df97e4b65cc..14c39851f66 100644 --- a/samples/drivers/soc_flash_nrf/README.rst +++ b/samples/drivers/soc_flash_nrf/README.rst @@ -61,7 +61,7 @@ Sample Output Data read: 1234 Data read matches data written. Good! - Test 3: Flash erase (4 pages at 0x80000) + Test 3: Flash erase (2 pages at 0x80000) Flash erase succeeded! Test 4: Flash write (word array 2) @@ -130,3 +130,5 @@ Sample Output Test 8: Write block size API write-block-size = 1 + + Finished! diff --git a/samples/drivers/soc_flash_nrf/prj.conf b/samples/drivers/soc_flash_nrf/prj.conf index 9909ef3b29f..48e64121b6a 100644 --- a/samples/drivers/soc_flash_nrf/prj.conf +++ b/samples/drivers/soc_flash_nrf/prj.conf @@ -3,3 +3,7 @@ CONFIG_FLASH=y CONFIG_FLASH_PAGE_LAYOUT=y CONFIG_MPU_ALLOW_FLASH_WRITE=y CONFIG_SOC_FLASH_NRF_EMULATE_ONE_BYTE_WRITE_ACCESS=y +CONFIG_FCB=y +CONFIG_FLASH_MAP=y +CONFIG_SETTINGS=y +CONFIG_SETTINGS_FCB=y diff --git a/samples/drivers/soc_flash_nrf/sample.yaml b/samples/drivers/soc_flash_nrf/sample.yaml index 6557988b6e3..c493c553e1f 100644 --- a/samples/drivers/soc_flash_nrf/sample.yaml +++ b/samples/drivers/soc_flash_nrf/sample.yaml @@ -30,3 +30,4 @@ tests: - "Data read matches data written. Good!" - "SoC flash consists of \\d+ pages." - "write-block-size = 1" + - "Finished!" diff --git a/samples/drivers/soc_flash_nrf/src/main.c b/samples/drivers/soc_flash_nrf/src/main.c index a438a67cf2f..29606a9ca5d 100644 --- a/samples/drivers/soc_flash_nrf/src/main.c +++ b/samples/drivers/soc_flash_nrf/src/main.c @@ -13,11 +13,7 @@ #include -#ifdef CONFIG_TRUSTED_EXECUTION_NONSECURE -#define TEST_PARTITION slot1_ns_partition -#else -#define TEST_PARTITION slot1_partition -#endif +#define TEST_PARTITION storage_partition #define TEST_PARTITION_OFFSET FIXED_PARTITION_OFFSET(TEST_PARTITION) #define TEST_PARTITION_DEVICE FIXED_PARTITION_DEVICE(TEST_PARTITION) @@ -84,9 +80,9 @@ int main(void) } } - offset = TEST_PARTITION_OFFSET - FLASH_PAGE_SIZE * 2; - printf("\nTest 3: Flash erase (4 pages at 0x%x)\n", offset); - if (flash_erase(flash_dev, offset, FLASH_PAGE_SIZE * 4) != 0) { + offset = TEST_PARTITION_OFFSET; + printf("\nTest 3: Flash erase (2 pages at 0x%x)\n", offset); + if (flash_erase(flash_dev, offset, FLASH_PAGE_SIZE * 2) != 0) { printf(" Flash erase failed!\n"); } else { printf(" Flash erase succeeded!\n"); @@ -191,5 +187,7 @@ int main(void) printf("\nTest 8: Write block size API\n"); printf(" write-block-size = %u\n", flash_get_write_block_size(flash_dev)); + + printf("\nFinished!\n"); return 0; } diff --git a/samples/net/dhcpv4_client/overlay-nrf700x.conf b/samples/net/dhcpv4_client/overlay-nrf700x.conf new file mode 100644 index 00000000000..2d552e9c623 --- /dev/null +++ b/samples/net/dhcpv4_client/overlay-nrf700x.conf @@ -0,0 +1,14 @@ +# Wi-Fi +CONFIG_WIFI=y +CONFIG_WIFI_NRF700X=y +CONFIG_WPA_SUPP=y +CONFIG_NET_L2_ETHERNET=y + +# Connection manager +CONFIG_NET_CONNECTION_MANAGER=y + +# Credentials +CONFIG_WIFI_CREDENTIALS=y +CONFIG_WIFI_CREDENTIALS_STATIC=y +CONFIG_WIFI_CREDENTIALS_STATIC_SSID="" +CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="" diff --git a/samples/net/dns_resolve/overlay-nrf700x.conf b/samples/net/dns_resolve/overlay-nrf700x.conf new file mode 100644 index 00000000000..aa59e5d5ea2 --- /dev/null +++ b/samples/net/dns_resolve/overlay-nrf700x.conf @@ -0,0 +1,18 @@ +# Wi-Fi +CONFIG_WIFI=y +CONFIG_WIFI_NRF700X=y +CONFIG_WPA_SUPP=y +CONFIG_NET_L2_ETHERNET=y + +# DHCPv4 +CONFIG_NET_CONFIG_MY_IPV4_ADDR="" +CONFIG_NET_DHCPV4=y + +# Connection manager +CONFIG_NET_CONNECTION_MANAGER=y + +# Credentials +CONFIG_WIFI_CREDENTIALS=y +CONFIG_WIFI_CREDENTIALS_STATIC=y +CONFIG_WIFI_CREDENTIALS_STATIC_SSID="" +CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="" diff --git a/samples/net/ipv4_autoconf/overlay-nrf700x.conf b/samples/net/ipv4_autoconf/overlay-nrf700x.conf new file mode 100644 index 00000000000..2d552e9c623 --- /dev/null +++ b/samples/net/ipv4_autoconf/overlay-nrf700x.conf @@ -0,0 +1,14 @@ +# Wi-Fi +CONFIG_WIFI=y +CONFIG_WIFI_NRF700X=y +CONFIG_WPA_SUPP=y +CONFIG_NET_L2_ETHERNET=y + +# Connection manager +CONFIG_NET_CONNECTION_MANAGER=y + +# Credentials +CONFIG_WIFI_CREDENTIALS=y +CONFIG_WIFI_CREDENTIALS_STATIC=y +CONFIG_WIFI_CREDENTIALS_STATIC_SSID="" +CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="" diff --git a/samples/net/lwm2m_client/overlay-queue.conf b/samples/net/lwm2m_client/overlay-queue.conf index 060767dcb89..1adc9eda5ec 100644 --- a/samples/net/lwm2m_client/overlay-queue.conf +++ b/samples/net/lwm2m_client/overlay-queue.conf @@ -1,2 +1,6 @@ CONFIG_LWM2M_QUEUE_MODE_ENABLED=y -CONFIG_LWM2M_ENGINE_DEFAULT_LIFETIME=180 +CONFIG_LWM2M_QUEUE_MODE_UPTIME=20 +# Default lifetime is 1 day +CONFIG_LWM2M_ENGINE_DEFAULT_LIFETIME=86400 +# Send update once an hour +CONFIG_LWM2M_UPDATE_PERIOD=3600 diff --git a/samples/net/lwm2m_client/src/lwm2m-client.c b/samples/net/lwm2m_client/src/lwm2m-client.c index e20008c2f66..748f654d9ee 100644 --- a/samples/net/lwm2m_client/src/lwm2m-client.c +++ b/samples/net/lwm2m_client/src/lwm2m-client.c @@ -231,6 +231,9 @@ static void rd_client_event(struct lwm2m_ctx *client, case LWM2M_RD_CLIENT_EVENT_REG_UPDATE: LOG_DBG("Registration update"); break; + case LWM2M_RD_CLIENT_EVENT_DEREGISTER: + LOG_DBG("Client De-register"); + break; } } diff --git a/samples/net/mdns_responder/overlay-nrf700x.conf b/samples/net/mdns_responder/overlay-nrf700x.conf new file mode 100644 index 00000000000..aa59e5d5ea2 --- /dev/null +++ b/samples/net/mdns_responder/overlay-nrf700x.conf @@ -0,0 +1,18 @@ +# Wi-Fi +CONFIG_WIFI=y +CONFIG_WIFI_NRF700X=y +CONFIG_WPA_SUPP=y +CONFIG_NET_L2_ETHERNET=y + +# DHCPv4 +CONFIG_NET_CONFIG_MY_IPV4_ADDR="" +CONFIG_NET_DHCPV4=y + +# Connection manager +CONFIG_NET_CONNECTION_MANAGER=y + +# Credentials +CONFIG_WIFI_CREDENTIALS=y +CONFIG_WIFI_CREDENTIALS_STATIC=y +CONFIG_WIFI_CREDENTIALS_STATIC_SSID="" +CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="" diff --git a/samples/net/mqtt_publisher/overlay-nrf700x.conf b/samples/net/mqtt_publisher/overlay-nrf700x.conf new file mode 100644 index 00000000000..a812c7896f6 --- /dev/null +++ b/samples/net/mqtt_publisher/overlay-nrf700x.conf @@ -0,0 +1,19 @@ +# Wi-Fi +CONFIG_WIFI=y +CONFIG_WIFI_NRF700X=y +CONFIG_WPA_SUPP=y +CONFIG_NET_L2_ETHERNET=y + +# DHCPv4 +CONFIG_NET_CONFIG_MY_IPV4_ADDR="" +CONFIG_NET_CONFIG_NEED_IPV4=y +CONFIG_NET_DHCPV4=y + +# Connection manager +CONFIG_NET_CONNECTION_MANAGER=y + +# Credentials +CONFIG_WIFI_CREDENTIALS=y +CONFIG_WIFI_CREDENTIALS_STATIC=y +CONFIG_WIFI_CREDENTIALS_STATIC_SSID="" +CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="" diff --git a/samples/net/mqtt_sn_publisher/overlay-nrf700x.conf b/samples/net/mqtt_sn_publisher/overlay-nrf700x.conf new file mode 100644 index 00000000000..cbc47b96572 --- /dev/null +++ b/samples/net/mqtt_sn_publisher/overlay-nrf700x.conf @@ -0,0 +1,20 @@ +CONFIG_POSIX_MAX_FDS=16 + +# Wi-Fi +CONFIG_WIFI=y +CONFIG_WIFI_NRF700X=y +CONFIG_WPA_SUPP=y +CONFIG_NET_L2_ETHERNET=y + +# DHCPv4 +CONFIG_NET_CONFIG_MY_IPV4_ADDR="" +CONFIG_NET_DHCPV4=y + +# Connection manager +CONFIG_NET_CONNECTION_MANAGER=y + +# Credentials +CONFIG_WIFI_CREDENTIALS=y +CONFIG_WIFI_CREDENTIALS_STATIC=y +CONFIG_WIFI_CREDENTIALS_STATIC_SSID="" +CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="" diff --git a/samples/net/sockets/coap_client/overlay-nrf700x.conf b/samples/net/sockets/coap_client/overlay-nrf700x.conf new file mode 100644 index 00000000000..a0e436e3537 --- /dev/null +++ b/samples/net/sockets/coap_client/overlay-nrf700x.conf @@ -0,0 +1,16 @@ +CONFIG_HEAP_MEM_POOL_SIZE=153000 + +# Wi-Fi +CONFIG_WIFI=y +CONFIG_WIFI_NRF700X=y +CONFIG_WPA_SUPP=y +CONFIG_NET_L2_ETHERNET=y + +# Connection manager +CONFIG_NET_CONNECTION_MANAGER=y + +# Credentials +CONFIG_WIFI_CREDENTIALS=y +CONFIG_WIFI_CREDENTIALS_STATIC=y +CONFIG_WIFI_CREDENTIALS_STATIC_SSID="" +CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="" diff --git a/samples/net/sockets/coap_server/overlay-nrf700x.conf b/samples/net/sockets/coap_server/overlay-nrf700x.conf new file mode 100644 index 00000000000..4817a4f73ba --- /dev/null +++ b/samples/net/sockets/coap_server/overlay-nrf700x.conf @@ -0,0 +1,26 @@ +CONFIG_HEAP_MEM_POOL_SIZE=153000 + +# Wi-Fi +CONFIG_WIFI=y +CONFIG_WIFI_NRF700X=y +CONFIG_WPA_SUPP=y +CONFIG_NET_L2_ETHERNET=y + +# DHCPv4 +CONFIG_NET_CONFIG_MY_IPV4_ADDR="" +CONFIG_NET_DHCPV4=y + +# The sample can run either IPv4 or IPv6, not both +CONFIG_NET_IPV6=n +CONFIG_NET_CONFIG_NEED_IPV6=n +CONFIG_NET_IPV4=y +CONFIG_NET_CONFIG_NEED_IPV4=y + +# Connection manager +CONFIG_NET_CONNECTION_MANAGER=y + +# Credentials +CONFIG_WIFI_CREDENTIALS=y +CONFIG_WIFI_CREDENTIALS_STATIC=y +CONFIG_WIFI_CREDENTIALS_STATIC_SSID="" +CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="" diff --git a/samples/net/sockets/echo/overlay-nrf700x.conf b/samples/net/sockets/echo/overlay-nrf700x.conf new file mode 100644 index 00000000000..aa59e5d5ea2 --- /dev/null +++ b/samples/net/sockets/echo/overlay-nrf700x.conf @@ -0,0 +1,18 @@ +# Wi-Fi +CONFIG_WIFI=y +CONFIG_WIFI_NRF700X=y +CONFIG_WPA_SUPP=y +CONFIG_NET_L2_ETHERNET=y + +# DHCPv4 +CONFIG_NET_CONFIG_MY_IPV4_ADDR="" +CONFIG_NET_DHCPV4=y + +# Connection manager +CONFIG_NET_CONNECTION_MANAGER=y + +# Credentials +CONFIG_WIFI_CREDENTIALS=y +CONFIG_WIFI_CREDENTIALS_STATIC=y +CONFIG_WIFI_CREDENTIALS_STATIC_SSID="" +CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="" diff --git a/samples/net/sockets/echo_async/overlay-nrf700x.conf b/samples/net/sockets/echo_async/overlay-nrf700x.conf new file mode 100644 index 00000000000..aa59e5d5ea2 --- /dev/null +++ b/samples/net/sockets/echo_async/overlay-nrf700x.conf @@ -0,0 +1,18 @@ +# Wi-Fi +CONFIG_WIFI=y +CONFIG_WIFI_NRF700X=y +CONFIG_WPA_SUPP=y +CONFIG_NET_L2_ETHERNET=y + +# DHCPv4 +CONFIG_NET_CONFIG_MY_IPV4_ADDR="" +CONFIG_NET_DHCPV4=y + +# Connection manager +CONFIG_NET_CONNECTION_MANAGER=y + +# Credentials +CONFIG_WIFI_CREDENTIALS=y +CONFIG_WIFI_CREDENTIALS_STATIC=y +CONFIG_WIFI_CREDENTIALS_STATIC_SSID="" +CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="" diff --git a/samples/net/sockets/echo_client/overlay-nrf700x.conf b/samples/net/sockets/echo_client/overlay-nrf700x.conf new file mode 100644 index 00000000000..cbc47b96572 --- /dev/null +++ b/samples/net/sockets/echo_client/overlay-nrf700x.conf @@ -0,0 +1,20 @@ +CONFIG_POSIX_MAX_FDS=16 + +# Wi-Fi +CONFIG_WIFI=y +CONFIG_WIFI_NRF700X=y +CONFIG_WPA_SUPP=y +CONFIG_NET_L2_ETHERNET=y + +# DHCPv4 +CONFIG_NET_CONFIG_MY_IPV4_ADDR="" +CONFIG_NET_DHCPV4=y + +# Connection manager +CONFIG_NET_CONNECTION_MANAGER=y + +# Credentials +CONFIG_WIFI_CREDENTIALS=y +CONFIG_WIFI_CREDENTIALS_STATIC=y +CONFIG_WIFI_CREDENTIALS_STATIC_SSID="" +CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="" diff --git a/samples/net/sockets/echo_server/overlay-nrf700x.conf b/samples/net/sockets/echo_server/overlay-nrf700x.conf new file mode 100644 index 00000000000..cbc47b96572 --- /dev/null +++ b/samples/net/sockets/echo_server/overlay-nrf700x.conf @@ -0,0 +1,20 @@ +CONFIG_POSIX_MAX_FDS=16 + +# Wi-Fi +CONFIG_WIFI=y +CONFIG_WIFI_NRF700X=y +CONFIG_WPA_SUPP=y +CONFIG_NET_L2_ETHERNET=y + +# DHCPv4 +CONFIG_NET_CONFIG_MY_IPV4_ADDR="" +CONFIG_NET_DHCPV4=y + +# Connection manager +CONFIG_NET_CONNECTION_MANAGER=y + +# Credentials +CONFIG_WIFI_CREDENTIALS=y +CONFIG_WIFI_CREDENTIALS_STATIC=y +CONFIG_WIFI_CREDENTIALS_STATIC_SSID="" +CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="" diff --git a/samples/net/sockets/http_get/overlay-nrf700x.conf b/samples/net/sockets/http_get/overlay-nrf700x.conf new file mode 100644 index 00000000000..aa59e5d5ea2 --- /dev/null +++ b/samples/net/sockets/http_get/overlay-nrf700x.conf @@ -0,0 +1,18 @@ +# Wi-Fi +CONFIG_WIFI=y +CONFIG_WIFI_NRF700X=y +CONFIG_WPA_SUPP=y +CONFIG_NET_L2_ETHERNET=y + +# DHCPv4 +CONFIG_NET_CONFIG_MY_IPV4_ADDR="" +CONFIG_NET_DHCPV4=y + +# Connection manager +CONFIG_NET_CONNECTION_MANAGER=y + +# Credentials +CONFIG_WIFI_CREDENTIALS=y +CONFIG_WIFI_CREDENTIALS_STATIC=y +CONFIG_WIFI_CREDENTIALS_STATIC_SSID="" +CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="" diff --git a/samples/net/sockets/sntp_client/overlay-nrf700x.conf b/samples/net/sockets/sntp_client/overlay-nrf700x.conf new file mode 100644 index 00000000000..aa59e5d5ea2 --- /dev/null +++ b/samples/net/sockets/sntp_client/overlay-nrf700x.conf @@ -0,0 +1,18 @@ +# Wi-Fi +CONFIG_WIFI=y +CONFIG_WIFI_NRF700X=y +CONFIG_WPA_SUPP=y +CONFIG_NET_L2_ETHERNET=y + +# DHCPv4 +CONFIG_NET_CONFIG_MY_IPV4_ADDR="" +CONFIG_NET_DHCPV4=y + +# Connection manager +CONFIG_NET_CONNECTION_MANAGER=y + +# Credentials +CONFIG_WIFI_CREDENTIALS=y +CONFIG_WIFI_CREDENTIALS_STATIC=y +CONFIG_WIFI_CREDENTIALS_STATIC_SSID="" +CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="" diff --git a/samples/net/syslog_net/overlay-nrf700x.conf b/samples/net/syslog_net/overlay-nrf700x.conf new file mode 100644 index 00000000000..aa59e5d5ea2 --- /dev/null +++ b/samples/net/syslog_net/overlay-nrf700x.conf @@ -0,0 +1,18 @@ +# Wi-Fi +CONFIG_WIFI=y +CONFIG_WIFI_NRF700X=y +CONFIG_WPA_SUPP=y +CONFIG_NET_L2_ETHERNET=y + +# DHCPv4 +CONFIG_NET_CONFIG_MY_IPV4_ADDR="" +CONFIG_NET_DHCPV4=y + +# Connection manager +CONFIG_NET_CONNECTION_MANAGER=y + +# Credentials +CONFIG_WIFI_CREDENTIALS=y +CONFIG_WIFI_CREDENTIALS_STATIC=y +CONFIG_WIFI_CREDENTIALS_STATIC_SSID="" +CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="" diff --git a/samples/net/telnet/overlay-nrf700x.conf b/samples/net/telnet/overlay-nrf700x.conf new file mode 100644 index 00000000000..aa59e5d5ea2 --- /dev/null +++ b/samples/net/telnet/overlay-nrf700x.conf @@ -0,0 +1,18 @@ +# Wi-Fi +CONFIG_WIFI=y +CONFIG_WIFI_NRF700X=y +CONFIG_WPA_SUPP=y +CONFIG_NET_L2_ETHERNET=y + +# DHCPv4 +CONFIG_NET_CONFIG_MY_IPV4_ADDR="" +CONFIG_NET_DHCPV4=y + +# Connection manager +CONFIG_NET_CONNECTION_MANAGER=y + +# Credentials +CONFIG_WIFI_CREDENTIALS=y +CONFIG_WIFI_CREDENTIALS_STATIC=y +CONFIG_WIFI_CREDENTIALS_STATIC_SSID="" +CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="" diff --git a/samples/posix/eventfd/prj.conf b/samples/posix/eventfd/prj.conf index 7ff74543b2f..a0bfcdf747b 100644 --- a/samples/posix/eventfd/prj.conf +++ b/samples/posix/eventfd/prj.conf @@ -1,5 +1,6 @@ # General config CONFIG_NEWLIB_LIBC=y +CONFIG_NEWLIB_LIBC_NANO=n CONFIG_POSIX_API=y CONFIG_EVENTFD=y diff --git a/samples/subsys/mgmt/mcumgr/smp_svr/child_image/hci_rpmsg.conf b/samples/subsys/mgmt/mcumgr/smp_svr/child_image/hci_rpmsg.conf new file mode 100644 index 00000000000..98260877332 --- /dev/null +++ b/samples/subsys/mgmt/mcumgr/smp_svr/child_image/hci_rpmsg.conf @@ -0,0 +1,10 @@ +# +# Copyright (c) 2022 Nordic Semiconductor +# +# SPDX-License-Identifier: Apache-2.0 +# + +CONFIG_BT_MAX_CONN=2 +CONFIG_BT_BUF_ACL_RX_SIZE=502 +CONFIG_BT_BUF_ACL_TX_SIZE=502 +CONFIG_BT_CTLR_DATA_LENGTH_MAX=251 diff --git a/samples/subsys/mgmt/mcumgr/smp_svr/sample.yaml b/samples/subsys/mgmt/mcumgr/smp_svr/sample.yaml index ed7ffe8f33e..a13270e5845 100644 --- a/samples/subsys/mgmt/mcumgr/smp_svr/sample.yaml +++ b/samples/subsys/mgmt/mcumgr/smp_svr/sample.yaml @@ -43,6 +43,19 @@ tests: - mg100 integration_platforms: - nrf52840dk_nrf52840 + # In mcuboot_flags test overlay-serial.conf is used for convenience as it is the simplest + # transport. Transport does not affect flags so it does not really matter which is selected, + # flags should affect any transport the same way. + sample.mcumgr.smp_svr.mcuboot_flags.direct_xip_withrevert: + extra_args: OVERLAY_CONFIG="overlay-serial.conf" + extra_configs: + - CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP_WITH_REVERT=y + platform_allow: + - nrf52840dk_nrf52840 + - pinnacle_100_dvk + - mg100 + integration_platforms: + - nrf52840dk_nrf52840 sample.mcumgr.smp_svr.serial-console: extra_args: OVERLAY_CONFIG="overlay-serial-console.conf" platform_allow: diff --git a/samples/subsys/mgmt/mcumgr/smp_svr/sysbuild/hci_rpmsg.conf b/samples/subsys/mgmt/mcumgr/smp_svr/sysbuild/hci_rpmsg.conf new file mode 100644 index 00000000000..98260877332 --- /dev/null +++ b/samples/subsys/mgmt/mcumgr/smp_svr/sysbuild/hci_rpmsg.conf @@ -0,0 +1,10 @@ +# +# Copyright (c) 2022 Nordic Semiconductor +# +# SPDX-License-Identifier: Apache-2.0 +# + +CONFIG_BT_MAX_CONN=2 +CONFIG_BT_BUF_ACL_RX_SIZE=502 +CONFIG_BT_BUF_ACL_TX_SIZE=502 +CONFIG_BT_CTLR_DATA_LENGTH_MAX=251 diff --git a/samples/subsys/testsuite/pytest/shell/pytest/test_shell.py b/samples/subsys/testsuite/pytest/shell/pytest/test_shell.py index 37efa74795b..84d16ec1e5a 100755 --- a/samples/subsys/testsuite/pytest/shell/pytest/test_shell.py +++ b/samples/subsys/testsuite/pytest/shell/pytest/test_shell.py @@ -4,21 +4,11 @@ import logging -import pytest -from twister_harness import DeviceAdapter, Shell +from twister_harness import Shell logger = logging.getLogger(__name__) -@pytest.fixture(scope='function') -def shell(dut: DeviceAdapter) -> Shell: - """Return ready to use shell interface""" - shell = Shell(dut, timeout=20.0) - logger.info('wait for prompt') - assert shell.wait_for_prompt() - return shell - - def test_shell_print_help(shell: Shell): logger.info('send "help" command') lines = shell.exec_command('help') diff --git a/scripts/ci/check_compliance.py b/scripts/ci/check_compliance.py index 8a70ffabeeb..07d7431dd65 100755 --- a/scripts/ci/check_compliance.py +++ b/scripts/ci/check_compliance.py @@ -16,6 +16,7 @@ import tempfile import traceback import shlex +import shutil from yamllint import config, linter @@ -310,6 +311,13 @@ def get_modules(self, modules_file): modules = [name for name in os.listdir(modules_dir) if os.path.exists(os.path.join(modules_dir, name, 'Kconfig'))] + nrf_modules_dir = ZEPHYR_BASE + '/../nrf/modules' + nrf_modules = [] + if os.path.exists(nrf_modules_dir): + nrf_modules = [name for name in os.listdir(nrf_modules_dir) if + os.path.exists(os.path.join(nrf_modules_dir, name, + 'Kconfig'))] + with open(modules_file, 'r') as fp_module_file: content = fp_module_file.read() @@ -319,6 +327,15 @@ def get_modules(self, modules_file): re.sub('[^a-zA-Z0-9]', '_', module).upper(), modules_dir + '/' + module + '/Kconfig' )) + for module in nrf_modules: + fp_module_file.write("ZEPHYR_{}_KCONFIG = {}\n".format( + re.sub('[^a-zA-Z0-9]', '_', module).upper(), + nrf_modules_dir + '/' + module + '/Kconfig' + )) + fp_module_file.write("NCS_{}_KCONFIG = {}\n".format( + re.sub('[^a-zA-Z0-9]', '_', module).upper(), + modules_dir + '/' + module + '/Kconfig' + )) fp_module_file.write(content) def get_kconfig_dts(self, kconfig_dts_file): @@ -353,6 +370,8 @@ def parse_kconfig(self): if not os.path.exists(kconfig_path): self.error(kconfig_path + " not found") + kconfiglib_dir = tempfile.mkdtemp(prefix="kconfiglib_") + sys.path.insert(0, kconfig_path) # Import globally so that e.g. kconfiglib.Symbol can be referenced in # tests @@ -367,7 +386,7 @@ def parse_kconfig(self): os.environ["ARCH_DIR"] = "arch/" os.environ["BOARD_DIR"] = "boards/*/*" os.environ["ARCH"] = "*" - os.environ["KCONFIG_BINARY_DIR"] = tempfile.gettempdir() + os.environ["KCONFIG_BINARY_DIR"] = kconfiglib_dir os.environ['DEVICETREE_CONF'] = "dummy" os.environ['TOOLCHAIN_HAS_NEWLIB'] = "y" @@ -376,10 +395,9 @@ def parse_kconfig(self): os.environ["GENERATED_DTS_BOARD_CONF"] = "dummy" # For multi repo support - self.get_modules(os.path.join(tempfile.gettempdir(), "Kconfig.modules")) - + self.get_modules(os.path.join(kconfiglib_dir, "Kconfig.modules")) # For Kconfig.dts support - self.get_kconfig_dts(os.path.join(tempfile.gettempdir(), "Kconfig.dts")) + self.get_kconfig_dts(os.path.join(kconfiglib_dir, "Kconfig.dts")) # Tells Kconfiglib to generate warnings for all references to undefined # symbols within Kconfig files @@ -394,6 +412,9 @@ def parse_kconfig(self): except kconfiglib.KconfigError as e: self.failure(str(e)) raise EndTest + finally: + # Clean up the temporary directory + shutil.rmtree(kconfiglib_dir) def get_defined_syms(self, kconf): # Returns a set() with the names of all defined Kconfig symbols (with no @@ -612,6 +633,8 @@ def check_no_undef_outside_kconfig(self, kconf): # toolchain Kconfig which is sourced based on # Zephyr toolchain variant and therefore not # visible to compliance. + "BOOT_ENCRYPTION_KEY_FILE", # Used in sysbuild + "BOOT_ENCRYPT_IMAGE", # Used in sysbuild "BOOT_UPGRADE_ONLY", # Used in example adjusting MCUboot config, but # symbol is defined in MCUboot itself. "BOOT_SERIAL_BOOT_MODE", # Used in (sysbuild-based) test/ diff --git a/scripts/ci/test_plan.py b/scripts/ci/test_plan.py index 8f2f24b0825..21cac8e4914 100755 --- a/scripts/ci/test_plan.py +++ b/scripts/ci/test_plan.py @@ -12,6 +12,7 @@ import json import logging import sys +import glob from pathlib import Path from git import Repo from west.manifest import Manifest @@ -19,10 +20,17 @@ if "ZEPHYR_BASE" not in os.environ: exit("$ZEPHYR_BASE environment variable undefined.") -repository_path = Path(os.environ['ZEPHYR_BASE']) +# These are globaly used variables. They are assigned in __main__ and are visible in further methods +# however, pylint complains that it doesn't recognized them when used (used-before-assignment). +zephyr_base = Path(os.environ['ZEPHYR_BASE']) +repository_path = zephyr_base +repo_to_scan = zephyr_base +args = None + + logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO) -sys.path.append(os.path.join(repository_path, 'scripts')) +sys.path.append(os.path.join(zephyr_base, 'scripts')) import list_boards def _get_match_fn(globs, regexes): @@ -86,15 +94,20 @@ def __repr__(self): return "".format(self.name) class Filters: - def __init__(self, modified_files, pull_request=False, platforms=[]): + def __init__(self, modified_files, ignore_path, alt_tags, testsuite_root, + pull_request=False, platforms=[], detailed_test_id=True): self.modified_files = modified_files + self.testsuite_root = testsuite_root + self.resolved_files = [] self.twister_options = [] self.full_twister = False self.all_tests = [] self.tag_options = [] self.pull_request = pull_request self.platforms = platforms - self.default_run = False + self.detailed_test_id = detailed_test_id + self.ignore_path = ignore_path + self.tag_cfg_file = alt_tags def process(self): self.find_modules() @@ -103,15 +116,16 @@ def process(self): if not self.platforms: self.find_archs() self.find_boards() + self.find_excludes() - if self.default_run: - self.find_excludes(skip=["tests/*", "boards/*/*/*"]) - else: - self.find_excludes() - - def get_plan(self, options, integration=False): + def get_plan(self, options, integration=False, use_testsuite_root=True): fname = "_test_plan_partial.json" - cmd = ["scripts/twister", "-c"] + options + ["--save-tests", fname ] + cmd = [f"{zephyr_base}/scripts/twister", "-c"] + options + ["--save-tests", fname ] + if not self.detailed_test_id: + cmd += ["--no-detailed-test-id"] + if self.testsuite_root and use_testsuite_root: + for root in self.testsuite_root: + cmd+=["-T", root] if integration: cmd.append("--integration") @@ -128,7 +142,7 @@ def find_modules(self): if 'west.yml' in self.modified_files: print(f"Manifest file 'west.yml' changed") print("=========") - old_manifest_content = repo.git.show(f"{args.commits[:-2]}:west.yml") + old_manifest_content = repo_to_scan.git.show(f"{args.commits[:-2]}:west.yml") with open("west_old.yml", "w") as manifest: manifest.write(old_manifest_content) old_manifest = Manifest.from_file("west_old.yml") @@ -183,6 +197,8 @@ def find_archs(self): archs.add('riscv64') else: archs.add(p.group(1)) + # Modified file is treated as resolved, since a matching scope was found + self.resolved_files.append(f) _options = [] for arch in archs: @@ -201,6 +217,7 @@ def find_archs(self): def find_boards(self): boards = set() all_boards = set() + resolved = [] for f in self.modified_files: if f.endswith(".rst") or f.endswith(".png") or f.endswith(".jpg"): @@ -208,9 +225,14 @@ def find_boards(self): p = re.match(r"^boards\/[^/]+\/([^/]+)\/", f) if p and p.groups(): boards.add(p.group(1)) + resolved.append(f) - # Limit search to $ZEPHYR_BASE since this is where the changed files are - lb_args = argparse.Namespace(**{ 'arch_roots': [repository_path], 'board_roots': [repository_path] }) + roots = [zephyr_base] + if repository_path != zephyr_base: + roots.append(repository_path) + + # Look for boards in monitored repositories + lb_args = argparse.Namespace(**{ 'arch_roots': roots, 'board_roots': roots}) known_boards = list_boards.find_boards(lb_args) for b in boards: name_re = re.compile(b) @@ -218,10 +240,16 @@ def find_boards(self): if name_re.search(kb.name): all_boards.add(kb.name) + # If modified file is catched by "find_boards" workflow (change in "boards" dir AND board recognized) + # it means a proper testing scope for this file was found and this file can be removed + # from further consideration + for board in all_boards: + self.resolved_files.extend(list(filter(lambda f: board in f, resolved))) + _options = [] if len(all_boards) > 20: logging.warning(f"{len(boards)} boards changed, this looks like a global change, skipping test handling, revert to default.") - self.default_run = True + self.full_twister = True return for board in all_boards: @@ -237,11 +265,27 @@ def find_tests(self): if f.endswith(".rst"): continue d = os.path.dirname(f) - while d: + scope_found = False + while not scope_found and d: + head, tail = os.path.split(d) if os.path.exists(os.path.join(d, "testcase.yaml")) or \ os.path.exists(os.path.join(d, "sample.yaml")): tests.add(d) - break + # Modified file is treated as resolved, since a matching scope was found + self.resolved_files.append(f) + scope_found = True + elif tail == "common": + # Look for yamls in directories collocated with common + + yamls_found = [yaml for yaml in glob.iglob(head + '/**/testcase.yaml', recursive=True)] + yamls_found.extend([yaml for yaml in glob.iglob(head + '/**/sample.yaml', recursive=True)]) + if yamls_found: + for yaml in yamls_found: + tests.add(os.path.dirname(yaml)) + self.resolved_files.append(f) + scope_found = True + else: + d = os.path.dirname(d) else: d = os.path.dirname(d) @@ -251,7 +295,7 @@ def find_tests(self): if len(tests) > 20: logging.warning(f"{len(tests)} tests changed, this looks like a global change, skipping test handling, revert to default") - self.default_run = True + self.full_twister = True return if _options: @@ -261,12 +305,11 @@ def find_tests(self): _options.extend(["-p", platform]) else: _options.append("--all") - self.get_plan(_options) + self.get_plan(_options, use_testsuite_root=False) def find_tags(self): - tag_cfg_file = os.path.join(repository_path, 'scripts', 'ci', 'tags.yaml') - with open(tag_cfg_file, 'r') as ymlfile: + with open(self.tag_cfg_file, 'r') as ymlfile: tags_config = yaml.safe_load(ymlfile) tags = {} @@ -303,26 +346,27 @@ def find_tags(self): logging.info(f'Potential tag based filters: {exclude_tags}') def find_excludes(self, skip=[]): - with open("scripts/ci/twister_ignore.txt", "r") as twister_ignore: + with open(self.ignore_path, "r") as twister_ignore: ignores = twister_ignore.read().splitlines() ignores = filter(lambda x: not x.startswith("#"), ignores) found = set() - files = list(filter(lambda x: x, self.modified_files)) + files_not_resolved = list(filter(lambda x: x not in self.resolved_files, self.modified_files)) for pattern in ignores: - if pattern in skip: - continue if pattern: - found.update(fnmatch.filter(files, pattern)) + found.update(fnmatch.filter(files_not_resolved, pattern)) logging.debug(found) - logging.debug(files) + logging.debug(files_not_resolved) - if sorted(files) != sorted(found): + # Full twister run can be ordered by detecting great number of tests/boards changed + # or if not all modified files were resolved (corresponding scope found) + self.full_twister = self.full_twister or sorted(files_not_resolved) != sorted(found) + + if self.full_twister: _options = [] logging.info(f'Need to run full or partial twister...') - self.full_twister = True if self.platforms: for platform in self.platforms: _options.extend(["-p", platform]) @@ -353,6 +397,27 @@ def parse_args(): help="Number of tests per builder") parser.add_argument('-n', '--default-matrix', default=10, type=int, help="Number of tests per builder") + parser.add_argument('--detailed-test-id', action='store_true', + help="Include paths to tests' locations in tests' names.") + parser.add_argument("--no-detailed-test-id", dest='detailed_test_id', action="store_false", + help="Don't put paths into tests' names.") + parser.add_argument('-r', '--repo-to-scan', default=None, + help="Repo to scan") + parser.add_argument('--ignore-path', + default=os.path.join(zephyr_base, 'scripts', 'ci', 'twister_ignore.txt'), + help="Path to a text file with patterns of files to be matched against changed files") + parser.add_argument('--alt-tags', + default=os.path.join(zephyr_base, 'scripts', 'ci', 'tags.yaml'), + help="Path to a file describing relations between directories and tags") + parser.add_argument( + "-T", "--testsuite-root", action="append", default=[], + help="Base directory to recursively search for test cases. All " + "testcase.yaml files under here will be processed. May be " + "called multiple times. Defaults to the 'samples/' and " + "'tests/' directories at the base of the Zephyr tree.") + + # Include paths in names by default. + parser.set_defaults(detailed_test_id=True) return parser.parse_args() @@ -362,9 +427,11 @@ def parse_args(): args = parse_args() files = [] errors = 0 + if args.repo_to_scan: + repository_path = Path(args.repo_to_scan) if args.commits: - repo = Repo(repository_path) - commit = repo.git.diff("--name-only", args.commits) + repo_to_scan = Repo(repository_path) + commit = repo_to_scan.git.diff("--name-only", args.commits) files = commit.split("\n") elif args.modified_files: with open(args.modified_files, "r") as fp: @@ -375,8 +442,8 @@ def parse_args(): print("\n".join(files)) print("=========") - - f = Filters(files, args.pull_request, args.platform) + f = Filters(files, args.ignore_path, args.alt_tags, args.testsuite_root, + args.pull_request, args.platform, args.detailed_test_id) f.process() # remove dupes and filtered cases diff --git a/scripts/ci/twister_ignore.txt b/scripts/ci/twister_ignore.txt index 59f735495ca..fb267ed81f7 100644 --- a/scripts/ci/twister_ignore.txt +++ b/scripts/ci/twister_ignore.txt @@ -17,25 +17,6 @@ CODEOWNERS MAINTAINERS.yml LICENSE Makefile -tests/* -samples/* -boards/*/*/* -arch/xtensa/* -arch/x86/* -arch/posix/* -arch/arc/* -arch/sparc/* -arch/arm/* -arch/nios2/* -arch/riscv/* -include/arch/xtensa/* -include/arch/x86/* -include/arch/posix/* -include/arch/arc/* -include/arch/sparc/* -include/arch/arm/* -include/arch/nios2/* -include/arch/riscv/* doc/* # GH action have no impact on code .github/* @@ -43,6 +24,7 @@ doc/* *.md # if we change this file or associated script, it should not trigger a full # twister. +scripts/ci/test_plan.py scripts/ci/twister_ignore.txt scripts/ci/what_changed.py scripts/ci/version_mgr.py diff --git a/scripts/kconfig/hardened.csv b/scripts/kconfig/hardened.csv index a09defdc756..6a2937df65c 100644 --- a/scripts/kconfig/hardened.csv +++ b/scripts/kconfig/hardened.csv @@ -39,6 +39,7 @@ TEST_RANDOM_GENERATOR,n TEST_SHELL,n TEST_USERSPACE,n TFM_CMAKE_BUILD_TYPE_DEBUG,n +TFM_DUMMY_PROVISIONING,n THREAD_MONITOR,n THREAD_NAME,n TIMER_RANDOM_GENERATOR,n diff --git a/scripts/pylib/build_helpers/domains.py b/scripts/pylib/build_helpers/domains.py index c748a94a14f..978569d9547 100644 --- a/scripts/pylib/build_helpers/domains.py +++ b/scripts/pylib/build_helpers/domains.py @@ -8,6 +8,8 @@ Domain class. ''' +from dataclasses import dataclass + import yaml import pykwalify.core import logging @@ -27,7 +29,7 @@ required: true type: str domains: - required: false + required: true type: seq sequence: - type: map @@ -38,6 +40,11 @@ build_dir: required: true type: str + flash_order: + required: false + type: seq + sequence: + - type: str ''' schema = yaml.safe_load(DOMAINS_SCHEMA) @@ -51,72 +58,61 @@ class Domains: - def __init__(self, data): - self._domains = [] - self._domain_names = [] - self._domain_default = [] + def __init__(self, domains_yaml): + try: + data = yaml.safe_load(domains_yaml) + pykwalify.core.Core(source_data=data, + schema_data=schema).validate() + except (yaml.YAMLError, pykwalify.errors.SchemaError): + logger.critical(f'malformed domains.yaml') + exit(1) - self._build_dir = data.get('build_dir') - domain_list = data.get('domains') - if not domain_list: - logger.warning("no domains defined; this probably won't work") + self._build_dir = data['build_dir'] + self._domains = { + d['name']: Domain(d['name'], d['build_dir']) + for d in data['domains'] + } - for d in domain_list: - domain = Domain(d['name'], d['build_dir']) - self._domains.append(domain) - self._domain_names.append(domain.name) - if domain.name == data['default']: - self._default_domain = domain + # In the YAML data, the values for "default" and "flash_order" + # must not name any domains that aren't listed under "domains". + # Now that self._domains has been initialized, we can leverage + # the common checks in self.get_domain to verify this. + self._default_domain = self.get_domain(data['default']) + self._flash_order = self.get_domains(data['flash_order'] or []) @staticmethod def from_file(domains_file): - '''Load domains from domains.yaml. - - Exception raised: - - ``FileNotFoundError`` if the domains file is not found. + '''Load domains from a domains.yaml file. ''' try: with open(domains_file, 'r') as f: - domains = yaml.safe_load(f.read()) + domains_yaml = f.read() except FileNotFoundError: logger.critical(f'domains.yaml file not found: {domains_file}') exit(1) - try: - pykwalify.core.Core(source_data=domains, schema_data=schema)\ - .validate() - except pykwalify.errors.SchemaError: - logger.critical(f'ERROR: Malformed yaml in file: {domains_file}') - exit(1) - - return Domains(domains) + return Domains(domains_yaml) @staticmethod - def from_data(domains_data): - '''Load domains from domains dictionary. + def from_yaml(domains_yaml): + '''Load domains from a string with YAML contents. ''' - return Domains(domains_data) - - def get_domains(self, names=None): - ret = [] - - if not names: - return self._domains - - for n in names: - found = False - for d in self._domains: - if n == d.name: - ret.append(d) - found = True - break - # Getting here means the domain was not found. - # Todo: throw an error. - if not found: - logger.critical(f'domain {n} not found, ' - f'valid domains are:', *self._domain_names) - exit(1) - return ret + return Domains(domains_yaml) + + def get_domains(self, names=None, default_flash_order=False): + if names is None: + if default_flash_order: + return self._flash_order + return list(self._domains.values()) + return list(map(self.get_domain, names)) + + def get_domain(self, name): + found = self._domains.get(name) + if not found: + logger.critical(f'domain "{name}" not found, ' + f'valid domains are: {", ".join(self._domains)}') + exit(1) + return found def get_default_domain(self): return self._default_domain @@ -125,24 +121,8 @@ def get_top_build_dir(self): return self._build_dir +@dataclass class Domain: - def __init__(self, name, build_dir): - self.name = name - self.build_dir = build_dir - - @property - def name(self): - return self._name - - @name.setter - def name(self, value): - self._name = value - - @property - def build_dir(self): - return self._build_dir - - @build_dir.setter - def build_dir(self, value): - self._build_dir = value + name: str + build_dir: str diff --git a/scripts/pylib/pytest-twister-harness/src/twister_harness/__init__.py b/scripts/pylib/pytest-twister-harness/src/twister_harness/__init__.py index 251c5deb672..4897e2cf391 100644 --- a/scripts/pylib/pytest-twister-harness/src/twister_harness/__init__.py +++ b/scripts/pylib/pytest-twister-harness/src/twister_harness/__init__.py @@ -5,7 +5,7 @@ # flake8: noqa from twister_harness.device.device_adapter import DeviceAdapter -from twister_harness.fixtures.mcumgr import MCUmgr +from twister_harness.helpers.mcumgr import MCUmgr from twister_harness.helpers.shell import Shell __all__ = ['DeviceAdapter', 'MCUmgr', 'Shell'] diff --git a/scripts/pylib/pytest-twister-harness/src/twister_harness/device/hardware_adapter.py b/scripts/pylib/pytest-twister-harness/src/twister_harness/device/hardware_adapter.py index 3a3f242fa85..3b7bf5d8214 100644 --- a/scripts/pylib/pytest-twister-harness/src/twister_harness/device/hardware_adapter.py +++ b/scripts/pylib/pytest-twister-harness/src/twister_harness/device/hardware_adapter.py @@ -116,7 +116,6 @@ def _flash_and_run(self) -> None: stdout_decoded = stdout.decode(errors='ignore') with open(self.device_log_path, 'a+') as log_file: log_file.write(stdout_decoded) - logger.debug(f'Flashing output:\n{stdout_decoded}') if self.device_config.post_flash_script: self._run_custom_script(self.device_config.post_flash_script, self.base_timeout) if process is not None and process.returncode == 0: @@ -250,6 +249,7 @@ def _clear_internal_resources(self) -> None: super()._clear_internal_resources() self._serial_connection = None self._serial_pty_proc = None + self._serial_buffer.clear() @staticmethod def _run_custom_script(script_path: str | Path, timeout: float) -> None: diff --git a/scripts/pylib/pytest-twister-harness/src/twister_harness/fixtures/dut.py b/scripts/pylib/pytest-twister-harness/src/twister_harness/fixtures.py similarity index 63% rename from scripts/pylib/pytest-twister-harness/src/twister_harness/fixtures/dut.py rename to scripts/pylib/pytest-twister-harness/src/twister_harness/fixtures.py index 0f34c05b252..f2b1b53706c 100644 --- a/scripts/pylib/pytest-twister-harness/src/twister_harness/fixtures/dut.py +++ b/scripts/pylib/pytest-twister-harness/src/twister_harness/fixtures.py @@ -10,6 +10,8 @@ from twister_harness.device.device_adapter import DeviceAdapter from twister_harness.device.factory import DeviceFactory from twister_harness.twister_harness_config import DeviceConfig, TwisterHarnessConfig +from twister_harness.helpers.shell import Shell +from twister_harness.helpers.mcumgr import MCUmgr logger = logging.getLogger(__name__) @@ -34,13 +36,38 @@ def device_object(twister_harness_config: TwisterHarnessConfig) -> Generator[Dev device_object.close() -@pytest.fixture(scope='function') +def determine_scope(fixture_name, config): + if dut_scope := config.getoption("--dut-scope", None): + return dut_scope + return 'function' + + +@pytest.fixture(scope=determine_scope) def dut(request: pytest.FixtureRequest, device_object: DeviceAdapter) -> Generator[DeviceAdapter, None, None]: """Return launched device - with run application.""" - test_name = request.node.name - device_object.initialize_log_files(test_name) + device_object.initialize_log_files(request.node.name) try: device_object.launch() yield device_object finally: # to make sure we close all running processes execution device_object.close() + + +@pytest.fixture(scope=determine_scope) +def shell(dut: DeviceAdapter) -> Shell: + """Return ready to use shell interface""" + shell = Shell(dut, timeout=20.0) + logger.info('Wait for prompt') + assert shell.wait_for_prompt() + return shell + + +@pytest.fixture(scope='session') +def is_mcumgr_available() -> None: + if not MCUmgr.is_available(): + pytest.skip('mcumgr not available') + + +@pytest.fixture() +def mcumgr(is_mcumgr_available: None, dut: DeviceAdapter) -> Generator[MCUmgr, None, None]: + yield MCUmgr.create_for_serial(dut.device_config.serial) diff --git a/scripts/pylib/pytest-twister-harness/src/twister_harness/fixtures/__init__.py b/scripts/pylib/pytest-twister-harness/src/twister_harness/fixtures/__init__.py deleted file mode 100644 index ed61bf17b1c..00000000000 --- a/scripts/pylib/pytest-twister-harness/src/twister_harness/fixtures/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2023 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: Apache-2.0 - -import pytest - -pytest.register_assert_rewrite('twister_harness.fixtures.dut') -pytest.register_assert_rewrite('twister_harness.fixtures.mcumgr') diff --git a/scripts/pylib/pytest-twister-harness/src/twister_harness/fixtures/mcumgr.py b/scripts/pylib/pytest-twister-harness/src/twister_harness/helpers/mcumgr.py similarity index 88% rename from scripts/pylib/pytest-twister-harness/src/twister_harness/fixtures/mcumgr.py rename to scripts/pylib/pytest-twister-harness/src/twister_harness/helpers/mcumgr.py index e0d85893792..b6cab6475c1 100755 --- a/scripts/pylib/pytest-twister-harness/src/twister_harness/fixtures/mcumgr.py +++ b/scripts/pylib/pytest-twister-harness/src/twister_harness/helpers/mcumgr.py @@ -3,19 +3,14 @@ # SPDX-License-Identifier: Apache-2.0 from __future__ import annotations -import pytest import logging import re import shlex -from typing import Generator from subprocess import check_output, getstatusoutput from pathlib import Path from dataclasses import dataclass -from twister_harness.device.device_adapter import DeviceAdapter - - logger = logging.getLogger(__name__) @@ -108,14 +103,3 @@ def image_confirm(self, hash: str | None = None): image_list = self.get_image_list() hash = image_list[0].hash self.run_command(f'image confirm {hash}') - - -@pytest.fixture(scope='session') -def is_mcumgr_available() -> None: - if not MCUmgr.is_available(): - pytest.skip('mcumgr not available') - - -@pytest.fixture() -def mcumgr(is_mcumgr_available: None, dut: DeviceAdapter) -> Generator[MCUmgr, None, None]: - yield MCUmgr.create_for_serial(dut.device_config.serial) diff --git a/scripts/pylib/pytest-twister-harness/src/twister_harness/helpers/shell.py b/scripts/pylib/pytest-twister-harness/src/twister_harness/helpers/shell.py index fc8009d12fe..37f8ad432cf 100644 --- a/scripts/pylib/pytest-twister-harness/src/twister_harness/helpers/shell.py +++ b/scripts/pylib/pytest-twister-harness/src/twister_harness/helpers/shell.py @@ -57,7 +57,7 @@ def exec_command(self, command: str, timeout: float | None = None, print_output: timeout = timeout or self.base_timeout command_ext = f'{command}\n\n' regex_prompt = re.escape(self.prompt) - regex_command = f'{regex_prompt}.*{command}' + regex_command = f'.*{command}' self._device.clear_buffer() self._device.write(command_ext.encode()) lines: list[str] = [] diff --git a/scripts/pylib/pytest-twister-harness/src/twister_harness/plugin.py b/scripts/pylib/pytest-twister-harness/src/twister_harness/plugin.py index 59bec12955d..dbd3465aba1 100644 --- a/scripts/pylib/pytest-twister-harness/src/twister_harness/plugin.py +++ b/scripts/pylib/pytest-twister-harness/src/twister_harness/plugin.py @@ -14,8 +14,7 @@ logger = logging.getLogger(__name__) pytest_plugins = ( - 'twister_harness.fixtures.dut', - 'twister_harness.fixtures.mcumgr' + 'twister_harness.fixtures' ) @@ -101,6 +100,11 @@ def pytest_addoption(parser: pytest.Parser): metavar='PATH', help='Script executed after closing serial connection.' ) + twister_harness_group.addoption( + '--dut-scope', + choices=('function', 'class', 'module', 'package', 'session'), + help='The scope for which `dut` and `shell` fixtures are shared.' + ) def pytest_configure(config: pytest.Config): diff --git a/scripts/pylib/pytest-twister-harness/tests/fixtures/mcumgr_fixture_test.py b/scripts/pylib/pytest-twister-harness/tests/fixtures/mcumgr_fixture_test.py index f294adba30a..f336311143a 100644 --- a/scripts/pylib/pytest-twister-harness/tests/fixtures/mcumgr_fixture_test.py +++ b/scripts/pylib/pytest-twister-harness/tests/fixtures/mcumgr_fixture_test.py @@ -6,7 +6,7 @@ import textwrap from unittest import mock -from twister_harness.fixtures.mcumgr import MCUmgr, MCUmgrException +from twister_harness.helpers.mcumgr import MCUmgr, MCUmgrException @pytest.fixture(name='mcumgr') @@ -14,7 +14,7 @@ def fixture_mcumgr() -> MCUmgr: return MCUmgr.create_for_serial('SERIAL_PORT') -@mock.patch('twister_harness.fixtures.mcumgr.MCUmgr.run_command', return_value='') +@mock.patch('twister_harness.helpers.mcumgr.MCUmgr.run_command', return_value='') def test_if_mcumgr_fixture_generate_proper_command( patched_run_command: mock.Mock, mcumgr: MCUmgr ) -> None: diff --git a/scripts/pylib/twister/twisterlib/config_parser.py b/scripts/pylib/twister/twisterlib/config_parser.py index c822e9dfc77..f431e2d57c2 100644 --- a/scripts/pylib/twister/twisterlib/config_parser.py +++ b/scripts/pylib/twister/twisterlib/config_parser.py @@ -48,6 +48,7 @@ class TwisterConfigParser: "extra_conf_files": {"type": "list", "default": []}, "extra_overlay_confs" : {"type": "list", "default": []}, "extra_dtc_overlay_files": {"type": "list", "default": []}, + "required_snippets": {"type": "list"}, "build_only": {"type": "bool", "default": False}, "build_on_all": {"type": "bool", "default": False}, "skip": {"type": "bool", "default": False}, diff --git a/scripts/pylib/twister/twisterlib/environment.py b/scripts/pylib/twister/twisterlib/environment.py index 160233a66fe..d1ce56711c2 100644 --- a/scripts/pylib/twister/twisterlib/environment.py +++ b/scripts/pylib/twister/twisterlib/environment.py @@ -211,6 +211,12 @@ def add_parse_arguments(parser = None): and 'fifo_loop' is a name of a function found in main.c without test prefix. """) + parser.add_argument( + "--pytest-args", action="append", + help="""Pass additional arguments to the pytest subprocess. This parameter + will override the pytest_args from the harness_config in YAML file. + """) + valgrind_asan_group.add_argument( "--enable-valgrind", action="store_true", help="""Run binary through valgrind and check for several memory access @@ -437,6 +443,21 @@ def add_parse_arguments(parser = None): help="Re-use the outdir before building. Will result in " "faster compilation since builds will be incremental.") + parser.add_argument( + '--detailed-test-id', action='store_true', + help="Include paths to tests' locations in tests' names. Names will follow " + "PATH_TO_TEST/SCENARIO_NAME schema " + "e.g. samples/hello_world/sample.basic.helloworld") + + parser.add_argument( + "--no-detailed-test-id", dest='detailed_test_id', action="store_false", + help="Don't put paths into tests' names. " + "With this arg a test name will be a scenario name " + "e.g. sample.basic.helloworld.") + + # Include paths in names by default. + parser.set_defaults(detailed_test_id=True) + # To be removed in favor of --detailed-skipped-report parser.add_argument( "--no-skipped-report", action="store_true", diff --git a/scripts/pylib/twister/twisterlib/hardwaremap.py b/scripts/pylib/twister/twisterlib/hardwaremap.py index 5e90b7c84c6..a0ea7c59a8e 100644 --- a/scripts/pylib/twister/twisterlib/hardwaremap.py +++ b/scripts/pylib/twister/twisterlib/hardwaremap.py @@ -13,7 +13,6 @@ import scl import logging from pathlib import Path -from natsort import natsorted from twisterlib.environment import ZEPHYR_BASE @@ -322,7 +321,7 @@ def readlink(link): def save(self, hwm_file): # use existing map - self.detected = natsorted(self.detected, key=lambda x: x.serial or '') + self.detected.sort(key=lambda x: x.serial or '') if os.path.exists(hwm_file): with open(hwm_file, 'r') as yaml_file: hwm = yaml.load(yaml_file, Loader=SafeLoader) diff --git a/scripts/pylib/twister/twisterlib/harness.py b/scripts/pylib/twister/twisterlib/harness.py index 96b4a2e3461..edb053232b9 100644 --- a/scripts/pylib/twister/twisterlib/harness.py +++ b/scripts/pylib/twister/twisterlib/harness.py @@ -245,22 +245,25 @@ def pytest_run(self, timeout): def generate_command(self): config = self.instance.testsuite.harness_config - pytest_root = config.get('pytest_root', 'pytest') if config else 'pytest' - pytest_args = config.get('pytest_args', []) if config else [] + handler: Handler = self.instance.handler + pytest_root = config.get('pytest_root', ['pytest']) if config else ['pytest'] + pytest_args_yaml = config.get('pytest_args', []) if config else [] + pytest_dut_scope = config.get('pytest_dut_scope', None) if config else None command = [ 'pytest', '--twister-harness', '-s', '-v', - os.path.join(self.source_dir, pytest_root), f'--build-dir={self.running_dir}', f'--junit-xml={self.report_file}', '--log-file-level=DEBUG', '--log-file-format=%(asctime)s.%(msecs)d:%(levelname)s:%(name)s: %(message)s', f'--log-file={self.pytest_log_file_path}' ] - command.extend(pytest_args) + command.extend([os.path.normpath(os.path.join( + self.source_dir, os.path.expanduser(os.path.expandvars(src)))) for src in pytest_root]) - handler: Handler = self.instance.handler + if pytest_dut_scope: + command.append(f'--dut-scope={pytest_dut_scope}') if handler.options.verbose > 1: command.extend([ @@ -278,6 +281,16 @@ def generate_command(self): command.append('--device-type=custom') else: raise PytestHarnessException(f'Handling of handler {handler.type_str} not implemented yet') + + if handler.options.pytest_args: + command.extend(handler.options.pytest_args) + if pytest_args_yaml: + logger.warning(f'The pytest_args ({handler.options.pytest_args}) specified ' + 'in the command line will override the pytest_args defined ' + f'in the YAML file {pytest_args_yaml}') + else: + command.extend(pytest_args_yaml) + return command def _generate_parameters_for_hardware(self, handler: Handler): @@ -396,8 +409,10 @@ def _parse_report_file(self, report): if elem_ts := root.find('testsuite'): if elem_ts.get('failures') != '0': self.state = 'failed' + self.instance.reason = f"{elem_ts.get('failures')}/{elem_ts.get('tests')} pytest scenario(s) failed" elif elem_ts.get('errors') != '0': self.state = 'error' + self.instance.reason = 'Error during pytest execution' elif elem_ts.get('skipped') == elem_ts.get('tests'): self.state = 'skipped' else: @@ -405,7 +420,7 @@ def _parse_report_file(self, report): self.instance.execution_time = float(elem_ts.get('time')) for elem_tc in elem_ts.findall('testcase'): - tc = self.instance.get_case_or_create(f"{self.id}.{elem_tc.get('name')}") + tc = self.instance.add_testcase(f"{self.id}.{elem_tc.get('name')}") tc.duration = float(elem_tc.get('time')) elem = elem_tc.find('*') if elem is None: @@ -419,6 +434,9 @@ def _parse_report_file(self, report): tc.status = 'error' tc.reason = elem.get('message') tc.output = elem.text + else: + self.state = 'skipped' + self.instance.reason = 'No tests collected' class Gtest(Harness): diff --git a/scripts/pylib/twister/twisterlib/reports.py b/scripts/pylib/twister/twisterlib/reports.py index bd005d4319a..be1bbb4dbd7 100644 --- a/scripts/pylib/twister/twisterlib/reports.py +++ b/scripts/pylib/twister/twisterlib/reports.py @@ -246,6 +246,7 @@ def json_report(self, filename, version="NA"): for instance in self.instances.values(): suite = {} handler_log = os.path.join(instance.build_dir, "handler.log") + pytest_log = os.path.join(instance.build_dir, "twister_harness.log") build_log = os.path.join(instance.build_dir, "build.log") device_log = os.path.join(instance.build_dir, "device.log") @@ -258,6 +259,7 @@ def json_report(self, filename, version="NA"): "name": instance.testsuite.name, "arch": instance.platform.arch, "platform": instance.platform.name, + "path": instance.testsuite.source_dir_rel } if instance.run_id: suite['run_id'] = instance.run_id @@ -283,7 +285,9 @@ def json_report(self, filename, version="NA"): suite['status'] = instance.status suite["reason"] = instance.reason # FIXME - if os.path.exists(handler_log): + if os.path.exists(pytest_log): + suite["log"] = self.process_log(pytest_log) + elif os.path.exists(handler_log): suite["log"] = self.process_log(handler_log) elif os.path.exists(device_log): suite["log"] = self.process_log(device_log) @@ -419,6 +423,7 @@ def footprint_reports(self, report, show_footprint, all_deltas, def synopsis(self): cnt = 0 example_instance = None + detailed_test_id = self.env.options.detailed_test_id for instance in self.instances.values(): if instance.status not in ["passed", "filtered", "skipped"]: cnt = cnt + 1 @@ -434,11 +439,14 @@ def synopsis(self): if cnt and example_instance: logger.info("") logger.info("To rerun the tests, call twister using the following commandline:") - logger.info("west twister -p -s , for example:") + extra_parameters = '' if detailed_test_id else ' --no-detailed-test-id' + logger.info(f"west twister -p -s {extra_parameters}, for example:") logger.info("") - logger.info(f"west twister -p {example_instance.platform.name} -s {example_instance.testsuite.name}") + logger.info(f"west twister -p {example_instance.platform.name} -s {example_instance.testsuite.name}" + f"{extra_parameters}") logger.info(f"or with west:") - logger.info(f"west build -p -b {example_instance.platform.name} -T {example_instance.testsuite.name}") + logger.info(f"west build -p -b {example_instance.platform.name} " + f"{example_instance.testsuite.source_dir_rel} -T {example_instance.testsuite.id}") logger.info("-+" * 40) def summary(self, results, unrecognized_sections, duration): diff --git a/scripts/pylib/twister/twisterlib/runner.py b/scripts/pylib/twister/twisterlib/runner.py index ad93145db38..4dbdb21f882 100644 --- a/scripts/pylib/twister/twisterlib/runner.py +++ b/scripts/pylib/twister/twisterlib/runner.py @@ -40,6 +40,9 @@ from twisterlib.log_helper import log_command from twisterlib.testinstance import TestInstance +from twisterlib.environment import TwisterEnv +from twisterlib.testsuite import TestSuite +from twisterlib.platform import Platform from twisterlib.testplan import change_skip_to_error_if_integration from twisterlib.harness import HarnessImporter, Pytest @@ -220,7 +223,7 @@ class CMake: config_re = re.compile('(CONFIG_[A-Za-z0-9_]+)[=]\"?([^\"]*)\"?$') dt_re = re.compile('([A-Za-z0-9_]+)[=]\"?([^\"]*)\"?$') - def __init__(self, testsuite, platform, source_dir, build_dir, jobserver): + def __init__(self, testsuite: TestSuite, platform: Platform, source_dir, build_dir, jobserver): self.cwd = None self.capture_output = True @@ -359,6 +362,10 @@ def run_cmake(self, args="", filter_stages=[]): cmake_opts = ['-DBOARD={}'.format(self.platform.name)] cmake_args.extend(cmake_opts) + if self.instance.testsuite.required_snippets: + cmake_opts = ['-DSNIPPET={}'.format(';'.join(self.instance.testsuite.required_snippets))] + cmake_args.extend(cmake_opts) + cmake = shutil.which('cmake') cmd = [cmake] + cmake_args @@ -410,7 +417,7 @@ def run_cmake(self, args="", filter_stages=[]): class FilterBuilder(CMake): - def __init__(self, testsuite, platform, source_dir, build_dir, jobserver): + def __init__(self, testsuite: TestSuite, platform: Platform, source_dir, build_dir, jobserver): super().__init__(testsuite, platform, source_dir, build_dir, jobserver) self.log = "config-twister.log" @@ -513,7 +520,7 @@ def parse_generated(self, filter_stages=[]): class ProjectBuilder(FilterBuilder): - def __init__(self, instance, env, jobserver, **kwargs): + def __init__(self, instance: TestInstance, env: TwisterEnv, jobserver, **kwargs): super().__init__(instance.testsuite, instance.platform, instance.testsuite.source_dir, instance.build_dir, jobserver) self.log = "build.log" @@ -523,8 +530,7 @@ def __init__(self, instance, env, jobserver, **kwargs): self.env = env self.duts = None - @staticmethod - def log_info(filename, inline_logs): + def log_info(self, filename, inline_logs, log_testcases=False): filename = os.path.abspath(os.path.realpath(filename)) if inline_logs: logger.info("{:-^100}".format(filename)) @@ -538,6 +544,17 @@ def log_info(filename, inline_logs): logger.error(data) logger.info("{:-^100}".format(filename)) + + if log_testcases: + for tc in self.instance.testcases: + if not tc.reason: + continue + logger.info( + f"\n{str(tc.name).center(100, '_')}\n" + f"{tc.reason}\n" + f"{100*'_'}\n" + f"{tc.output}" + ) else: logger.error("see: " + Fore.YELLOW + filename + Fore.RESET) @@ -547,9 +564,12 @@ def log_info_file(self, inline_logs): b_log = "{}/build.log".format(build_dir) v_log = "{}/valgrind.log".format(build_dir) d_log = "{}/device.log".format(build_dir) + pytest_log = "{}/twister_harness.log".format(build_dir) if os.path.exists(v_log) and "Valgrind" in self.instance.reason: self.log_info("{}".format(v_log), inline_logs) + elif os.path.exists(pytest_log) and os.path.getsize(pytest_log) > 0: + self.log_info("{}".format(pytest_log), inline_logs, log_testcases=True) elif os.path.exists(h_log) and os.path.getsize(h_log) > 0: self.log_info("{}".format(h_log), inline_logs) elif os.path.exists(d_log) and os.path.getsize(d_log) > 0: @@ -974,9 +994,15 @@ def report_out(self, results): sys.stdout.flush() @staticmethod - def cmake_assemble_args(args, handler, extra_conf_files, extra_overlay_confs, + def cmake_assemble_args(extra_args, handler, extra_conf_files, extra_overlay_confs, extra_dtc_overlay_files, cmake_extra_args, build_dir): + # Retain quotes around config options + config_options = [arg for arg in extra_args if arg.startswith("CONFIG_")] + args = [arg for arg in extra_args if not arg.startswith("CONFIG_")] + + args_expanded = ["-D{}".format(a.replace('"', '\"')) for a in config_options] + if handler.ready: args.extend(handler.args) @@ -999,7 +1025,7 @@ def cmake_assemble_args(args, handler, extra_conf_files, extra_overlay_confs, args.append("OVERLAY_CONFIG=\"%s\"" % (" ".join(overlays))) # Build the final argument list - args_expanded = ["-D{}".format(a.replace('"', '\"')) for a in cmake_extra_args] + args_expanded.extend(["-D{}".format(a.replace('"', '\"')) for a in cmake_extra_args]) args_expanded.extend(["-D{}".format(a.replace('"', '')) for a in args]) return args_expanded diff --git a/scripts/pylib/twister/twisterlib/testinstance.py b/scripts/pylib/twister/twisterlib/testinstance.py index ccfc3cd06d4..b9e7d411cb5 100644 --- a/scripts/pylib/twister/twisterlib/testinstance.py +++ b/scripts/pylib/twister/twisterlib/testinstance.py @@ -55,7 +55,12 @@ def __init__(self, testsuite, platform, outdir): self.name = os.path.join(platform.name, testsuite.name) self.run_id = self._get_run_id() self.dut = None - self.build_dir = os.path.join(outdir, platform.name, testsuite.name) + if testsuite.detailed_test_id: + self.build_dir = os.path.join(outdir, platform.name, testsuite.name) + else: + # if suite is not in zephyr, keep only the part after ".." in reconstructed dir structure + source_dir_rel = testsuite.source_dir_rel.rsplit(os.pardir+os.path.sep, 1)[-1] + self.build_dir = os.path.join(outdir, platform.name, source_dir_rel, testsuite.name) self.domains = None diff --git a/scripts/pylib/twister/twisterlib/testplan.py b/scripts/pylib/twister/twisterlib/testplan.py index 85340885dbb..ae122862d1e 100755 --- a/scripts/pylib/twister/twisterlib/testplan.py +++ b/scripts/pylib/twister/twisterlib/testplan.py @@ -16,6 +16,8 @@ import copy import shutil import random +import snippets +from pathlib import Path logger = logging.getLogger('twister') logger.setLevel(logging.DEBUG) @@ -522,7 +524,7 @@ def add_testsuites(self, testsuite_filter=[]): for name in parsed_data.scenarios.keys(): suite_dict = parsed_data.get_scenario(name) - suite = TestSuite(root, suite_path, name, data=suite_dict) + suite = TestSuite(root, suite_path, name, data=suite_dict, detailed_test_id=self.options.detailed_test_id) suite.add_subcases(suite_dict, subcases, ztest_suite_names) if testsuite_filter: if suite.name and suite.name in testsuite_filter: @@ -738,7 +740,7 @@ def apply_filters(self, **kwargs): instance.add_filter("Not part of requested test plan", Filters.TESTSUITE) if runnable and not instance.run: - instance.add_filter("Not runnable on device", Filters.PLATFORM) + instance.add_filter("Not runnable on device", Filters.CMD_LINE) if self.options.integration and ts.integration_platforms and plat.name not in ts.integration_platforms: instance.add_filter("Not part of integration platforms", Filters.TESTSUITE) @@ -819,6 +821,46 @@ def apply_filters(self, **kwargs): if plat.only_tags and not set(plat.only_tags) & ts.tags: instance.add_filter("Excluded tags per platform (only_tags)", Filters.PLATFORM) + if ts.required_snippets: + missing_snippet = False + snippet_args = {"snippets": ts.required_snippets} + found_snippets = snippets.find_snippets_in_roots(snippet_args, [Path(ZEPHYR_BASE), Path(ts.source_dir)]) + + # Search and check that all required snippet files are found + for this_snippet in snippet_args['snippets']: + if this_snippet not in found_snippets: + logger.error(f"Can't find snippet '%s' for test '%s'", this_snippet, ts.name) + instance.status = "error" + instance.reason = f"Snippet {this_snippet} not found" + missing_snippet = True + break + + if not missing_snippet: + # Look for required snippets and check that they are applicable for these + # platforms/boards + for this_snippet in found_snippets: + matched_snippet_board = False + + # If the "appends" key is present with at least one entry then this + # snippet applies to all boards and further platform-specific checks + # are not required + if found_snippets[this_snippet].appends: + continue + + for this_board in found_snippets[this_snippet].board2appends: + if this_board.startswith('/'): + match = re.search(this_board[1:-1], plat.name) + if match is not None: + matched_snippet_board = True + break + elif this_board == plat.name: + matched_snippet_board = True + break + + if matched_snippet_board is False: + instance.add_filter("Snippet not supported", Filters.PLATFORM) + break + # platform_key is a list of unique platform attributes that form a unique key a test # will match against to determine if it should be scheduled to run. A key containing a # field name that the platform does not have will filter the platform. diff --git a/scripts/pylib/twister/twisterlib/testsuite.py b/scripts/pylib/twister/twisterlib/testsuite.py index 3f4a71e4a8c..39c21098718 100644 --- a/scripts/pylib/twister/twisterlib/testsuite.py +++ b/scripts/pylib/twister/twisterlib/testsuite.py @@ -348,7 +348,7 @@ class TestSuite(DisablePyTestCollectionMixin): """Class representing a test application """ - def __init__(self, suite_root, suite_path, name, data=None): + def __init__(self, suite_root, suite_path, name, data=None, detailed_test_id=True): """TestSuite constructor. This gets called by TestPlan as it finds and reads test yaml files. @@ -369,10 +369,14 @@ def __init__(self, suite_root, suite_path, name, data=None): """ workdir = os.path.relpath(suite_path, suite_root) - self.name = self.get_unique(suite_root, workdir, name) + + assert self.check_suite_name(name, suite_root, workdir) + self.detailed_test_id = detailed_test_id + self.name = self.get_unique(suite_root, workdir, name) if self.detailed_test_id else name self.id = name self.source_dir = suite_path + self.source_dir_rel = os.path.relpath(os.path.realpath(suite_path), start=canonical_zephyr_base) self.yamlfile = suite_path self.testcases = [] @@ -425,10 +429,14 @@ def get_unique(testsuite_root, workdir, name): # workdir can be "." unique = os.path.normpath(os.path.join(relative_ts_root, workdir, name)) + return unique + + @staticmethod + def check_suite_name(name, testsuite_root, workdir): check = name.split(".") if len(check) < 2: raise TwisterException(f"""bad test name '{name}' in {testsuite_root}/{workdir}. \ Tests should reference the category and subsystem with a dot as a separator. """ ) - return unique + return True diff --git a/scripts/quarantine.yaml b/scripts/quarantine.yaml new file mode 100644 index 00000000000..20c4f9248ea --- /dev/null +++ b/scripts/quarantine.yaml @@ -0,0 +1,88 @@ +# The configurations resulting as a product of scenarios and platforms +# will be skipped if quarantine is used. More details here: +# https://docs.zephyrproject.org/latest/guides/test/twister.html#quarantine + +- scenarios: + - testing.ztest.busy_sim + - testing.ztest.busy_sim_nrf52840dk_pin + platforms: + - nrf52840dk_nrf52840 + +# Already reported, but will not be fixed (look at the discussion): +# https://github.com/zephyrproject-rtos/zephyr/issues/44947 +- scenarios: + - libraries.cmsis_dsp.matrix.unary_f64 + platforms: + - nrf5340dk_nrf5340_cpunet + - qemu_cortex_m3 + comment: "Flash overflows" + +# Already reported, but will not be fixed (look at the discussion): +# https://github.com/zephyrproject-rtos/zephyr/issues/44947 +- scenarios: + - libraries.cmsis_dsp.matrix.binary_f16 + - libraries.cmsis_dsp.matrix.binary_f16.fpu + platforms: + - nrf5340dk_nrf5340_cpuapp_ns + comment: "Flash overflows" + +# Already reported, but will not be fixed (look at the discussion): +# https://github.com/zephyrproject-rtos/zephyr/issues/44947 +- scenarios: + - libraries.cmsis_dsp.matrix.binary_q15 + - libraries.cmsis_dsp.matrix.binary_q15.fpu + - libraries.cmsis_dsp.matrix.unary_f32 + - libraries.cmsis_dsp.matrix.unary_f32.fpu + - libraries.cmsis_dsp.matrix.unary_f64 + - libraries.cmsis_dsp.matrix.unary_f64.fpu + platforms: + - nrf5340dk_nrf5340_cpuapp_ns + - nrf9160dk_nrf9160_ns + comment: "Flash overflows" + +# libsdl2-dev package should be added into docker image +- scenarios: + - sample.boards.nrf.nrf_led_matrix + - sample.display.lvgl.gui + platforms: + - native_posix + comment: "libsdl2-dev package not available" + +- scenarios: + - sample.net.sockets.echo_server.usbnet + - sample.net.sockets.echo_server.usbnet_composite + platforms: + - nrf5340dk_nrf5340_cpuapp_ns + comment: "Ram/flash overflows, also in the upstream" + +- scenarios: + - sample.net.zperf.netusb_ecm + - sample.net.zperf.netusb_eem + - sample.net.zperf.netusb_rndis + platforms: + - nrf52833dk_nrf52833 + - nrf5340dk_nrf5340_cpuapp_ns + comment: "Ram/flash overflows, also in the upstream" + +- scenarios: + - net.mqtt.tls + platforms: + - nrf5340dk_nrf5340_cpuapp_ns + - nrf9160dk_nrf9160_ns + comment: "Ram/flash overflows, also in the upstream" + +- scenarios: + - kernel.common.picolibc + - libraries.picolibc + - libraries.libc.picolibc.mem_alloc + - libraries.picolibc.sprintf_new + platforms: + - nrf52dk_nrf52832 + comment: "Ram overflows, also in the upstream" + +- scenarios: + - sample.psa_crypto + platforms: + - nrf5340dk_nrf5340_cpuapp_ns + - nrf9160dk_nrf9160_ns + comment: "Due to using sdk-zephyr manifest instead of nrf. Should be fixed after the change" diff --git a/scripts/requirements-run-test.txt b/scripts/requirements-run-test.txt index 83692128110..3bb0b21c44e 100644 --- a/scripts/requirements-run-test.txt +++ b/scripts/requirements-run-test.txt @@ -7,7 +7,6 @@ pyocd>=0.35.0 # used by twister for board/hardware map tabulate -natsort # used by mcuboot cbor>=1.0.0 diff --git a/scripts/schemas/twister/testsuite-schema.yaml b/scripts/schemas/twister/testsuite-schema.yaml index 4d67fe71706..1e198173c72 100644 --- a/scripts/schemas/twister/testsuite-schema.yaml +++ b/scripts/schemas/twister/testsuite-schema.yaml @@ -95,13 +95,19 @@ mapping: type: int required: false "pytest_root": - type: str + type: seq required: false + sequence: + - type: str "pytest_args": type: seq required: false sequence: - type: str + "pytest_dut_scope": + type: str + enum: ["function", "class", "module", "package", "session"] + required: false "regex": type: seq required: false @@ -145,6 +151,11 @@ mapping: matching: "all" sequence: - type: str + "required_snippets": + type: seq + required: false + sequence: + - type: str "tags": type: any required: false @@ -243,6 +254,11 @@ mapping: "extra_sections": type: any required: false + "required_snippets": + type: seq + required: false + sequence: + - type: str "filter": type: str required: false @@ -283,13 +299,19 @@ mapping: type: int required: false "pytest_root": - type: str + type: seq required: false + sequence: + - type: str "pytest_args": type: seq required: false sequence: - type: str + "pytest_dut_scope": + type: str + enum: ["function", "class", "module", "package", "session"] + required: false "regex": type: seq required: false diff --git a/scripts/snippets.py b/scripts/snippets.py index 9662f3edcec..78ab896e85b 100644 --- a/scripts/snippets.py +++ b/scripts/snippets.py @@ -238,6 +238,22 @@ def process_snippets(args: argparse.Namespace) -> Snippets: return snippets +def find_snippets_in_roots(requested_snippets, snippet_roots) -> Snippets: + '''Process snippet.yml files under each *snippet_root* + by recursive search. Return a Snippets object describing + the results of the search. + ''' + # This will contain information about all the snippets + # we discover in each snippet_root element. + snippets = Snippets(requested=requested_snippets) + + # Process each path in snippet_root in order, adjusting + # snippets as needed for each one. + for root in snippet_roots: + process_snippets_in(root, snippets) + + return snippets + def process_snippets_in(root_dir: Path, snippets: Snippets) -> None: '''Process snippet.yml files in *root_dir*, updating *snippets* as needed.''' diff --git a/scripts/tests/twister/pytest_integration/test_harness_pytest.py b/scripts/tests/twister/pytest_integration/test_harness_pytest.py index db7bf389fbd..befd384be37 100644 --- a/scripts/tests/twister/pytest_integration/test_harness_pytest.py +++ b/scripts/tests/twister/pytest_integration/test_harness_pytest.py @@ -25,6 +25,7 @@ def testinstance() -> TestInstance: testinstance.handler = mock.Mock() testinstance.handler.options = mock.Mock() testinstance.handler.options.verbose = 1 + testinstance.handler.options.pytest_args = None testinstance.handler.type_str = 'native' return testinstance @@ -48,6 +49,99 @@ def test_pytest_command(testinstance: TestInstance, device_type): assert c in command +def test_pytest_command_dut_scope(testinstance: TestInstance): + pytest_harness = Pytest() + dut_scope = 'session' + testinstance.testsuite.harness_config['pytest_dut_scope'] = dut_scope + pytest_harness.configure(testinstance) + command = pytest_harness.generate_command() + assert f'--dut-scope={dut_scope}' in command + + +def test_pytest_command_extra_args(testinstance: TestInstance): + pytest_harness = Pytest() + pytest_args = ['-k test1', '-m mark1'] + testinstance.testsuite.harness_config['pytest_args'] = pytest_args + pytest_harness.configure(testinstance) + command = pytest_harness.generate_command() + for c in pytest_args: + assert c in command + + +def test_pytest_command_extra_args_in_options(testinstance: TestInstance): + pytest_harness = Pytest() + pytest_args_from_yaml = '-k test_from_yaml' + pytest_args_from_cmd = ['-k', 'test_from_cmd'] + testinstance.testsuite.harness_config['pytest_args'] = [pytest_args_from_yaml] + testinstance.handler.options.pytest_args = pytest_args_from_cmd + pytest_harness.configure(testinstance) + command = pytest_harness.generate_command() + assert pytest_args_from_cmd[0] in command + assert pytest_args_from_cmd[1] in command + assert pytest_args_from_yaml not in command + + +@pytest.mark.parametrize( + ('pytest_root', 'expected'), + [ + ( + ['pytest/test_shell_help.py'], + ['samples/hello/pytest/test_shell_help.py'] + ), + ( + ['pytest/test_shell_help.py', 'pytest/test_shell_version.py', 'test_dir'], + ['samples/hello/pytest/test_shell_help.py', + 'samples/hello/pytest/test_shell_version.py', + 'samples/hello/test_dir'] + ), + ( + ['../shell/pytest/test_shell.py'], + ['samples/shell/pytest/test_shell.py'] + ), + ( + ['/tmp/test_temp.py'], + ['/tmp/test_temp.py'] + ), + ( + ['~/tmp/test_temp.py'], + ['/home/joe/tmp/test_temp.py'] + ), + ( + ['$ZEPHYR_BASE/samples/subsys/testsuite/pytest/shell/pytest'], + ['/zephyr_base/samples/subsys/testsuite/pytest/shell/pytest'] + ), + ( + ['pytest/test_shell_help.py::test_A', 'pytest/test_shell_help.py::test_B'], + ['samples/hello/pytest/test_shell_help.py::test_A', + 'samples/hello/pytest/test_shell_help.py::test_B'] + ), + ( + ['pytest/test_shell_help.py::test_A[param_a]'], + ['samples/hello/pytest/test_shell_help.py::test_A[param_a]'] + ) + ], + ids=[ + 'one_file', + 'more_files', + 'relative_path', + 'absollute_path', + 'user_dir', + 'with_env_var', + 'subtests', + 'subtest_with_param' + ] +) +def test_pytest_handle_source_list(testinstance: TestInstance, monkeypatch, pytest_root, expected): + monkeypatch.setenv('ZEPHYR_BASE', '/zephyr_base') + monkeypatch.setenv('HOME', '/home/joe') + testinstance.testsuite.harness_config['pytest_root'] = pytest_root + pytest_harness = Pytest() + pytest_harness.configure(testinstance) + command = pytest_harness.generate_command() + for pytest_src in expected: + assert pytest_src in command + + def test_if_report_is_parsed(pytester, testinstance: TestInstance): test_file_content = textwrap.dedent(""" def test_1(): @@ -108,6 +202,8 @@ def test_err(): assert tc.status == "failed" assert tc.output assert tc.reason + assert testinstance.reason + assert '2/2' in testinstance.reason def test_if_report_with_skip(pytester, testinstance: TestInstance): @@ -140,3 +236,56 @@ def test_skip_2(): assert len(testinstance.testcases) == 2 for tc in testinstance.testcases: assert tc.status == "skipped" + + +def test_if_report_with_filter(pytester, testinstance: TestInstance): + test_file_content = textwrap.dedent(""" + import pytest + def test_A(): + pass + def test_B(): + pass + """) + test_file = pytester.path / 'test_filter.py' + test_file.write_text(test_file_content) + report_file = pytester.path / 'report.xml' + result = pytester.runpytest( + str(test_file), + '-k', 'test_B', + f'--junit-xml={str(report_file)}' + ) + result.assert_outcomes(passed=1) + assert report_file.is_file() + + pytest_harness = Pytest() + pytest_harness.configure(testinstance) + pytest_harness.report_file = report_file + pytest_harness._update_test_status() + assert pytest_harness.state == "passed" + assert testinstance.status == "passed" + assert len(testinstance.testcases) == 1 + + +def test_if_report_with_no_collected(pytester, testinstance: TestInstance): + test_file_content = textwrap.dedent(""" + import pytest + def test_A(): + pass + """) + test_file = pytester.path / 'test_filter.py' + test_file.write_text(test_file_content) + report_file = pytester.path / 'report.xml' + result = pytester.runpytest( + str(test_file), + '-k', 'test_B', + f'--junit-xml={str(report_file)}' + ) + result.assert_outcomes(passed=0) + assert report_file.is_file() + + pytest_harness = Pytest() + pytest_harness.configure(testinstance) + pytest_harness.report_file = report_file + pytest_harness._update_test_status() + assert pytest_harness.state == "skipped" + assert testinstance.status == "skipped" diff --git a/scripts/tests/twister/test_harness.py b/scripts/tests/twister/test_harness.py index a33d6431ab4..1da2aed3f46 100644 --- a/scripts/tests/twister/test_harness.py +++ b/scripts/tests/twister/test_harness.py @@ -40,6 +40,7 @@ def gtest(): mock_platform.name = "mock_platform" mock_testsuite = mock.Mock() mock_testsuite.name = "mock_testsuite" + mock_testsuite.detailed_test_id = True mock_testsuite.id = "id" mock_testsuite.testcases = [] instance = TestInstance(testsuite=mock_testsuite, platform=mock_platform, outdir="") diff --git a/scripts/tests/twister/test_runner.py b/scripts/tests/twister/test_runner.py index 7540da04394..37c02b6d17b 100644 --- a/scripts/tests/twister/test_runner.py +++ b/scripts/tests/twister/test_runner.py @@ -90,7 +90,7 @@ class MockHandler: handler.ready = True assert(ProjectBuilder.cmake_assemble_args( - ["basearg1"], + ["basearg1", "CONFIG_t=\"test\"", "SNIPPET_t=\"test\""], handler, ["a.conf;b.conf", "c.conf"], ["extra_overlay.conf"], @@ -98,8 +98,9 @@ class MockHandler: ["cmake1=foo", "cmake2=bar"], "/builddir/", ) == [ + "-DCONFIG_t=\"test\"", "-Dcmake1=foo", "-Dcmake2=bar", - "-Dbasearg1", + "-Dbasearg1", "-DSNIPPET_t=test", "-Dhandler_arg1", "-Dhandler_arg2", "-DCONF_FILE=a.conf;b.conf;c.conf", "-DDTC_OVERLAY_FILE=x.overlay;y.overlay;z.overlay", diff --git a/scripts/tests/twister/test_testsuite.py b/scripts/tests/twister/test_testsuite.py index 49f19d0d0f8..23e4f8ed034 100644 --- a/scripts/tests/twister/test_testsuite.py +++ b/scripts/tests/twister/test_testsuite.py @@ -749,3 +749,43 @@ def test_testcase_dunders(): assert case_lesser < case_greater assert str(case_greater) == 'a greater name' assert repr(case_greater) == '' + + +TESTDATA_11 = [ + ( + ZEPHYR_BASE + '/scripts/tests/twister/test_data/testsuites', + ZEPHYR_BASE + '/scripts/tests/twister/test_data/testsuites/tests/test_a', + 'test_a.check_1', + 'test_a.check_1' + ), + ( + ZEPHYR_BASE, + ZEPHYR_BASE, + 'test_a.check_1', + 'test_a.check_1' + ), + ( + ZEPHYR_BASE, + ZEPHYR_BASE + '/scripts/tests/twister/test_data/testsuites/test_b', + 'test_b.check_1', + 'test_b.check_1' + ), + ( + os.path.join(ZEPHYR_BASE, 'scripts/tests'), + os.path.join(ZEPHYR_BASE, 'scripts/tests'), + 'test_b.check_1', + 'test_b.check_1' + ), + ( + ZEPHYR_BASE, + ZEPHYR_BASE, + 'test_a.check_1.check_2', + 'test_a.check_1.check_2' + ), +] +@pytest.mark.parametrize("testsuite_root, suite_path, name, expected", TESTDATA_11) +def test_get_no_detailed_test_id(testsuite_root, suite_path, name, expected): + '''Test to check if the name without path is given for each testsuite''' + suite = TestSuite(testsuite_root, suite_path, name, detailed_test_id=False) + print(suite.name) + assert suite.name == expected diff --git a/scripts/twister b/scripts/twister index 0f5e622011f..f41349871ed 100755 --- a/scripts/twister +++ b/scripts/twister @@ -44,6 +44,9 @@ pairs: Extra configuration options to be merged with a master prj.conf when building or running the test case. + required_snippets: + Snippets that must be applied for the test case to run. + sysbuild: (default False) If true, build the sample using the sysbuild infrastructure. Filtering will only be enabled for the main project, and is not supported for diff --git a/scripts/west_commands/build.py b/scripts/west_commands/build.py index 65dbe963cd4..f7a605f4d7f 100644 --- a/scripts/west_commands/build.py +++ b/scripts/west_commands/build.py @@ -293,6 +293,7 @@ def _parse_test_item(self, test_item): extra_dtc_overlay_files = [] extra_overlay_confs = [] extra_conf_files = [] + required_snippets = [] for section in [common, item]: if not section: continue @@ -302,7 +303,8 @@ def _parse_test_item(self, test_item): 'extra_configs', 'extra_conf_files', 'extra_overlay_confs', - 'extra_dtc_overlay_files' + 'extra_dtc_overlay_files', + 'required_snippets' ]: extra = section.get(data) if not extra: @@ -315,7 +317,11 @@ def _parse_test_item(self, test_item): if data == 'extra_configs': args = ["-D{}".format(arg.replace('"', '\"')) for arg in arg_list] elif data == 'extra_args': - args = ["-D{}".format(arg.replace('"', '')) for arg in arg_list] + # Retain quotes around config options + config_options = [arg for arg in arg_list if arg.startswith("CONFIG_")] + non_config_options = [arg for arg in arg_list if not arg.startswith("CONFIG_")] + args = ["-D{}".format(a.replace('"', '\"')) for a in config_options] + args.extend(["-D{}".format(arg.replace('"', '')) for arg in non_config_options]) elif data == 'extra_conf_files': extra_conf_files.extend(arg_list) continue @@ -325,6 +331,9 @@ def _parse_test_item(self, test_item): elif data == 'extra_dtc_overlay_files': extra_dtc_overlay_files.extend(arg_list) continue + elif data == 'required_snippets': + required_snippets.extend(arg_list) + continue if self.args.cmake_opts: self.args.cmake_opts.extend(args) @@ -343,6 +352,10 @@ def _parse_test_item(self, test_item): if extra_overlay_confs: args.append(f"OVERLAY_CONFIG=\"{';'.join(extra_overlay_confs)}\"") + + if required_snippets: + args.append(f"SNIPPET=\"{';'.join(required_snippets)}\"") + # Build the final argument list args_expanded = ["-D{}".format(a.replace('"', '')) for a in args] diff --git a/scripts/west_commands/build_helpers.py b/scripts/west_commands/build_helpers.py index e4352ff1f28..ca6845f01da 100644 --- a/scripts/west_commands/build_helpers.py +++ b/scripts/west_commands/build_helpers.py @@ -151,8 +151,14 @@ def load_domains(path): domains_file = Path(path) / 'domains.yaml' if not domains_file.is_file(): - return Domains.from_data({'default': 'app', - 'build_dir': path, - 'domains': [{'name': 'app', 'build_dir': path}]}) + return Domains.from_yaml(f'''\ +default: app +build_dir: {path} +domains: + - name: app + build_dir: {path} +flash_order: + - app +''') return Domains.from_file(domains_file) diff --git a/scripts/west_commands/flash.py b/scripts/west_commands/flash.py index 073a1ab2a28..4f173535be8 100644 --- a/scripts/west_commands/flash.py +++ b/scripts/west_commands/flash.py @@ -28,5 +28,6 @@ def do_add_parser(self, parser_adder): def do_run(self, my_args, runner_args): build_dir = get_build_dir(my_args) - domains = load_domains(build_dir).get_domains(my_args.domain) + domains = load_domains(build_dir).get_domains(my_args.domain, + default_flash_order=True) do_run_common(self, my_args, runner_args, domains=domains) diff --git a/share/sysbuild/cmake/domains.cmake b/share/sysbuild/cmake/domains.cmake index c46d261aff2..1d197059bf1 100644 --- a/share/sysbuild/cmake/domains.cmake +++ b/share/sysbuild/cmake/domains.cmake @@ -7,12 +7,13 @@ sysbuild_images_order(IMAGES_FLASHING_ORDER FLASH IMAGES ${IMAGES}) set(domains_yaml "default: ${DEFAULT_IMAGE}") set(domains_yaml "${domains_yaml}\nbuild_dir: ${CMAKE_BINARY_DIR}") set(domains_yaml "${domains_yaml}\ndomains:") -foreach(image ${IMAGES_FLASHING_ORDER}) - get_target_property(image_is_build_only ${image} BUILD_ONLY) - if(image_is_build_only) - continue() - endif() +foreach(image ${IMAGES}) set(domains_yaml "${domains_yaml}\n - name: ${image}") set(domains_yaml "${domains_yaml}\n build_dir: $") endforeach() +set(domains_yaml "${domains_yaml}\nflash_order:") +foreach(image ${IMAGES_FLASHING_ORDER}) + set(flash_cond "$>>") + set(domains_yaml "${domains_yaml}$<${flash_cond}:\n - ${image}>") +endforeach() file(GENERATE OUTPUT ${CMAKE_BINARY_DIR}/domains.yaml CONTENT "${domains_yaml}") diff --git a/share/sysbuild/cmake/modules/sysbuild_extensions.cmake b/share/sysbuild/cmake/modules/sysbuild_extensions.cmake index a3838415eee..ac8bac25f8c 100644 --- a/share/sysbuild/cmake/modules/sysbuild_extensions.cmake +++ b/share/sysbuild/cmake/modules/sysbuild_extensions.cmake @@ -107,6 +107,87 @@ function(sysbuild_get variable) endif() endfunction() +# Usage: +# sysbuild_cache(CREATE APPLICATION [CMAKE_RERUN]) +# +# This function works on the sysbuild cache for sysbuild managed applications. +# +# Arguments: +# CREATE : Create or update existing sysbuild cache file for the application. +# The sysbuild cache is only updated if it contain changes. +# APPLICATION : Name of the application. +# CMAKE_RERUN : Force a CMake rerun for the application during next build +# invocation if the sysbuild cache has changed. It is +# advised to always use this flag. Not using this flag can +# reduce build time, but only do so if application is +# guranteed to be up-to-date. +# +function(sysbuild_cache) + cmake_parse_arguments(SB_CACHE "CREATE;CMAKE_RERUN" "APPLICATION" "" ${ARGN}) + zephyr_check_arguments_required(sysbuild_cache SB_CACHE APPLICATION) + zephyr_check_flags_required(sysbuild_cache SB_CACHE CREATE) + + get_target_property(${SB_CACHE_APPLICATION}_MAIN_APP ${SB_CACHE_APPLICATION} MAIN_APP) + get_cmake_property(sysbuild_cache CACHE_VARIABLES) + + foreach(var_name ${sysbuild_cache}) + if(NOT "${var_name}" MATCHES "^(CMAKE_.*|BOARD)$") + # Perform a dummy read to prevent a false warning about unused variables + # being emitted due to a cmake bug: https://gitlab.kitware.com/cmake/cmake/-/issues/24555 + set(unused_tmp_var ${${var_name}}) + + # We don't want to pass internal CMake variables. + # Required CMake variable to be passed, like CMAKE_BUILD_TYPE must be + # passed using `-D` on command invocation. + get_property(var_type CACHE ${var_name} PROPERTY TYPE) + set(cache_entry "${var_name}:${var_type}=$CACHE{${var_name}}") + string(REPLACE ";" "\;" cache_entry "${cache_entry}") + list(APPEND sysbuild_cache_strings "${cache_entry}\n") + endif() + endforeach() + if(DEFINED BOARD_REVISION) + list(APPEND sysbuild_cache_strings "BOARD:STRING=${BOARD}@${BOARD_REVISION}\n") + else() + list(APPEND sysbuild_cache_strings "BOARD:STRING=${BOARD}\n") + endif() + list(APPEND sysbuild_cache_strings "SYSBUILD_NAME:STRING=${SB_CACHE_APPLICATION}\n") + + if(${SB_CACHE_APPLICATION}_MAIN_APP) + list(APPEND sysbuild_cache_strings "SYSBUILD_MAIN_APP:BOOL=True\n") + endif() + + if(${SB_CACHE_APPLICATION}_BOARD AND NOT DEFINED CACHE{${SB_CACHE_APPLICATION}_BOARD}) + # Only set image specific board if provided. + # The sysbuild BOARD is exported through sysbuild cache, and will be used + # unless _BOARD is defined. + list(APPEND sysbuild_cache_strings + "${SB_CACHE_APPLICATION}_BOARD:STRING=${${SB_CACHE_APPLICATION}_BOARD}\n" + ) + endif() + + get_target_property(${SB_CACHE_APPLICATION}_CACHE_FILE ${SB_CACHE_APPLICATION} CACHE_FILE) + file(WRITE ${${SB_CACHE_APPLICATION}_CACHE_FILE}.tmp ${sysbuild_cache_strings}) + if(SB_CACHE_CMAKE_RERUN) + execute_process(COMMAND ${CMAKE_COMMAND} -E compare_files + ${${SB_CACHE_APPLICATION}_CACHE_FILE}.tmp + ${${SB_CACHE_APPLICATION}_CACHE_FILE} + RESULT_VARIABLE compare_res + ) + if(NOT compare_res EQUAL 0) + file(COPY_FILE ${${SB_CACHE_APPLICATION}_CACHE_FILE}.tmp + ${${SB_CACHE_APPLICATION}_CACHE_FILE} + ) + ExternalProject_Get_Property(${SB_CACHE_APPLICATION} BINARY_DIR) + file(TOUCH_NOCREATE ${BINARY_DIR}/CMakeCache.txt) + endif() + else() + zephyr_file_copy(${${SB_CACHE_APPLICATION}_CACHE_FILE}.tmp + ${${SB_CACHE_APPLICATION}_CACHE_FILE} ONLY_IF_DIFFERENT + ) + endif() + +endfunction() + # Usage: # ExternalZephyrProject_Add(APPLICATION # SOURCE_DIR @@ -372,9 +453,7 @@ function(ExternalZephyrProject_Cmake) ) ExternalProject_Get_Property(${ZCMAKE_APPLICATION} SOURCE_DIR BINARY_DIR CMAKE_ARGS) - get_target_property(${ZCMAKE_APPLICATION}_CACHE_FILE ${ZCMAKE_APPLICATION} CACHE_FILE) get_target_property(${ZCMAKE_APPLICATION}_BOARD ${ZCMAKE_APPLICATION} BOARD) - get_target_property(${ZCMAKE_APPLICATION}_MAIN_APP ${ZCMAKE_APPLICATION} MAIN_APP) get_property(${ZCMAKE_APPLICATION}_CONF_SCRIPT TARGET ${ZCMAKE_APPLICATION} PROPERTY IMAGE_CONF_SCRIPT @@ -390,46 +469,7 @@ function(ExternalZephyrProject_Cmake) endif() endforeach() - get_cmake_property(sysbuild_cache CACHE_VARIABLES) - foreach(var_name ${sysbuild_cache}) - if(NOT "${var_name}" MATCHES "^(CMAKE_.*|BOARD)$") - # Perform a dummy read to prevent a false warning about unused variables - # being emitted due to a cmake bug: https://gitlab.kitware.com/cmake/cmake/-/issues/24555 - set(unused_tmp_var ${${var_name}}) - - # We don't want to pass internal CMake variables. - # Required CMake variable to be passed, like CMAKE_BUILD_TYPE must be - # passed using `-D` on command invocation. - get_property(var_type CACHE ${var_name} PROPERTY TYPE) - set(cache_entry "${var_name}:${var_type}=$CACHE{${var_name}}") - string(REPLACE ";" "\;" cache_entry "${cache_entry}") - list(APPEND sysbuild_cache_strings "${cache_entry}\n") - endif() - endforeach() - if(DEFINED BOARD_REVISION) - list(APPEND sysbuild_cache_strings "BOARD:STRING=${BOARD}@${BOARD_REVISION}\n") - else() - list(APPEND sysbuild_cache_strings "BOARD:STRING=${BOARD}\n") - endif() - list(APPEND sysbuild_cache_strings "SYSBUILD_NAME:STRING=${ZCMAKE_APPLICATION}\n") - - if(${ZCMAKE_APPLICATION}_MAIN_APP) - list(APPEND sysbuild_cache_strings "SYSBUILD_MAIN_APP:BOOL=True\n") - endif() - - if(${ZCMAKE_APPLICATION}_BOARD AND NOT DEFINED CACHE{${ZCMAKE_APPLICATION}_BOARD}) - # Only set image specific board if provided. - # The sysbuild BOARD is exported through sysbuild cache, and will be used - # unless _BOARD is defined. - list(APPEND sysbuild_cache_strings - "${ZCMAKE_APPLICATION}_BOARD:STRING=${${ZCMAKE_APPLICATION}_BOARD}\n" - ) - endif() - - file(WRITE ${${ZCMAKE_APPLICATION}_CACHE_FILE}.tmp ${sysbuild_cache_strings}) - zephyr_file_copy(${${ZCMAKE_APPLICATION}_CACHE_FILE}.tmp - ${${ZCMAKE_APPLICATION}_CACHE_FILE} ONLY_IF_DIFFERENT - ) + sysbuild_cache(CREATE APPLICATION ${ZCMAKE_APPLICATION}) foreach(script ${${ZCMAKE_APPLICATION}_CONF_SCRIPT}) include(${script}) diff --git a/share/sysbuild/image_configurations/MAIN_image_default.cmake b/share/sysbuild/image_configurations/MAIN_image_default.cmake index a6bd72d7d1a..a2ae840ef97 100644 --- a/share/sysbuild/image_configurations/MAIN_image_default.cmake +++ b/share/sysbuild/image_configurations/MAIN_image_default.cmake @@ -9,6 +9,9 @@ set_config_bool(${ZCMAKE_APPLICATION} CONFIG_BOOTLOADER_MCUBOOT "${SB_CONFIG_BOO set_config_string(${ZCMAKE_APPLICATION} CONFIG_MCUBOOT_SIGNATURE_KEY_FILE "${SB_CONFIG_BOOT_SIGNATURE_KEY_FILE}" ) +set_config_string(${ZCMAKE_APPLICATION} CONFIG_MCUBOOT_ENCRYPTION_KEY_FILE + "${SB_CONFIG_BOOT_ENCRYPTION_KEY_FILE}" +) if(SB_CONFIG_BOOTLOADER_MCUBOOT) if("${SB_CONFIG_SIGNATURE_TYPE}" STREQUAL "NONE") diff --git a/share/sysbuild/images/bootloader/CMakeLists.txt b/share/sysbuild/images/bootloader/CMakeLists.txt index c00a8e97783..2a0f12f958d 100644 --- a/share/sysbuild/images/bootloader/CMakeLists.txt +++ b/share/sysbuild/images/bootloader/CMakeLists.txt @@ -15,4 +15,8 @@ if(SB_CONFIG_BOOTLOADER_MCUBOOT) sysbuild_add_dependencies(FLASH ${DEFAULT_IMAGE} ${image}) set_config_string(${image} CONFIG_BOOT_SIGNATURE_KEY_FILE "${SB_CONFIG_BOOT_SIGNATURE_KEY_FILE}") + set_config_bool(${image} CONFIG_BOOT_ENCRYPT_IMAGE "${SB_CONFIG_BOOT_ENCRYPTION}") + if(SB_CONFIG_BOOT_ENCRYPTION) + set_config_string(${image} CONFIG_BOOT_ENCRYPTION_KEY_FILE "${SB_CONFIG_BOOT_ENCRYPTION_KEY_FILE}") + endif() endif() diff --git a/share/sysbuild/images/bootloader/Kconfig b/share/sysbuild/images/bootloader/Kconfig index 19924a6ca18..e8c788f72c5 100644 --- a/share/sysbuild/images/bootloader/Kconfig +++ b/share/sysbuild/images/bootloader/Kconfig @@ -56,12 +56,28 @@ config BOOT_SIGNATURE_TYPE_ED25519 endchoice config BOOT_SIGNATURE_KEY_FILE - string "PEM key file" + string "Signing PEM key file" default "$(ZEPHYR_MCUBOOT_MODULE_DIR)/root-ec-p256.pem" if BOOT_SIGNATURE_TYPE_ECDSA_P256 default "$(ZEPHYR_MCUBOOT_MODULE_DIR)/root-ed25519.pem" if BOOT_SIGNATURE_TYPE_ED25519 default "$(ZEPHYR_MCUBOOT_MODULE_DIR)/root-rsa-2048.pem" if BOOT_SIGNATURE_TYPE_RSA default "" help - Absolute path to key file to use with MCUBoot. + Absolute path to signing key file to use with MCUBoot. + +config BOOT_ENCRYPTION + bool "Encrypted image support" + depends on !BOOT_SIGNATURE_TYPE_NONE + help + Support encrypted images. + +config BOOT_ENCRYPTION_KEY_FILE + string "Encryption PEM key file" + depends on BOOT_ENCRYPTION + default "$(ZEPHYR_MCUBOOT_MODULE_DIR)/enc-ec256-priv.pem" if BOOT_SIGNATURE_TYPE_ECDSA_P256 + default "$(ZEPHYR_MCUBOOT_MODULE_DIR)/enc-x25519-priv.pem" if BOOT_SIGNATURE_TYPE_ED25519 + default "$(ZEPHYR_MCUBOOT_MODULE_DIR)/enc-rsa2048-priv.pem" if BOOT_SIGNATURE_TYPE_RSA + default "" + help + Absolute path to encryption key file to use with MCUBoot. endif diff --git a/soc/Kconfig b/soc/Kconfig index 20734c0f51f..33c2d0c29a4 100644 --- a/soc/Kconfig +++ b/soc/Kconfig @@ -24,8 +24,7 @@ source "subsys/logging/Kconfig.template.log_config" endmenu # The helper symbols below are put here due to an unusual setup: The simulated -# nrf52_bsim board uses the POSIX arch, but is compatible with Nordic ARM -# boards +# nrf5x_bsim boards use the POSIX arch, but are compatible with Nordic ARM boards config SOC_COMPATIBLE_NRF bool @@ -33,9 +32,18 @@ config SOC_COMPATIBLE_NRF config SOC_COMPATIBLE_NRF52X bool +config SOC_COMPATIBLE_NRF53X + bool + config SOC_COMPATIBLE_NRF52833 bool +config SOC_COMPATIBLE_NRF5340_CPUNET + bool + +config SOC_COMPATIBLE_NRF5340_CPUAPP + bool + # # SOC_*_LD: SoC specific Linker script additions # diff --git a/soc/arm/Kconfig b/soc/arm/Kconfig index 443a0031189..461f0b41f84 100644 --- a/soc/arm/Kconfig +++ b/soc/arm/Kconfig @@ -34,28 +34,12 @@ config CPU_HAS_ARM_SAU config CPU_HAS_NRF_IDAU bool - depends on SOC_SERIES_NRF91X || SOC_NRF5340_CPUAPP select CPU_HAS_TEE help MCU implements the nRF (vendor-specific) Security Attribution Unit. (IDAU: "Implementation-Defined Attribution Unit", in accordance with ARM terminology). -if CPU_HAS_NRF_IDAU -config NRF_SPU_FLASH_REGION_SIZE - hex - default 0x8000 if SOC_SERIES_NRF91X - default 0x4000 if SOC_NRF5340_CPUAPP - help - FLASH region size for the NRF_SPU peripheral - -config NRF_SPU_RAM_REGION_SIZE - hex - default 0x2000 if SOC_SERIES_NRF91X || SOC_NRF5340_CPUAPP - help - RAM region size for the NRF_SPU peripheral -endif - config HAS_SWO bool help diff --git a/soc/arm/common/cortex_m/arm_mpu_regions.c b/soc/arm/common/cortex_m/arm_mpu_regions.c index 557079ef5bc..a7df11a19e1 100644 --- a/soc/arm/common/cortex_m/arm_mpu_regions.c +++ b/soc/arm/common/cortex_m/arm_mpu_regions.c @@ -9,6 +9,9 @@ #include #include +#if USE_PARTITION_MANAGER +#include +#endif static const struct arm_mpu_region mpu_regions[] = { /* Region 0 */ @@ -22,6 +25,14 @@ static const struct arm_mpu_region mpu_regions[] = { #endif /* Region 1 */ MPU_REGION_ENTRY("SRAM_0", +#if USE_PARTITION_MANAGER + PM_SRAM_ADDRESS, +#if defined(CONFIG_ARMV8_M_BASELINE) || defined(CONFIG_ARMV8_M_MAINLINE) + REGION_RAM_ATTR(PM_SRAM_ADDRESS, PM_SRAM_SIZE)), +#else + REGION_RAM_ATTR(REGION_SRAM_SIZE)), +#endif +#else CONFIG_SRAM_BASE_ADDRESS, #if defined(CONFIG_ARMV8_M_BASELINE) || defined(CONFIG_ARMV8_M_MAINLINE) REGION_RAM_ATTR(CONFIG_SRAM_BASE_ADDRESS, \ @@ -30,6 +41,8 @@ static const struct arm_mpu_region mpu_regions[] = { REGION_RAM_ATTR(REGION_SRAM_SIZE)), #endif +#endif /* USE_PARTITION_MANAGER */ + /* DT-defined regions */ DT_MEMORY_ATTR_APPLY(ARM_MPU_REGION_INIT) }; diff --git a/soc/arm/nordic_nrf/CMakeLists.txt b/soc/arm/nordic_nrf/CMakeLists.txt index 4cfc162f7fd..3b097d73569 100644 --- a/soc/arm/nordic_nrf/CMakeLists.txt +++ b/soc/arm/nordic_nrf/CMakeLists.txt @@ -21,4 +21,12 @@ if(CONFIG_BUILD_WITH_TFM) set_property(TARGET zephyr_property_target APPEND PROPERTY TFM_CMAKE_OPTIONS -DHAL_NORDIC_PATH=${ZEPHYR_HAL_NORDIC_MODULE_DIR} ) + + set_property(TARGET zephyr_property_target + APPEND PROPERTY TFM_CMAKE_OPTIONS -DZEPHYR_BASE=${ZEPHYR_BASE} + ) + + set_property(TARGET zephyr_property_target + APPEND PROPERTY TFM_CMAKE_OPTIONS -DNRF_NS_STORAGE=${CONFIG_TFM_NRF_NS_STORAGE} + ) endif() diff --git a/soc/arm/nordic_nrf/Kconfig b/soc/arm/nordic_nrf/Kconfig index 2c386f99a53..0372492cd7d 100644 --- a/soc/arm/nordic_nrf/Kconfig +++ b/soc/arm/nordic_nrf/Kconfig @@ -25,6 +25,33 @@ config NRF_SOC_SECURE_SUPPORTED For non-secure the functions must redirect to secure services exposed by the secure firmware. +config BUILD_WITH_TFM + default y if TRUSTED_EXECUTION_NONSECURE + help + By default, if we build for a Non-Secure version of the board, + enable building with TF-M as the Secure Execution Environment. + +if BUILD_WITH_TFM + +config TFM_FLASH_MERGED_BINARY + default y + help + By default, if we build with TF-M, instruct build system to + flash the combined TF-M (Secure) & Zephyr (Non Secure) image + +config TFM_LOG_LEVEL_SILENCE + default y if !$(dt_nodelabel_has_prop,uart1,pinctrl-names) + help + Disable TF-M secure output if the uart1 node has not assigned GPIO + pins using pinctrl. + +config TFM_NRF_NS_STORAGE + bool "TF-M non-secure storage partition" + default y + +endif # BUILD_WITH_TFM + + config NRF_MPU_FLASH_REGION_SIZE hex default 0x1000 @@ -47,8 +74,9 @@ config NRF_ACL_FLASH_REGION_SIZE FLASH region size for the NRF_ACL peripheral. config NFCT_PINS_AS_GPIOS - bool "NFCT pins as GPIOs" + bool "[DEPRECATED] NFCT pins as GPIOs" depends on $(dt_has_compat,$(DT_COMPAT_NORDIC_NRF_NFCT)) + select DEPRECATED help Two pins are usually reserved for NFC in SoCs that implement the NFCT peripheral. This option switches them to normal GPIO mode. @@ -60,6 +88,13 @@ config NFCT_PINS_AS_GPIOS NFC pins in nRF52 series: P0.09 and P0.10 NFC pins in nRF5340: P0.02 and P0.03 + This option is deprecated, please use devicetree to configure NFCT + pins as GPIOS like this: + + &uicr { + nfct-pins-as-gpios; + }; + choice NRF_APPROTECT_HANDLING bool "APPROTECT handling" depends on SOC_SERIES_NRF52X || SOC_NRF5340_CPUNET || \ diff --git a/soc/arm/nordic_nrf/Kconfig.peripherals b/soc/arm/nordic_nrf/Kconfig.peripherals index c7bfb552409..1645cf34ea0 100644 --- a/soc/arm/nordic_nrf/Kconfig.peripherals +++ b/soc/arm/nordic_nrf/Kconfig.peripherals @@ -13,10 +13,12 @@ config HAS_HW_NRF_BPROT def_bool $(dt_compat_enabled,$(DT_COMPAT_NORDIC_NRF_BPROT)) config HAS_HW_NRF_CC310 - def_bool $(dt_compat_enabled,$(DT_COMPAT_NORDIC_NRF_CC310)) + def_bool $(dt_compat_enabled,$(DT_COMPAT_NORDIC_NRF_CC310)) || \ + ($(dt_nodelabel_enabled,psa_rng) && SOC_SERIES_NRF91X) config HAS_HW_NRF_CC312 - def_bool $(dt_compat_enabled,$(DT_COMPAT_NORDIC_NRF_CC312)) + def_bool $(dt_compat_enabled,$(DT_COMPAT_NORDIC_NRF_CC312)) || \ + ($(dt_nodelabel_enabled,psa_rng) && SOC_NRF5340_CPUAPP) config HAS_HW_NRF_CCM def_bool $(dt_compat_enabled,$(DT_COMPAT_NORDIC_NRF_CCM)) diff --git a/soc/arm/nordic_nrf/nrf51/soc.c b/soc/arm/nordic_nrf/nrf51/soc.c index 078a422c06b..2b22c95679f 100644 --- a/soc/arm/nordic_nrf/nrf51/soc.c +++ b/soc/arm/nordic_nrf/nrf51/soc.c @@ -29,7 +29,7 @@ LOG_MODULE_REGISTER(soc); */ void sys_arch_reboot(int type) { - nrf_power_gpregret_set(NRF_POWER, (uint8_t)type); + nrf_power_gpregret_set(NRF_POWER, 0, (uint8_t)type); NVIC_SystemReset(); } #endif diff --git a/soc/arm/nordic_nrf/nrf52/Kconfig.soc b/soc/arm/nordic_nrf/nrf52/Kconfig.soc index 13a2fef932b..b2a915f168e 100644 --- a/soc/arm/nordic_nrf/nrf52/Kconfig.soc +++ b/soc/arm/nordic_nrf/nrf52/Kconfig.soc @@ -94,8 +94,15 @@ config SOC_DCDC_NRF52X_HV Enable nRF52 series System on Chip High Voltage DC/DC converter. config GPIO_AS_PINRESET - bool "GPIO as pin reset (reset button)" - default y + bool "[DEPRECATED] GPIO as pin reset (reset button)" + select DEPRECATED + help + This option is deprecated, use devicetree instead. Example + configuration: + + &uicr { + gpio-as-nreset; + }; config NRF_ENABLE_ICACHE bool "The instruction cache (I-Cache)" diff --git a/soc/arm/nordic_nrf/nrf52/soc.c b/soc/arm/nordic_nrf/nrf52/soc.c index 52b97164e72..5f310e5f945 100644 --- a/soc/arm/nordic_nrf/nrf52/soc.c +++ b/soc/arm/nordic_nrf/nrf52/soc.c @@ -31,7 +31,7 @@ LOG_MODULE_REGISTER(soc); */ void sys_arch_reboot(int type) { - nrf_power_gpregret_set(NRF_POWER, (uint8_t)type); + nrf_power_gpregret_set(NRF_POWER, 0, (uint8_t)type); NVIC_SystemReset(); } #endif diff --git a/soc/arm/nordic_nrf/nrf53/Kconfig.series b/soc/arm/nordic_nrf/nrf53/Kconfig.series index 03e47bacf63..8ab81ad310b 100644 --- a/soc/arm/nordic_nrf/nrf53/Kconfig.series +++ b/soc/arm/nordic_nrf/nrf53/Kconfig.series @@ -6,6 +6,7 @@ config SOC_SERIES_NRF53X bool "Nordic Semiconductor nRF53 series MCU" select ARM + select SOC_COMPATIBLE_NRF53X select CPU_CORTEX_M33 select CPU_CORTEX_M_HAS_DWT select CPU_HAS_ARM_MPU diff --git a/soc/arm/nordic_nrf/nrf53/Kconfig.soc b/soc/arm/nordic_nrf/nrf53/Kconfig.soc index 122c36988e5..1c8527e1716 100644 --- a/soc/arm/nordic_nrf/nrf53/Kconfig.soc +++ b/soc/arm/nordic_nrf/nrf53/Kconfig.soc @@ -10,11 +10,15 @@ config SOC_NRF5340_CPUAPP select CPU_HAS_FPU select ARMV8_M_DSP select HAS_POWEROFF + select SOC_COMPATIBLE_NRF5340_CPUAPP + imply SOC_NRF53_RTC_PRETICK config SOC_NRF5340_CPUNET bool select ARM_ON_EXIT_CPU_IDLE + select SOC_COMPATIBLE_NRF5340_CPUNET imply SOC_NRF53_ANOMALY_160_WORKAROUND_NEEDED + imply SOC_NRF53_RTC_PRETICK if !WDT_NRFX choice prompt "nRF53x MCU Selection" @@ -47,6 +51,34 @@ config SOC_NRF53_ANOMALY_160_WORKAROUND depends on SYS_CLOCK_EXISTS select ARM_ON_ENTER_CPU_IDLE_HOOK +config SOC_NRF53_RTC_PRETICK + bool "Pre-tick workaround for nRF5340 anomaly 165" + depends on (SYS_CLOCK_EXISTS && SOC_NRF5340_CPUNET) || SOC_NRF5340_CPUAPP + select NRFX_DPPI + select ARM_ON_ENTER_CPU_IDLE_HOOK if SOC_NRF5340_CPUNET + select ARM_ON_ENTER_CPU_IDLE_PREPARE_HOOK if SOC_NRF5340_CPUNET + help + Indicates that the pre-tick workaround for the anomaly 165 that affects + the nRF5340 SoC should be applied. The workaround applies to wake ups caused + by EVENTS_COMPARE and EVENTS_OVRFLW on RTC0 and RTC1 for which interrupts are + enabled through INTENSET register. The case when these events are generated + by EVTEN but without interrupts enabled through INTENSET is not handled. + The EVENTS_TICK event is not handled. + +if SOC_NRF53_RTC_PRETICK + +config SOC_NRF53_RTC_PRETICK_IPC_CH_FROM_NET + int "IPC 0 channel for RTC pretick" + range 0 15 + default 10 + +config SOC_NRF53_RTC_PRETICK_IPC_CH_TO_NET + int "IPC 1 channel for RTC pretick" + range 0 15 + default 11 + +endif + if SOC_NRF5340_CPUAPP config SOC_DCDC_NRF53X_APP @@ -66,6 +98,32 @@ config SOC_DCDC_NRF53X_HV help Enable nRF53 series System on Chip High Voltage DC/DC converter. +config NRF_SPU_FLASH_REGION_SIZE + hex + default 0x4000 + help + FLASH region size for the NRF_SPU peripheral + +config NRF_SPU_FLASH_REGION_ALIGNMENT + hex + default 0x4000 + help + FLASH regions must be aligned to this value due to SPU HW + limitations. + +config NRF_SPU_RAM_REGION_SIZE + hex + default 0x2000 + help + RAM region size for the NRF_SPU peripheral + +config NRF_SPU_RAM_REGION_ALIGNMENT + hex + default 0x2000 + help + RAM regions must be aligned to this value due to SPU HW + limitations. + config SOC_NRF_GPIO_FORWARDER_FOR_NRF5340 bool depends on NRF_SOC_SECURE_SUPPORTED @@ -176,6 +234,7 @@ config NRF53_SYNC_RTC_INIT_PRIORITY nRF53 Synchronized RTC initialization priority. config NRF_RTC_TIMER_USER_CHAN_COUNT + default 2 if NRF_802154_RADIO_DRIVER && SOC_NRF5340_CPUNET default 3 if NRF_802154_RADIO_DRIVER default 1 diff --git a/soc/arm/nordic_nrf/nrf53/soc.c b/soc/arm/nordic_nrf/nrf53/soc.c index 6552f9d4bb1..3542ef3e914 100644 --- a/soc/arm/nordic_nrf/nrf53/soc.c +++ b/soc/arm/nordic_nrf/nrf53/soc.c @@ -14,10 +14,13 @@ #include #include +#include #include #include #include #include +#include +#include #if defined(CONFIG_SOC_NRF5340_CPUAPP) #include #include @@ -31,6 +34,8 @@ #if defined(CONFIG_PM_S2RAM) #include #endif +#include +#include #include #include @@ -38,6 +43,12 @@ #define PIN_XL1 0 #define PIN_XL2 1 +#define RTC1_PRETICK_CC_CHAN (RTC1_CC_NUM - 1) + +/* Mask of CC channels capable of generating interrupts, see nrf_rtc_timer.c */ +#define RTC1_PRETICK_SELECTED_CC_MASK BIT_MASK(CONFIG_NRF_RTC_TIMER_USER_CHAN_COUNT + 1U) +#define RTC0_PRETICK_SELECTED_CC_MASK BIT_MASK(NRF_RTC_CC_COUNT_MAX) + #if defined(CONFIG_SOC_NRF_GPIO_FORWARDER_FOR_NRF5340) #define GPIOS_PSEL_BY_IDX(node_id, prop, idx) \ NRF_DT_GPIOS_TO_PSEL_BY_IDX(node_id, prop, idx), @@ -122,25 +133,359 @@ static bool nrf53_anomaly_160_check(void) return true; } +#endif /* CONFIG_SOC_NRF53_ANOMALY_160_WORKAROUND */ + +#if defined(CONFIG_SOC_NRF53_RTC_PRETICK) && defined(CONFIG_SOC_NRF5340_CPUNET) + +BUILD_ASSERT(!IS_ENABLED(CONFIG_WDT_NRFX), + "For CONFIG_SOC_NRF53_RTC_PRETICK watchdog is used internally for the pre-tick workaround on nRF5340 cpunet. Application cannot use the watchdog."); + +static inline uint32_t rtc_counter_sub(uint32_t a, uint32_t b) +{ + return (a - b) & NRF_RTC_COUNTER_MAX; +} + +static bool rtc_ticks_to_next_event_get(NRF_RTC_Type *rtc, uint32_t selected_cc_mask, uint32_t cntr, + uint32_t *ticks_to_next_event) +{ + bool result = false; + + /* Let's preload register to speed-up. */ + uint32_t reg_intenset = rtc->INTENSET; + + /* Note: TICK event not handled. */ + + if (reg_intenset & NRF_RTC_INT_OVERFLOW_MASK) { + /* Overflow can generate an interrupt. */ + *ticks_to_next_event = NRF_RTC_COUNTER_MAX + 1U - cntr; + result = true; + } + + for (uint32_t chan = 0; chan < NRF_RTC_CC_COUNT_MAX; chan++) { + if ((selected_cc_mask & (1U << chan)) && + (reg_intenset & NRF_RTC_CHANNEL_INT_MASK(chan))) { + /* The CC is in selected mask and is can generate an interrupt. */ + uint32_t cc = nrf_rtc_cc_get(rtc, chan); + uint32_t ticks_to_fire = rtc_counter_sub(cc, cntr); + + if (ticks_to_fire == 0U) { + /* When ticks_to_fire == 0, the event should have been just + * generated the interrupt can be already handled or be pending. + * However the next event is expected to be after counter wraps. + */ + ticks_to_fire = NRF_RTC_COUNTER_MAX + 1U; + } + + if (!result) { + *ticks_to_next_event = ticks_to_fire; + result = true; + } else if (ticks_to_fire < *ticks_to_next_event) { + *ticks_to_next_event = ticks_to_fire; + result = true; + } else { + /* CC that fires no earlier than already found. */ + } + } + } + + return result; +} + +static void rtc_counter_synchronized_get(NRF_RTC_Type *rtc_a, NRF_RTC_Type *rtc_b, + uint32_t *counter_a, uint32_t *counter_b) +{ + do { + *counter_a = nrf_rtc_counter_get(rtc_a); + barrier_dmem_fence_full(); + *counter_b = nrf_rtc_counter_get(rtc_b); + barrier_dmem_fence_full(); + } while (*counter_a != nrf_rtc_counter_get(rtc_a)); +} + +static uint8_t cpu_idle_prepare_monitor_dummy; +static bool cpu_idle_prepare_allows_sleep; + +static void cpu_idle_prepare_monitor_begin(void) +{ + __LDREXB(&cpu_idle_prepare_monitor_dummy); +} + +/* Returns 0 if no exception preempted since the last call to cpu_idle_prepare_monitor_begin. */ +static bool cpu_idle_prepare_monitor_end(void) +{ + /* The value stored is irrelevant. If any exception took place after + * cpu_idle_prepare_monitor_begin, the the local monitor is cleared and + * the store fails returning 1. + * See Arm v8-M Architecture Reference Manual: + * Chapter B9.2 The local monitors + * Chapter B9.4 Exclusive access instructions and the monitors + * See Arm Cortex-M33 Processor Technical Reference Manual + * Chapter 3.5 Exclusive monitor + */ + return __STREXB(0U, &cpu_idle_prepare_monitor_dummy); +} + +static void rtc_pretick_finish_previous(void) +{ + NRF_IPC->PUBLISH_RECEIVE[CONFIG_SOC_NRF53_RTC_PRETICK_IPC_CH_TO_NET] &= + ~IPC_PUBLISH_RECEIVE_EN_Msk; + + nrf_rtc_event_clear(NRF_RTC1, NRF_RTC_CHANNEL_EVENT_ADDR(RTC1_PRETICK_CC_CHAN)); +} + + +void z_arm_on_enter_cpu_idle_prepare(void) +{ + bool ok_to_sleep = true; + + cpu_idle_prepare_monitor_begin(); + + uint32_t rtc_counter = 0U; + uint32_t rtc_ticks_to_next_event = 0U; + uint32_t rtc0_counter = 0U; + uint32_t rtc0_ticks_to_next_event = 0U; + + rtc_counter_synchronized_get(NRF_RTC1, NRF_RTC0, &rtc_counter, &rtc0_counter); + + bool rtc_scheduled = rtc_ticks_to_next_event_get(NRF_RTC1, RTC1_PRETICK_SELECTED_CC_MASK, + rtc_counter, &rtc_ticks_to_next_event); + + if (rtc_ticks_to_next_event_get(NRF_RTC0, RTC0_PRETICK_SELECTED_CC_MASK, rtc0_counter, + &rtc0_ticks_to_next_event)) { + /* An event is scheduled on RTC0. */ + if (!rtc_scheduled) { + rtc_ticks_to_next_event = rtc0_ticks_to_next_event; + rtc_scheduled = true; + } else if (rtc0_ticks_to_next_event < rtc_ticks_to_next_event) { + rtc_ticks_to_next_event = rtc0_ticks_to_next_event; + } else { + /* Event on RTC0 will not happen earlier than already found. */ + } + } + + if (rtc_scheduled) { + static bool rtc_pretick_cc_set_on_time; + /* The pretick should happen 1 tick before the earliest scheduled event + * that can trigger an interrupt. + */ + uint32_t rtc_pretick_cc_val = (rtc_counter + rtc_ticks_to_next_event - 1U) + & NRF_RTC_COUNTER_MAX; + + if (rtc_pretick_cc_val != nrf_rtc_cc_get(NRF_RTC1, RTC1_PRETICK_CC_CHAN)) { + /* The CC for pretick needs to be updated. */ + rtc_pretick_finish_previous(); + nrf_rtc_cc_set(NRF_RTC1, RTC1_PRETICK_CC_CHAN, rtc_pretick_cc_val); + + if (rtc_ticks_to_next_event >= NRF_RTC_COUNTER_MAX/2) { + /* Pretick is scheduled so far in the future, assumed on time. */ + rtc_pretick_cc_set_on_time = true; + } else { + /* Let's check if we updated CC on time, so that the CC can + * take effect. + */ + barrier_dmem_fence_full(); + rtc_counter = nrf_rtc_counter_get(NRF_RTC1); + uint32_t pretick_cc_to_counter = + rtc_counter_sub(rtc_pretick_cc_val, rtc_counter); + + if ((pretick_cc_to_counter < 3) || + (pretick_cc_to_counter >= NRF_RTC_COUNTER_MAX/2)) { + /* The COUNTER value is close enough to the expected + * pretick CC or has just expired, so the pretick event + * generation is not guaranteed. + */ + rtc_pretick_cc_set_on_time = false; + } else { + /* The written rtc_pretick_cc is guaranteed to to trigger + * compare event. + */ + rtc_pretick_cc_set_on_time = true; + } + } + } else { + /* The CC for pretick doesn't need to be updated, however + * rtc_pretick_cc_set_on_time still holds if we managed to set it on time. + */ + } + + /* If the CC for pretick is set on time, so the pretick CC event can be reliably + * generated then allow to sleep. Otherwise (the CC for pretick cannot be reliably + * generated, because CC was set very short to it's fire time) sleep not at all. + */ + ok_to_sleep = rtc_pretick_cc_set_on_time; + } else { + /* No events on any RTC timers are scheduled. */ + } + + if (ok_to_sleep) { + NRF_IPC->PUBLISH_RECEIVE[CONFIG_SOC_NRF53_RTC_PRETICK_IPC_CH_TO_NET] |= + IPC_PUBLISH_RECEIVE_EN_Msk; + if (!nrf_rtc_event_check(NRF_RTC1, + NRF_RTC_CHANNEL_EVENT_ADDR(RTC1_PRETICK_CC_CHAN))) { + NRF_WDT->TASKS_STOP = 1; + /* Check if any event did not occur after we checked for + * stopping condition. If yes, we might have stopped WDT + * when it should be running. Restart it. + */ + if (nrf_rtc_event_check(NRF_RTC1, + NRF_RTC_CHANNEL_EVENT_ADDR(RTC1_PRETICK_CC_CHAN))) { + NRF_WDT->TASKS_START = 1; + } + } + } + + cpu_idle_prepare_allows_sleep = ok_to_sleep; +} +#endif /* CONFIG_SOC_NRF53_RTC_PRETICK && CONFIG_SOC_NRF5340_CPUNET */ +#if defined(CONFIG_SOC_NRF53_ANOMALY_160_WORKAROUND) || \ + (defined(CONFIG_SOC_NRF53_RTC_PRETICK) && defined(CONFIG_SOC_NRF5340_CPUNET)) bool z_arm_on_enter_cpu_idle(void) { - bool ok_to_sleep = nrf53_anomaly_160_check(); + bool ok_to_sleep = true; -#if (LOG_LEVEL >= LOG_LEVEL_DBG) - static bool suppress_message; +#if defined(CONFIG_SOC_NRF53_RTC_PRETICK) && defined(CONFIG_SOC_NRF5340_CPUNET) + if (cpu_idle_prepare_monitor_end() == 0) { + /* No exception happened since cpu_idle_prepare_monitor_begin. + * We can trust the outcome of. z_arm_on_enter_cpu_idle_prepare + */ + ok_to_sleep = cpu_idle_prepare_allows_sleep; + } else { + /* Exception happened since cpu_idle_prepare_monitor_begin. + * The values which z_arm_on_enter_cpu_idle_prepare could be changed + * by the exception, so we can not trust to it's outcome. + * Do not sleep at all, let's try in the next iteration of idle loop. + */ + ok_to_sleep = false; + } +#endif +#if defined(CONFIG_SOC_NRF53_ANOMALY_160_WORKAROUND) if (ok_to_sleep) { - suppress_message = false; - } else if (!suppress_message) { - LOG_DBG("Anomaly 160 trigger conditions detected."); - suppress_message = true; + ok_to_sleep = nrf53_anomaly_160_check(); + +#if (LOG_LEVEL >= LOG_LEVEL_DBG) + static bool suppress_message; + + if (ok_to_sleep) { + suppress_message = false; + } else if (!suppress_message) { + LOG_DBG("Anomaly 160 trigger conditions detected."); + suppress_message = true; + } +#endif + } +#endif /* CONFIG_SOC_NRF53_ANOMALY_160_WORKAROUND */ + +#if defined(CONFIG_SOC_NRF53_RTC_PRETICK) && defined(CONFIG_SOC_NRF5340_CPUNET) + if (!ok_to_sleep) { + NRF_IPC->PUBLISH_RECEIVE[CONFIG_SOC_NRF53_RTC_PRETICK_IPC_CH_TO_NET] &= + ~IPC_PUBLISH_RECEIVE_EN_Msk; + NRF_WDT->TASKS_STOP = 1; } #endif return ok_to_sleep; } -#endif /* CONFIG_SOC_NRF53_ANOMALY_160_WORKAROUND */ +#endif /* CONFIG_SOC_NRF53_ANOMALY_160_WORKAROUND || + * (CONFIG_SOC_NRF53_RTC_PRETICK && CONFIG_SOC_NRF5340_CPUNET) + */ + +#if CONFIG_SOC_NRF53_RTC_PRETICK +#ifdef CONFIG_SOC_NRF5340_CPUAPP +/* RTC pretick - application core part. */ +static int rtc_pretick_cpuapp_init(void) +{ + uint8_t ch; + nrfx_err_t err; + nrf_ipc_event_t ipc_event = + nrf_ipc_receive_event_get(CONFIG_SOC_NRF53_RTC_PRETICK_IPC_CH_FROM_NET); + nrf_ipc_task_t ipc_task = + nrf_ipc_send_task_get(CONFIG_SOC_NRF53_RTC_PRETICK_IPC_CH_TO_NET); + uint32_t task_ipc = nrf_ipc_task_address_get(NRF_IPC, ipc_task); + uint32_t evt_ipc = nrf_ipc_event_address_get(NRF_IPC, ipc_event); + + err = nrfx_gppi_channel_alloc(&ch); + if (err != NRFX_SUCCESS) { + return -ENOMEM; + } + + nrf_ipc_receive_config_set(NRF_IPC, CONFIG_SOC_NRF53_RTC_PRETICK_IPC_CH_FROM_NET, + BIT(CONFIG_SOC_NRF53_RTC_PRETICK_IPC_CH_FROM_NET)); + nrf_ipc_send_config_set(NRF_IPC, CONFIG_SOC_NRF53_RTC_PRETICK_IPC_CH_TO_NET, + BIT(CONFIG_SOC_NRF53_RTC_PRETICK_IPC_CH_TO_NET)); + + nrfx_gppi_task_endpoint_setup(ch, task_ipc); + nrfx_gppi_event_endpoint_setup(ch, evt_ipc); + nrfx_gppi_channels_enable(BIT(ch)); + + return 0; +} +#else /* CONFIG_SOC_NRF5340_CPUNET */ + +void rtc_pretick_rtc0_isr_hook(void) +{ + rtc_pretick_finish_previous(); +} + +void rtc_pretick_rtc1_isr_hook(void) +{ + rtc_pretick_finish_previous(); +} + +static int rtc_pretick_cpunet_init(void) +{ + uint8_t ppi_ch; + nrf_ipc_task_t ipc_task = + nrf_ipc_send_task_get(CONFIG_SOC_NRF53_RTC_PRETICK_IPC_CH_FROM_NET); + nrf_ipc_event_t ipc_event = + nrf_ipc_receive_event_get(CONFIG_SOC_NRF53_RTC_PRETICK_IPC_CH_TO_NET); + uint32_t task_ipc = nrf_ipc_task_address_get(NRF_IPC, ipc_task); + uint32_t evt_ipc = nrf_ipc_event_address_get(NRF_IPC, ipc_event); + uint32_t task_wdt = nrf_wdt_task_address_get(NRF_WDT, NRF_WDT_TASK_START); + uint32_t evt_cc = nrf_rtc_event_address_get(NRF_RTC1, + NRF_RTC_CHANNEL_EVENT_ADDR(RTC1_PRETICK_CC_CHAN)); + + /* Configure Watchdog to allow stopping. */ + nrf_wdt_behaviour_set(NRF_WDT, WDT_CONFIG_STOPEN_Msk | BIT(4)); + *((volatile uint32_t *)0x41203120) = 0x14; + + /* Configure IPC */ + nrf_ipc_receive_config_set(NRF_IPC, CONFIG_SOC_NRF53_RTC_PRETICK_IPC_CH_TO_NET, + BIT(CONFIG_SOC_NRF53_RTC_PRETICK_IPC_CH_TO_NET)); + nrf_ipc_send_config_set(NRF_IPC, CONFIG_SOC_NRF53_RTC_PRETICK_IPC_CH_FROM_NET, + BIT(CONFIG_SOC_NRF53_RTC_PRETICK_IPC_CH_FROM_NET)); + + /* Allocate PPI channel for RTC Compare event publishers that starts WDT. */ + nrfx_err_t err = nrfx_gppi_channel_alloc(&ppi_ch); + + if (err != NRFX_SUCCESS) { + return -ENOMEM; + } + + nrfx_gppi_event_endpoint_setup(ppi_ch, evt_cc); + nrfx_gppi_task_endpoint_setup(ppi_ch, task_ipc); + nrfx_gppi_event_endpoint_setup(ppi_ch, evt_ipc); + nrfx_gppi_task_endpoint_setup(ppi_ch, task_wdt); + nrfx_gppi_channels_enable(BIT(ppi_ch)); + + nrf_rtc_event_enable(NRF_RTC1, NRF_RTC_CHANNEL_INT_MASK(RTC1_PRETICK_CC_CHAN)); + nrf_rtc_event_clear(NRF_RTC1, NRF_RTC_CHANNEL_EVENT_ADDR(RTC1_PRETICK_CC_CHAN)); + + return 0; +} +#endif /* CONFIG_SOC_NRF5340_CPUNET */ + +static int rtc_pretick_init(void) +{ +#ifdef CONFIG_SOC_NRF5340_CPUAPP + return rtc_pretick_cpuapp_init(); +#else + return rtc_pretick_cpunet_init(); +#endif +} +#endif /* CONFIG_SOC_NRF53_RTC_PRETICK */ + static int nordicsemi_nrf53_init(void) { @@ -209,13 +554,13 @@ static int nordicsemi_nrf53_init(void) #endif #if defined(CONFIG_SOC_DCDC_NRF53X_APP) - nrf_regulators_dcdcen_set(NRF_REGULATORS, true); + nrf_regulators_vreg_enable_set(NRF_REGULATORS, NRF_REGULATORS_VREG_MAIN, true); #endif #if defined(CONFIG_SOC_DCDC_NRF53X_NET) - nrf_regulators_dcdcen_radio_set(NRF_REGULATORS, true); + nrf_regulators_vreg_enable_set(NRF_REGULATORS, NRF_REGULATORS_VREG_RADIO, true); #endif #if defined(CONFIG_SOC_DCDC_NRF53X_HV) - nrf_regulators_dcdcen_vddh_set(NRF_REGULATORS, true); + nrf_regulators_vreg_enable_set(NRF_REGULATORS, NRF_REGULATORS_VREG_HIGH, true); #endif #if defined(CONFIG_SOC_NRF_GPIO_FORWARDER_FOR_NRF5340) @@ -242,3 +587,7 @@ void arch_busy_wait(uint32_t time_us) } SYS_INIT(nordicsemi_nrf53_init, PRE_KERNEL_1, 0); + +#ifdef CONFIG_SOC_NRF53_RTC_PRETICK +SYS_INIT(rtc_pretick_init, POST_KERNEL, 0); +#endif diff --git a/soc/arm/nordic_nrf/nrf91/Kconfig.series b/soc/arm/nordic_nrf/nrf91/Kconfig.series index fd8f5b04d7a..9e0e477e851 100644 --- a/soc/arm/nordic_nrf/nrf91/Kconfig.series +++ b/soc/arm/nordic_nrf/nrf91/Kconfig.series @@ -19,3 +19,31 @@ config SOC_SERIES_NRF91X select HAS_POWEROFF help Enable support for NRF91 MCU series + +if SOC_SERIES_NRF91X +config NRF_SPU_FLASH_REGION_SIZE + hex + default 0x8000 + help + FLASH region size for the NRF_SPU peripheral + +config NRF_SPU_FLASH_REGION_ALIGNMENT + hex + default 0x8000 + help + FLASH regions must be aligned to this value due to SPU HW + limitations. + +config NRF_SPU_RAM_REGION_SIZE + hex + default 0x2000 + help + RAM region size for the NRF_SPU peripheral + +config NRF_SPU_RAM_REGION_ALIGNMENT + hex + default 0x2000 + help + RAM regions must be aligned to this value due to SPU HW + limitations. +endif diff --git a/subsys/bluetooth/Kconfig b/subsys/bluetooth/Kconfig index e02901cd906..912846e4cda 100644 --- a/subsys/bluetooth/Kconfig +++ b/subsys/bluetooth/Kconfig @@ -175,6 +175,14 @@ config BT_SCA_UPDATE depends on !BT_CTLR || BT_CTLR_SCA_UPDATE_SUPPORT help Enable support for Bluetooth 5.1 Sleep Clock Accuracy Update Procedure + +config BT_TRANSMIT_POWER_CONTROL + bool "LE Power Control" + depends on !BT_CTLR || BT_CTLR_LE_POWER_CONTROL_SUPPORT + help + Enable support for LE Power Control Request feature that is defined in the + Bluetooth Core specification, Version 5.4 | Vol 6, Part B, Section 4.6.31. + endif # BT_CONN rsource "Kconfig.iso" diff --git a/subsys/bluetooth/Kconfig.iso b/subsys/bluetooth/Kconfig.iso index 553ff3a7fb4..304c408f42f 100644 --- a/subsys/bluetooth/Kconfig.iso +++ b/subsys/bluetooth/Kconfig.iso @@ -99,6 +99,10 @@ config BT_ISO_TX_MTU range 1 4095 help Maximum MTU for Isochronous channels TX buffers. + This is the actual data payload. It doesn't include the optional + HCI ISO Data packet fields (e.g. `struct bt_hci_iso_ts_data_hdr`). + Set this value to 247 to fit 247 bytes of data within a single + HCI ISO Data packet with a size of 255, without utilizing timestamps. config BT_ISO_RX_BUF_COUNT int "Number of Isochronous RX buffers" @@ -113,6 +117,8 @@ config BT_ISO_RX_MTU range 23 4095 help Maximum MTU for Isochronous channels RX buffers. + This is the actual data payload. It doesn't include the optional + HCI ISO Data packet fields (e.g. `struct bt_hci_iso_ts_data_hdr`) config BT_ISO_ADVANCED bool "Advanced ISO parameters" diff --git a/subsys/bluetooth/Kconfig.logging b/subsys/bluetooth/Kconfig.logging index 67a18afa919..addf8eb65fe 100644 --- a/subsys/bluetooth/Kconfig.logging +++ b/subsys/bluetooth/Kconfig.logging @@ -398,6 +398,16 @@ config BT_DEBUG_SERVICE This option enables debug support for the Bluetooth Services. +# CONTROLLER (subsys/bluetooth/controller/Kconfig) + +config BT_CTLR_DEBUG_ISOAL + bool "[DEPRECATED] Bluetooth ISO-AL debug" + select DEPRECATED + depends on BT_CTLR_ISO + help + This option enables debug support for the Bluetooth ISO-AL. + + endmenu # [DEPRECATED] Others menu "[DEPRECATED] BR/EDR" @@ -900,6 +910,13 @@ legacy-debug-sym = BT_DEBUG_SERVICE module-str = "Bluetooth Services" source "subsys/bluetooth/common/Kconfig.template.log_config_bt" +# CONTROLLER (subsys/bluetooth/controller/Kconfig) + +module = BT_CTLR_ISOAL +legacy-debug-sym = BT_CTLR_DEBUG_ISOAL +module-str = "Bluetooth Controller ISO-AL" +source "subsys/bluetooth/common/Kconfig.template.log_config_bt" + endmenu # Others menu "BR/EDR" diff --git a/subsys/bluetooth/audio/ascs.c b/subsys/bluetooth/audio/ascs.c index 9b45754fe90..d85a90ec77a 100644 --- a/subsys/bluetooth/audio/ascs.c +++ b/subsys/bluetooth/audio/ascs.c @@ -119,6 +119,8 @@ static void ase_free(struct bt_ascs_ase *ase) bt_conn_unref(ase->conn); ase->conn = NULL; + + (void)k_work_cancel(&ase->state_transition_work); } static void ase_status_changed(struct bt_ascs_ase *ase, uint8_t state) @@ -1094,7 +1096,17 @@ static void disconnected(struct bt_conn *conn, uint8_t reason) } if (ase->ep.status.state != BT_BAP_EP_STATE_IDLE) { - ase_release(ase, reason, BT_BAP_ASCS_RSP_NULL); + /* We must set the state to idle when the ACL is disconnected immediately, + * as when the ACL disconnect callbacks have been called, the application + * should expect there to be only a single reference to the bt_conn pointer + * from the stack. + * We trigger the work handler directly rather than e.g. calling + * ase_set_state_idle to trigger "regular" state change behavior (such) as + * calling stream->stopped when leaving the streaming state. + */ + ase->ep.reason = reason; + ase->state_pending = BT_BAP_EP_STATE_IDLE; + state_transition_work_handler(&ase->state_transition_work); /* At this point, `ase` object have been free'd */ } } diff --git a/subsys/bluetooth/audio/bap_broadcast_sink.c b/subsys/bluetooth/audio/bap_broadcast_sink.c index 9866dfd1c57..4fad67ce539 100644 --- a/subsys/bluetooth/audio/bap_broadcast_sink.c +++ b/subsys/bluetooth/audio/bap_broadcast_sink.c @@ -566,6 +566,16 @@ static void pa_recv(struct bt_le_per_adv_sync *sync, bt_data_parse(buf, pa_decode_base, (void *)sink); } +static void pa_term_cb(struct bt_le_per_adv_sync *sync, + const struct bt_le_per_adv_sync_term_info *info) +{ + struct bt_bap_broadcast_sink *sink = broadcast_sink_get_by_pa(sync); + + if (sink != NULL) { + sink->pa_sync = NULL; + } +} + static void update_recv_state_encryption(const struct bt_bap_broadcast_sink *sink) { struct bt_bap_scan_delegator_mod_src_param mod_src_param = { 0 }; @@ -1076,8 +1086,6 @@ int bt_bap_broadcast_sink_stop(struct bt_bap_broadcast_sink *sink) int bt_bap_broadcast_sink_delete(struct bt_bap_broadcast_sink *sink) { - int err; - CHECKIF(sink == NULL) { LOG_DBG("sink is NULL"); return -EINVAL; @@ -1099,17 +1107,6 @@ int bt_bap_broadcast_sink_delete(struct bt_bap_broadcast_sink *sink) } } - if (sink->pa_sync == NULL) { - LOG_DBG("Broadcast sink is already deleted"); - return -EALREADY; - } - - err = bt_le_per_adv_sync_delete(sink->pa_sync); - if (err != 0) { - LOG_DBG("Failed to delete periodic advertising sync (err %d)", err); - return err; - } - /* Reset the broadcast sink */ broadcast_sink_cleanup(sink); @@ -1121,6 +1118,7 @@ static int broadcast_sink_init(void) static struct bt_le_per_adv_sync_cb cb = { .recv = pa_recv, .biginfo = biginfo_recv, + .term = pa_term_cb, }; bt_le_per_adv_sync_cb_register(&cb); diff --git a/subsys/bluetooth/common/Kconfig b/subsys/bluetooth/common/Kconfig index d72ce90afa6..bf4aee75e48 100644 --- a/subsys/bluetooth/common/Kconfig +++ b/subsys/bluetooth/common/Kconfig @@ -282,6 +282,7 @@ endif # BT_ASSERT config BT_MONITOR bool + select LOG_OUTPUT choice BT_DEBUG_TYPE prompt "Bluetooth debug type" diff --git a/subsys/bluetooth/controller/Kconfig b/subsys/bluetooth/controller/Kconfig index 404d89cbf06..9ba67a05768 100644 --- a/subsys/bluetooth/controller/Kconfig +++ b/subsys/bluetooth/controller/Kconfig @@ -101,6 +101,9 @@ config BT_CTLR_READ_ISO_LINK_QUALITY_SUPPORT BT_CTLR_PERIPHERAL_ISO_SUPPORT bool +config BT_CTLR_LE_POWER_CONTROL_SUPPORT + bool + config BT_CTLR bool "Bluetooth Controller" help @@ -114,7 +117,8 @@ choice BT_LL_CHOICE Select the Bluetooth Link Layer to compile. config BT_LL_SW_SPLIT - bool "Software-based BLE Link Layer" + bool "Software-based BLE Link Layer [EXPERIMENTAL]" + select EXPERIMENTAL select ENTROPY_GENERATOR help Use Zephyr software BLE Link Layer ULL LLL split implementation. @@ -206,6 +210,13 @@ config BT_CTLR_ISO_TX_BUFFER_SIZE Size of the Isochronous Tx buffers and the value returned in HCI LE Read Buffer Size V2 command response. +config BT_CTLR_ISOAL_LOG_DBG_VERBOSE + bool "ISO-AL verbose debug logging" + depends on BT_CTLR_ISOAL_LOG_LEVEL = 4 + default n + help + Use this option to enable ISO-AL verbose debug logging. + config BT_CTLR_ISOAL_SOURCES int "Number of Isochronous Adaptation Layer sources" depends on BT_CTLR_ADV_ISO || BT_CTLR_CONN_ISO @@ -493,6 +504,14 @@ config BT_CTLR_CONN_RSSI help Enable connection RSSI measurement. +config BT_CTLR_LE_POWER_CONTROL + bool "LE Power Control Request Feature" + depends on BT_CTLR_LE_POWER_CONTROL_SUPPORT + default y if BT_TRANSMIT_POWER_CONTROL + help + Enable support for LE Power Control Request feature that is defined in the + Bluetooth Core specification, Version 5.4 | Vol 6, Part B, Section 4.6.31. + endif # BT_CONN config BT_CTLR_FILTER_ACCEPT_LIST @@ -902,6 +921,17 @@ config BT_CTLR_CONN_ISO_STREAMS_MAX_FT help Maximum number of CIS flush timeout events. +config BT_CTLR_CONN_ISO_AVOID_SEGMENTATION + bool "Avoid SDU fragmentation for framed mode" + depends on BT_CTLR_CENTRAL_ISO + help + When creating a CIG, the Max_PDU size is calculated according to BT + Core 5.4 Vol 6, Part G, Section 2.2. However, HAP specifies a need for + avoiding segmentation by forcing the Max_PDU to the appropriate value. + Since there is no way to control the Max_PDU using the non-test + interface, the config provides a way to force the Max_PDU to Max_SDU + + 5 (header + offset). + config BT_CTLR_ISO bool default BT_CTLR_BROADCAST_ISO || BT_CTLR_CONN_ISO diff --git a/subsys/bluetooth/controller/Kconfig.ll_sw_split b/subsys/bluetooth/controller/Kconfig.ll_sw_split index 84db5fddb10..e2975107f86 100644 --- a/subsys/bluetooth/controller/Kconfig.ll_sw_split +++ b/subsys/bluetooth/controller/Kconfig.ll_sw_split @@ -53,7 +53,9 @@ config BT_LLL_VENDOR_NORDIC (BT_OBSERVER && BT_CTLR_ADV_EXT) select BT_TICKER_REMAINDER if BT_CTLR_CENTRAL_ISO select BT_TICKER_REMAINDER_GET if BT_BROADCASTER && BT_CTLR_ADV_EXT - select BT_TICKER_LAZY_GET if BT_CTLR_ADV_PERIODIC + select BT_TICKER_LAZY_GET if BT_CTLR_ADV_PERIODIC || BT_CTLR_CENTRAL_ISO + + select BT_TICKER_PREFER_START_BEFORE_STOP if BT_TICKER_SLOT_AGNOSTIC default y help @@ -384,6 +386,18 @@ config BT_CTLR_ADV_RESERVE_MAX corresponding to the Advertising Data present at the time of the start/enable of Advertising is used. +config BT_CTLR_ADV_ISO_RESERVE_MAX + bool "Use maximum Broadcast ISO event time reservation" + depends on BT_CTLR_ADV_ISO + default y + help + Use maximum Broadcast ISO event time reservation. If disabled, then + time reservation does not include the pre-transmissions of the last + BIS and any Control subevents. This will allow extended or periodic + advertising events to preempt the BIG events but allow higher radio + utilizations by allowing larger BIG events when not overlapping with + extended or periodic advertising. + config BT_CTLR_ADV_AUX_SYNC_OFFSET int "Pre-defined offset between AUX_ADV_IND and AUX_SYNC_IND" depends on BT_CTLR_ADV_PERIODIC @@ -485,7 +499,8 @@ config BT_CTLR_SCAN_ENABLE_STRICT config BT_CTLR_ISOAL_SN_STRICT bool "Enforce Strict Tx ISO Data Sequence Number use" - depends on BT_CTLR_ADV_ISO || BT_CTLR_CONN_ISO + depends on !BT_CTLR_ISOAL_PSN_IGNORE && (BT_CTLR_ADV_ISO || \ + BT_CTLR_CONN_ISO) default y help Enforce strict sequencing of released payloads based on the TX SDU's @@ -503,6 +518,12 @@ config BT_CTLR_ISOAL_SN_STRICT dropped. This will result in better delivery of data to the receiver but at the cost of creating skews in the received stream of SDUs. +config BT_CTLR_ISOAL_PSN_IGNORE + bool "Ignore Tx ISO Data Packet Sequence Number use" + depends on BT_CTLR_ADV_ISO || BT_CTLR_CONN_ISO + help + Ignore the use of Tx ISO Data Packet Sequence Number. + config BT_CTLR_ZLI bool "Use Zero Latency IRQs" depends on ZERO_LATENCY_IRQS @@ -618,6 +639,17 @@ config BT_CTLR_CENTRAL_RESERVE_MAX Note, currently this value is only used to space multiple central connections and not for actual ticker time reservations. +config BT_CTLR_EVENT_OVERHEAD_RESERVE_MAX + bool "Reserve maximum event overhead in time reservations" + default y + help + Use radio event scheduling CPU time overhead in calculations of event + time reservations. + + If this option is disabled, then Peripheral ACL and Peripheral ISO + role will not include CPU time overhead. Other role will continue to + use CPU overheads in their event time reservations. + config BT_CTLR_SLOT_RESERVATION_UPDATE bool "Update event length reservation after PHY or DLE update" depends on (BT_CTLR_DATA_LENGTH || BT_CTLR_PHY) @@ -629,7 +661,7 @@ config BT_CTLR_SLOT_RESERVATION_UPDATE config BT_CTLR_LLL_PRIO int "Lower Link Layer (Radio) IRQ priority" if (BT_CTLR_ULL_LLL_PRIO_SUPPORT && !BT_CTLR_ZLI) range 0 3 if SOC_SERIES_NRF51X - range 0 6 if (SOC_SERIES_NRF52X || SOC_SERIES_NRF53X) + range 0 6 if (SOC_COMPATIBLE_NRF52X || SOC_COMPATIBLE_NRF53X) default 0 help The interrupt priority for event preparation and radio IRQ. @@ -637,7 +669,7 @@ config BT_CTLR_LLL_PRIO config BT_CTLR_ULL_HIGH_PRIO int "Upper Link Layer High IRQ priority" if BT_CTLR_ULL_LLL_PRIO_SUPPORT range BT_CTLR_LLL_PRIO 3 if SOC_SERIES_NRF51X - range BT_CTLR_LLL_PRIO 6 if (SOC_SERIES_NRF52X || SOC_SERIES_NRF53X) + range BT_CTLR_LLL_PRIO 6 if (SOC_COMPATIBLE_NRF52X || SOC_COMPATIBLE_NRF53X) default BT_CTLR_LLL_PRIO if (!BT_CTLR_ULL_LLL_PRIO_SUPPORT || BT_CTLR_ZLI || BT_CTLR_LOW_LAT) default 1 help @@ -647,7 +679,7 @@ config BT_CTLR_ULL_HIGH_PRIO config BT_CTLR_ULL_LOW_PRIO int "Upper Link Layer Low IRQ priority" if BT_CTLR_ULL_LLL_PRIO_SUPPORT range BT_CTLR_ULL_HIGH_PRIO 3 if SOC_SERIES_NRF51X - range BT_CTLR_ULL_HIGH_PRIO 6 if (SOC_SERIES_NRF52X || SOC_SERIES_NRF53X) + range BT_CTLR_ULL_HIGH_PRIO 6 if (SOC_COMPATIBLE_NRF52X || SOC_COMPATIBLE_NRF53X) default BT_CTLR_ULL_HIGH_PRIO help The interrupt priority for Ticker's Job IRQ and Upper Link Layer @@ -691,7 +723,7 @@ config BT_CTLR_RX_PDU_META config BT_CTLR_RADIO_ENABLE_FAST bool "Use tTXEN/RXEN,FAST ramp-up" - depends on SOC_COMPATIBLE_NRF52X || SOC_SERIES_NRF53X + depends on SOC_COMPATIBLE_NRF52X || SOC_COMPATIBLE_NRF53X default y help Enable use of fast radio ramp-up mode. @@ -705,7 +737,7 @@ config BT_CTLR_TIFS_HW config BT_CTLR_SW_SWITCH_SINGLE_TIMER bool "Single TIMER tIFS Trx SW switching" - depends on (!BT_CTLR_TIFS_HW) && (SOC_COMPATIBLE_NRF52X || SOC_SERIES_NRF53X) + depends on (!BT_CTLR_TIFS_HW) && (SOC_COMPATIBLE_NRF52X || SOC_COMPATIBLE_NRF53X) help Implement the tIFS Trx SW switch with the same TIMER instance, as the one used for BLE event timing. Requires @@ -728,18 +760,6 @@ config BT_CTLR_PARAM_CHECK if BT_CONN -config BT_CTLR_FAST_ENC - bool "Fast Encryption Setup" - depends on BT_CTLR_LE_ENC - default y if BT_HCI_RAW - help - Enable connection encryption setup in 4 connection events. - Peripheral will respond to Encryption Request with Encryption Response - in the next connection event, and will transmit Start Encryption - Request PDU in the same connection event, hence completing encryption - setup in 4 connection events. Encrypted data would be transmitted as - fast as in 4th connection event from Encryption Request. - config BT_CTLR_LLCP_CONN int "Number of connections with worst-case overlapping procedures" default BT_MAX_CONN @@ -1036,6 +1056,21 @@ config BT_TICKER_SLOT_AGNOSTIC reservations and collision handling, and operates as a simple multi-instance programmable timer. +config BT_TICKER_PREFER_START_BEFORE_STOP + bool "Ticker prefer start before stop request" + help + Under race conditions wherein for a given ticker node if a number of + start and stop operations are enqueued towards ticker_job by a said + user execution context, then start operations is preferred to be + processed before stop operations. + + Without this option, the default behavior is to defer all start + requests after all stop requests enqueued by all user context having + been processed. The rationale for default behavior being that under + race conditions, start followed by stop requests, or start before stop + requests, the said ticker node is always scheduled and at timeout the + execution context can take decision based on its execution state. + config BT_CTLR_JIT_SCHEDULING bool "Just-in-Time Scheduling" select BT_TICKER_SLOT_AGNOSTIC diff --git a/subsys/bluetooth/controller/coex/Kconfig b/subsys/bluetooth/controller/coex/Kconfig index 31754fbf52c..b7bc7d30681 100644 --- a/subsys/bluetooth/controller/coex/Kconfig +++ b/subsys/bluetooth/controller/coex/Kconfig @@ -5,8 +5,8 @@ menuconfig BT_CTLR_COEX_DRIVERS bool "Bluetooth Co-existence Drivers" - default n - depends on BT_CTLR + default y + depends on BT_CTLR && DT_HAS_GPIO_RADIO_COEX_ENABLED if BT_CTLR_COEX_DRIVERS diff --git a/subsys/bluetooth/controller/coex/coex_ticker.c b/subsys/bluetooth/controller/coex/coex_ticker.c index 229e2f9e125..be349622ecd 100644 --- a/subsys/bluetooth/controller/coex/coex_ticker.c +++ b/subsys/bluetooth/controller/coex/coex_ticker.c @@ -4,12 +4,13 @@ * SPDX-License-Identifier: Apache-2.0 */ +#define DT_DRV_COMPAT gpio_radio_coex + #include #include #include #include -#include #include #include #include @@ -195,18 +196,11 @@ static int coex_ticker_init(const struct device *dev) return 0; } -#define RADIO_NODE DT_NODELABEL(radio) -#define COEX_NODE DT_PROP(RADIO_NODE, coex) - -#if DT_NODE_EXISTS(COEX_NODE) static struct coex_ticker_config config = { - .grant_spec = GPIO_DT_SPEC_GET(COEX_NODE, grant_gpios), - .grant_delay_us = DT_PROP(COEX_NODE, grant_delay_us) + .grant_spec = GPIO_DT_SPEC_INST_GET(0, grant_gpios), + .grant_delay_us = DT_INST_PROP(0, grant_delay_us) }; static struct coex_ticker_data data; -DEVICE_DEFINE(coex_ticker, "COEX_TICKER", &coex_ticker_init, NULL, - &data, &config, - APPLICATION, 90, - NULL); -#endif +DEVICE_DT_INST_DEFINE(0, &coex_ticker_init, NULL, &data, &config, + POST_KERNEL, 90, NULL); diff --git a/subsys/bluetooth/controller/hci/hci.c b/subsys/bluetooth/controller/hci/hci.c index 9ca905c44e0..c3dd070720b 100644 --- a/subsys/bluetooth/controller/hci/hci.c +++ b/subsys/bluetooth/controller/hci/hci.c @@ -4245,8 +4245,8 @@ static void le_cis_established(struct pdu_data *pdu_data, sys_put_le24(cis->sync_delay, sep->cis_sync_delay); sys_put_le24(cig->c_latency, sep->c_latency); sys_put_le24(cig->p_latency, sep->p_latency); - sep->c_phy = lll_cis_c->phy; - sep->p_phy = lll_cis_p->phy; + sep->c_phy = find_lsb_set(lll_cis_c->phy); + sep->p_phy = find_lsb_set(lll_cis_p->phy); sep->nse = lll_cis->nse; sep->c_bn = lll_cis_c->bn; sep->p_bn = lll_cis_p->bn; @@ -5657,8 +5657,6 @@ int hci_iso_handle(struct net_buf *buf, struct net_buf **evt) struct bt_hci_iso_data_hdr *iso_data_hdr; struct isoal_sdu_tx sdu_frag_tx; struct bt_hci_iso_hdr *iso_hdr; - struct ll_iso_datapath *dp_in; - struct ll_iso_stream_hdr *hdr; uint32_t *time_stamp; uint16_t handle; uint8_t pb_flag; @@ -5668,8 +5666,6 @@ int hci_iso_handle(struct net_buf *buf, struct net_buf **evt) iso_data_hdr = NULL; *evt = NULL; - hdr = NULL; - dp_in = NULL; if (buf->len < sizeof(*iso_hdr)) { LOG_ERR("No HCI ISO header"); @@ -5700,17 +5696,21 @@ int hci_iso_handle(struct net_buf *buf, struct net_buf **evt) * -- A captured time stamp of the SDU * -- A time stamp provided by the higher layer * -- A computed time stamp based on a sequence counter provided by the - * higher layer (Not implemented) - * -- Any other method of determining Time_Offset (Not implemented) + * higher layer + * -- Any other method of determining Time_Offset + * (Uses a timestamp computed from the difference in provided + * timestamps, if the timestamp is deemed not based on the + * controller's clock) */ + sdu_frag_tx.cntr_time_stamp = HAL_TICKER_TICKS_TO_US(ticker_ticks_now_get()); if (ts_flag) { - /* Overwrite time stamp with HCI provided time stamp */ + /* Use HCI provided time stamp */ time_stamp = net_buf_pull_mem(buf, sizeof(*time_stamp)); len -= sizeof(*time_stamp); sdu_frag_tx.time_stamp = sys_le32_to_cpu(*time_stamp); } else { - sdu_frag_tx.time_stamp = - HAL_TICKER_TICKS_TO_US(ticker_ticks_now_get()); + /* Use controller's capture time */ + sdu_frag_tx.time_stamp = sdu_frag_tx.cntr_time_stamp; } /* Extract ISO data header if included (PB_Flag 0b00 or 0b10) */ @@ -5743,16 +5743,66 @@ int hci_iso_handle(struct net_buf *buf, struct net_buf **evt) * data path */ } else if (IS_CIS_HANDLE(handle)) { - struct ll_conn_iso_stream *cis = - ll_iso_stream_connected_get(handle); + struct ll_conn_iso_stream *cis; + struct ll_conn_iso_group *cig; + struct ll_iso_stream_hdr *hdr; + struct ll_iso_datapath *dp_in; + + cis = ll_iso_stream_connected_get(handle); if (!cis) { return -EINVAL; } - struct ll_conn_iso_group *cig = cis->group; - uint8_t event_offset; + cig = cis->group; + +#if defined(CONFIG_BT_CTLR_ISOAL_PSN_IGNORE) + uint64_t event_count; + uint64_t pkt_seq_num; + + /* Catch up local pkt_seq_num with internal pkt_seq_num */ + event_count = cis->lll.event_count; + pkt_seq_num = event_count + 1U; + /* If pb_flag is BT_ISO_START (0b00) or BT_ISO_SINGLE (0b10) + * then we simply check that the pb_flag is an even value, and + * then pkt_seq_num is a future sequence number value compare + * to last recorded number in cis->pkt_seq_num. + * + * When (pkt_seq_num - stream->pkt_seq_num) is negative then + * BIT64(39) will be set (2's compliment value). The diff value + * less than or equal to BIT64_MASK(38) means the diff value is + * positive and hence pkt_seq_num is greater than + * stream->pkt_seq_num. This calculation is valid for when value + * rollover too. + */ + if (!(pb_flag & 0x01) && + (((pkt_seq_num - cis->pkt_seq_num) & + BIT64_MASK(39)) <= BIT64_MASK(38))) { + cis->pkt_seq_num = pkt_seq_num; + } else { + pkt_seq_num = cis->pkt_seq_num; + } + + /* Pre-increment, when pg_flag is BT_ISO_SINGLE (0b10) or + * BT_ISO_END (0b11) then we simple check if pb_flag has bit 1 + * is set, for next ISO data packet seq num comparison. + */ + if (pb_flag & 0x10) { + cis->pkt_seq_num++; + } - hdr = &(cis->hdr); + /* Target next ISO event to avoid overlapping with, if any, + * current ISO event + */ + pkt_seq_num++; + sdu_frag_tx.target_event = pkt_seq_num; + sdu_frag_tx.grp_ref_point = + isoal_get_wrapped_time_us(cig->cig_ref_point, + ((pkt_seq_num - event_count) * + cig->iso_interval * + ISO_INT_UNIT_US)); + +#else /* !CONFIG_BT_CTLR_ISOAL_PSN_IGNORE */ + uint8_t event_offset; /* We must ensure sufficient time for ISO-AL to fragment SDU and * deliver PDUs to the TX queue. By checking ull_ref_get, we @@ -5776,11 +5826,15 @@ int hci_iso_handle(struct net_buf *buf, struct net_buf **evt) } sdu_frag_tx.target_event = cis->lll.event_count + event_offset; - sdu_frag_tx.grp_ref_point = isoal_get_wrapped_time_us(cig->cig_ref_point, - (event_offset * cig->iso_interval * - ISO_INT_UNIT_US)); + sdu_frag_tx.grp_ref_point = + isoal_get_wrapped_time_us(cig->cig_ref_point, + (event_offset * + cig->iso_interval * + ISO_INT_UNIT_US)); +#endif /* !CONFIG_BT_CTLR_ISOAL_PSN_IGNORE */ /* Get controller's input data path for CIS */ + hdr = &cis->hdr; dp_in = hdr->datapath_in; if (!dp_in || dp_in->path_id != BT_HCI_DATAPATH_ID_HCI) { LOG_ERR("Input data path not set for HCI"); @@ -5813,8 +5867,6 @@ int hci_iso_handle(struct net_buf *buf, struct net_buf **evt) struct ll_adv_iso_set *adv_iso; struct lll_adv_iso *lll_iso; uint16_t stream_handle; - uint8_t target_event; - uint8_t event_offset; uint16_t slen; /* FIXME: Code only expects header present */ @@ -5840,6 +5892,70 @@ int hci_iso_handle(struct net_buf *buf, struct net_buf **evt) return -EINVAL; } + lll_iso = &adv_iso->lll; + +#if defined(CONFIG_BT_CTLR_ISOAL_PSN_IGNORE) + uint64_t event_count; + uint64_t pkt_seq_num; + + /* Catch up local pkt_seq_num with internal pkt_seq_num */ + event_count = lll_iso->payload_count / lll_iso->bn; + pkt_seq_num = event_count; + /* If pb_flag is BT_ISO_START (0b00) or BT_ISO_SINGLE (0b10) + * then we simply check that the pb_flag is an even value, and + * then pkt_seq_num is a future sequence number value compare + * to last recorded number in cis->pkt_seq_num. + * + * When (pkt_seq_num - stream->pkt_seq_num) is negative then + * BIT64(39) will be set (2's compliment value). The diff value + * less than or equal to BIT64_MASK(38) means the diff value is + * positive and hence pkt_seq_num is greater than + * stream->pkt_seq_num. This calculation is valid for when value + * rollover too. + */ + if (!(pb_flag & 0x01) && + (((pkt_seq_num - stream->pkt_seq_num) & + BIT64_MASK(39)) <= BIT64_MASK(38))) { + stream->pkt_seq_num = pkt_seq_num; + } else { + pkt_seq_num = stream->pkt_seq_num; + } + + /* Pre-increment, when pg_flag is BT_ISO_SINGLE (0b10) or + * BT_ISO_END (0b11) then we simple check if pb_flag has bit 1 + * is set, for next ISO data packet seq num comparison. + */ + if (pb_flag & 0x10) { + stream->pkt_seq_num++; + } + + /* Target next ISO event to avoid overlapping with, if any, + * current ISO event + */ + /* FIXME: Implement ISO Tx ack generation early in done compared + * to currently only in prepare. I.e. to ensure upper + * layer has the number of completed packet before the + * next BIG event, so as to supply new ISO data packets. + * Without which upper layers need extra buffers to + * buffer next ISO data packet. + * + * Enable below increment once early Tx ack is + * implemented. + * + * pkt_seq_num++; + */ + sdu_frag_tx.target_event = pkt_seq_num; + sdu_frag_tx.grp_ref_point = + isoal_get_wrapped_time_us(adv_iso->big_ref_point, + (((pkt_seq_num + 1U) - + event_count) * + lll_iso->iso_interval * + ISO_INT_UNIT_US)); + +#else /* !CONFIG_BT_CTLR_ISOAL_PSN_IGNORE */ + uint8_t target_event; + uint8_t event_offset; + /* Determine the target event and the first event offset after * datapath setup. * event_offset mitigates the possibility of first SDU being @@ -5857,7 +5973,6 @@ int hci_iso_handle(struct net_buf *buf, struct net_buf **evt) * BIG event by incrementing the previous elapsed big_ref_point * by one additional ISO interval. */ - lll_iso = &adv_iso->lll; target_event = lll_iso->payload_count / lll_iso->bn; event_offset = ull_ref_get(&adv_iso->ull) ? 0U : 1U; event_offset += lll_iso->latency_prepare; @@ -5868,6 +5983,7 @@ int hci_iso_handle(struct net_buf *buf, struct net_buf **evt) ((event_offset + 1U) * lll_iso->iso_interval * ISO_INT_UNIT_US)); +#endif /* !CONFIG_BT_CTLR_ISOAL_PSN_IGNORE */ /* Start Fragmentation */ /* FIXME: need to ensure ISO-AL returns proper isoal_status. diff --git a/subsys/bluetooth/controller/hci/nordic/hci_vendor.h b/subsys/bluetooth/controller/hci/nordic/hci_vendor.h index 4d3245728ed..bea7004f9c9 100644 --- a/subsys/bluetooth/controller/hci/nordic/hci_vendor.h +++ b/subsys/bluetooth/controller/hci/nordic/hci_vendor.h @@ -10,7 +10,7 @@ #define BT_HCI_VS_HW_VAR BT_HCI_VS_HW_VAR_NORDIC_NRF51X #elif defined(CONFIG_SOC_COMPATIBLE_NRF52X) #define BT_HCI_VS_HW_VAR BT_HCI_VS_HW_VAR_NORDIC_NRF52X -#elif defined(CONFIG_SOC_SERIES_NRF53X) +#elif defined(CONFIG_SOC_COMPATIBLE_NRF53X) #define BT_HCI_VS_HW_VAR BT_HCI_VS_HW_VAR_NORDIC_NRF53X #endif #else diff --git a/subsys/bluetooth/controller/ll_sw/isoal.c b/subsys/bluetooth/controller/ll_sw/isoal.c index 0a500cfd05e..48ad0835074 100644 --- a/subsys/bluetooth/controller/ll_sw/isoal.c +++ b/subsys/bluetooth/controller/ll_sw/isoal.c @@ -16,6 +16,8 @@ #include #include +#include + #include "util/memq.h" #include "hal/ccm.h" @@ -34,15 +36,15 @@ #include -LOG_MODULE_REGISTER(bt_ctlr_isoal, LOG_LEVEL_INF); +LOG_MODULE_REGISTER(bt_ctlr_isoal, CONFIG_BT_CTLR_ISOAL_LOG_LEVEL); #define ISOAL_LOG_DBG(...) LOG_DBG(__VA_ARGS__) -#if defined(ISOAL_DEBUG_VERBOSE) +#if defined(CONFIG_BT_CTLR_ISOAL_LOG_DBG_VERBOSE) #define ISOAL_LOG_DBGV(...) LOG_DBG(__VA_ARGS__) #else #define ISOAL_LOG_DBGV(...) (void) 0 -#endif /* ISOAL_DEBUG_VERBOSE */ +#endif /* CONFIG_BT_CTLR_ISOAL_LOG_DBG_VERBOSE */ #include "hal/debug.h" @@ -50,6 +52,11 @@ LOG_MODULE_REGISTER(bt_ctlr_isoal, LOG_LEVEL_INF); (s == ISOAL_CONTINUE ? "CONTINUE" : \ (s == ISOAL_ERR_SPOOL ? "ERR SPOOL" : "???"))) +#define STATE_TO_STR(s) (s == BT_ISO_SINGLE ? "SINGLE" : \ + (s == BT_ISO_START ? "START" : \ + (s == BT_ISO_CONT ? "CONT" : \ + (s == BT_ISO_END ? "END" : "???")))) + #if defined(CONFIG_BT_CTLR_ADV_ISO) || defined(CONFIG_BT_CTLR_CONN_ISO) /* Given the minimum payload, this defines the minimum number of bytes that * should be remaining in a TX PDU such that it would make inserting a new @@ -186,7 +193,7 @@ static bool isoal_get_time_diff(uint32_t time_before, uint32_t time_after, uint3 #define SET_RX_SDU_TIMESTAMP(_sink, _timestamp, _value) \ _timestamp = _value; \ - ISOAL_LOG_DBGV("[%p] %s updated (%ld)", _sink, #_timestamp, _value); + ISOAL_LOG_DBGV("[%p] %s updated (%lu)", _sink, #_timestamp, _value); static void isoal_rx_framed_update_sdu_release(struct isoal_sink *sink); @@ -219,8 +226,17 @@ static isoal_status_t isoal_sink_allocate(isoal_sink_handle_t *hdl) */ static void isoal_sink_deallocate(isoal_sink_handle_t hdl) { - isoal_global.sink_allocated[hdl] = ISOAL_ALLOC_STATE_FREE; - (void)memset(&isoal_global.sink_state[hdl], 0, sizeof(struct isoal_sink)); + if (hdl < ARRAY_SIZE(isoal_global.sink_allocated)) { + isoal_global.sink_allocated[hdl] = ISOAL_ALLOC_STATE_FREE; + } else { + LL_ASSERT(0); + } + + if (hdl < ARRAY_SIZE(isoal_global.sink_state)) { + (void)memset(&isoal_global.sink_state[hdl], 0, sizeof(struct isoal_sink)); + } else { + LL_ASSERT(0); + } } /** @@ -358,12 +374,16 @@ isoal_status_t isoal_sink_create( */ void isoal_sink_enable(isoal_sink_handle_t hdl) { - /* Reset bookkeeping state */ - memset(&isoal_global.sink_state[hdl].sdu_production, 0, - sizeof(isoal_global.sink_state[hdl].sdu_production)); + if (hdl < ARRAY_SIZE(isoal_global.sink_state)) { + /* Reset bookkeeping state */ + memset(&isoal_global.sink_state[hdl].sdu_production, 0, + sizeof(isoal_global.sink_state[hdl].sdu_production)); - /* Atomically enable */ - isoal_global.sink_state[hdl].sdu_production.mode = ISOAL_PRODUCTION_MODE_ENABLED; + /* Atomically enable */ + isoal_global.sink_state[hdl].sdu_production.mode = ISOAL_PRODUCTION_MODE_ENABLED; + } else { + LL_ASSERT(0); + } } /** @@ -372,8 +392,12 @@ void isoal_sink_enable(isoal_sink_handle_t hdl) */ void isoal_sink_disable(isoal_sink_handle_t hdl) { - /* Atomically disable */ - isoal_global.sink_state[hdl].sdu_production.mode = ISOAL_PRODUCTION_MODE_DISABLED; + if (hdl < ARRAY_SIZE(isoal_global.sink_state)) { + /* Atomically disable */ + isoal_global.sink_state[hdl].sdu_production.mode = ISOAL_PRODUCTION_MODE_DISABLED; + } else { + LL_ASSERT(0); + } } /** @@ -415,7 +439,7 @@ static isoal_status_t isoal_rx_allocate_sdu(struct isoal_sink *sink, ); if (err == ISOAL_STATUS_OK) { - sp->sdu_allocated = true; + sp->sdu_allocated = 1U; } /* Nothing has been written into buffer yet */ @@ -599,7 +623,7 @@ static isoal_status_t isoal_rx_try_emit_sdu(struct isoal_sink *sink, bool end_of sdu->status = sp->sdu_status; err = isoal_rx_buffered_emit_sdu(sink, end_of_sdu); - sp->sdu_allocated = false; + sp->sdu_allocated = 0U; if (end_of_sdu) { isoal_rx_framed_update_sdu_release(sink); @@ -729,9 +753,9 @@ static isoal_status_t isoal_rx_unframed_consume(struct isoal_sink *sink, next_state = ISOAL_START; /* If status is not ISOAL_PDU_STATUS_VALID, length and LLID cannot be trusted */ - llid = pdu_meta->pdu->ll_id; + llid = pdu->ll_id; pdu_err = (pdu_meta->meta->status != ISOAL_PDU_STATUS_VALID); - length = pdu_meta->pdu->len; + length = pdu_err ? 0U : pdu->len; /* A zero length PDU with LLID 0b01 (PDU_BIS_LLID_START_CONTINUE) would be a padding PDU. * However if there are errors in the PDU, it could be an incorrectly receive non-padding * PDU. Therefore only consider a PDU with errors as padding if received after the end @@ -937,7 +961,7 @@ static isoal_status_t isoal_rx_unframed_consume(struct isoal_sink *sink, sp->prev_pdu_is_end = !pdu_err && llid == PDU_BIS_LLID_COMPLETE_END; sp->prev_pdu_is_padding = !pdu_err && pdu_padding; - sp->initialized = true; + sp->initialized = 1U; return err; } @@ -1153,7 +1177,7 @@ static isoal_status_t isoal_rx_framed_consume(struct isoal_sink *sink, case ISOAL_START: if (!sc) { /* Start segment, included time-offset */ - timeoffset = seg_hdr->timeoffset; + timeoffset = sys_le24_to_cpu(seg_hdr->timeoffset); anchorpoint = meta->timestamp; latency = session->sdu_sync_const; timestamp = isoal_get_wrapped_time_us(anchorpoint, @@ -1210,7 +1234,7 @@ static isoal_status_t isoal_rx_framed_consume(struct isoal_sink *sink, if (!sc) { /* Start segment, included time-offset */ - timeoffset = seg_hdr->timeoffset; + timeoffset = sys_le24_to_cpu(seg_hdr->timeoffset); anchorpoint = meta->timestamp; latency = session->sdu_sync_const; timestamp = isoal_get_wrapped_time_us(anchorpoint, @@ -1275,7 +1299,7 @@ static isoal_status_t isoal_rx_framed_consume(struct isoal_sink *sink, } /* Update next state */ - ISOAL_LOG_DBGV("[%p] Decoding: Next State %s", sink, FSM_TO_STR(next_state)); + ISOAL_LOG_DBGV("[%p] FSM Next State %s", sink, FSM_TO_STR(next_state)); sp->fsm = next_state; /* Find next segment header, set to null if past end of PDU */ @@ -1293,6 +1317,8 @@ static isoal_status_t isoal_rx_framed_consume(struct isoal_sink *sink, } if (pdu_err || seq_err || seg_err) { + bool error_sdu_pending = false; + /* When one or more ISO Data PDUs are not received, the receiving device may * discard all SDUs affected by the missing PDUs. Any partially received SDU * may also be discarded. @@ -1321,14 +1347,28 @@ static isoal_status_t isoal_rx_framed_consume(struct isoal_sink *sink, switch (sp->fsm) { case ISOAL_START: - /* First release lost SDUs and then release a new SDU - * with errors. Since we have an SDU to release - * following any lost SDUs, lost SDUs handling should be - * similar to when a valid timestamp exists. + /* If errors occur while waiting for the start of an SDU + * then an SDU should should only be released if there + * is confirmation that a reception occurred + * unsuccessfully. In the case of STATUS_LOST_DATA which + * could result from a flush, an SDU should not be + * released as the flush does not necessarily mean that + * part of an SDU has been lost. In this case Lost SDU + * release defaults to the lost SDU detection based on + * the SDU interval. If we have a SDU to release + * following any lost SDUs, lost SDU handling should be + * similar to when a valid timestamp for the next SDU + * exists. */ - err |= isoal_rx_framed_release_lost_sdus(sink, pdu_meta, true, timestamp); - sp->sdu_status = next_sdu_status; - err |= isoal_rx_append_to_sdu(sink, pdu_meta, 0, 0, true, false); + error_sdu_pending = seg_err || + (pdu_err && meta->status == ISOAL_SDU_STATUS_ERRORS); + err |= isoal_rx_framed_release_lost_sdus(sink, pdu_meta, + error_sdu_pending, timestamp); + + if (error_sdu_pending) { + sp->sdu_status = next_sdu_status; + err |= isoal_rx_append_to_sdu(sink, pdu_meta, 0U, 0U, true, false); + } break; case ISOAL_CONTINUE: @@ -1339,21 +1379,23 @@ static isoal_status_t isoal_rx_framed_consume(struct isoal_sink *sink, */ sp->sdu_status = next_sdu_status; err |= isoal_rx_append_to_sdu(sink, pdu_meta, 0, 0, true, false); - err |= isoal_rx_framed_release_lost_sdus(sink, pdu_meta, false, timestamp); + err |= isoal_rx_framed_release_lost_sdus(sink, pdu_meta, + error_sdu_pending, timestamp); break; case ISOAL_ERR_SPOOL: - err |= isoal_rx_framed_release_lost_sdus(sink, pdu_meta, false, timestamp); + err |= isoal_rx_framed_release_lost_sdus(sink, pdu_meta, + error_sdu_pending, timestamp); break; } /* Update next state */ - ISOAL_LOG_DBGV("[%p] Error: Next State %s", sink, FSM_TO_STR(next_state)); - sink->sdu_production.fsm = next_state; + ISOAL_LOG_DBGV("[%p] FSM Error Next State %s", sink, FSM_TO_STR(next_state)); + sp->fsm = next_state; } sp->prev_pdu_id = meta->payload_number; - sp->initialized = true; + sp->initialized = 1U; return err; } @@ -1417,7 +1459,13 @@ static void isoal_source_deallocate(isoal_source_handle_t hdl) struct isoal_pdu_production *pp; struct isoal_source *source; - source = &isoal_global.source_state[hdl]; + if (hdl < ARRAY_SIZE(isoal_global.source_state)) { + source = &isoal_global.source_state[hdl]; + } else { + LL_ASSERT(0); + return; + } + pp = &source->pdu_production; if (pp->pdu_available > 0) { @@ -1429,8 +1477,13 @@ static void isoal_source_deallocate(isoal_source_handle_t hdl) } } - isoal_global.source_allocated[hdl] = ISOAL_ALLOC_STATE_FREE; - (void)memset(&isoal_global.source_state[hdl], 0, sizeof(struct isoal_source)); + if (hdl < ARRAY_SIZE(isoal_global.source_allocated)) { + isoal_global.source_allocated[hdl] = ISOAL_ALLOC_STATE_FREE; + } else { + LL_ASSERT(0); + } + + (void)memset(source, 0, sizeof(struct isoal_source)); } /** @@ -1440,8 +1493,8 @@ static void isoal_source_deallocate(isoal_source_handle_t hdl) */ static isoal_status_t isoal_check_source_hdl_valid(isoal_source_handle_t hdl) { - if (hdl < CONFIG_BT_CTLR_ISOAL_SOURCES && - isoal_global.source_allocated[hdl] == ISOAL_ALLOC_STATE_TAKEN) { + if (hdl < ARRAY_SIZE(isoal_global.source_allocated) && + isoal_global.source_allocated[hdl] == ISOAL_ALLOC_STATE_TAKEN) { return ISOAL_STATUS_OK; } @@ -1535,12 +1588,16 @@ isoal_status_t isoal_source_create( */ void isoal_source_enable(isoal_source_handle_t hdl) { - /* Reset bookkeeping state */ - memset(&isoal_global.source_state[hdl].pdu_production, 0, - sizeof(isoal_global.source_state[hdl].pdu_production)); + if (hdl < ARRAY_SIZE(isoal_global.source_state)) { + /* Reset bookkeeping state */ + memset(&isoal_global.source_state[hdl].pdu_production, 0, + sizeof(isoal_global.source_state[hdl].pdu_production)); - /* Atomically enable */ - isoal_global.source_state[hdl].pdu_production.mode = ISOAL_PRODUCTION_MODE_ENABLED; + /* Atomically enable */ + isoal_global.source_state[hdl].pdu_production.mode = ISOAL_PRODUCTION_MODE_ENABLED; + } else { + LL_ASSERT(0); + } } /** @@ -1549,8 +1606,12 @@ void isoal_source_enable(isoal_source_handle_t hdl) */ void isoal_source_disable(isoal_source_handle_t hdl) { - /* Atomically disable */ - isoal_global.source_state[hdl].pdu_production.mode = ISOAL_PRODUCTION_MODE_DISABLED; + if (hdl < ARRAY_SIZE(isoal_global.source_state)) { + /* Atomically disable */ + isoal_global.source_state[hdl].pdu_production.mode = ISOAL_PRODUCTION_MODE_DISABLED; + } else { + LL_ASSERT(0); + } } /** @@ -1566,6 +1627,46 @@ void isoal_source_destroy(isoal_source_handle_t hdl) isoal_source_deallocate(hdl); } +static bool isoal_is_time_stamp_valid(const struct isoal_source *source_ctx, + const uint32_t cntr_time, + const uint32_t time_stamp) +{ + const struct isoal_source_session *session; + uint32_t time_diff; + + session = &source_ctx->session; + + /* This is an arbitrarily defined range. The purpose is to + * decide if the time stamp provided by the host is sensible + * within the controller's clock domain. An SDU interval plus ISO + * interval is expected to provide a good balance between situations + * where either could be significantly larger than the other. + * + * BT Core V5.4 : Vol 6 Low Energy Controller : Part G IS0-AL: + * 3.3 Time Stamp for SDU : + * When an HCI ISO Data packet sent by the Host does not contain + * a Time Stamp or the Time_Stamp value is not based on the + * Controller's clock, the Controller should determine the CIS + * or BIS event to be used to transmit the SDU contained in that + * packet based on the time of arrival of that packet. + */ + const uint32_t sdu_interval_us = session->sdu_interval; + const uint32_t iso_interval_us = session->iso_interval * ISO_INT_UNIT_US; + /* ISO Interval 0x0000_0004 ~ 0x0000_0C80 x 1250 + + * SDU Interval 0x0000_00FF ~ 0x000F_FFFF <= 004D_08FF + */ + const int32_t time_stamp_valid_half_range = sdu_interval_us + iso_interval_us; + const uint32_t time_stamp_valid_min = isoal_get_wrapped_time_us(cntr_time, + (-time_stamp_valid_half_range)); + const uint32_t time_stamp_valid_range = 2 * time_stamp_valid_half_range; + const bool time_stamp_is_valid = isoal_get_time_diff(time_stamp_valid_min, + time_stamp, + &time_diff) && + time_diff <= time_stamp_valid_range; + + return time_stamp_is_valid; +} + /** * Queue the PDU in production in the relevant LL transmit queue. If the * attmept to release the PDU fails, the buffer linked to the PDU will be released @@ -1610,6 +1711,10 @@ static isoal_status_t isoal_tx_pdu_emit(const struct isoal_source *source_ctx, /* Attempt to enqueue the node towards the LL */ status = source_ctx->session.pdu_emit(node_tx, handle); + ISOAL_LOG_DBG("[%p] PDU %llu err=%X len=%u frags=%u released", + source_ctx, payload_number, status, + produced_pdu->contents.pdu->len, sdu_fragments); + if (status != ISOAL_STATUS_OK) { /* If it fails, the node will be released and no further attempt * will be possible @@ -1666,6 +1771,7 @@ static isoal_status_t isoal_tx_allocate_pdu(struct isoal_source *source, /* Nothing has been written into buffer yet */ pp->pdu_written = 0; pp->pdu_available = available_len; + pp->pdu_allocated = 1U; LL_ASSERT(available_len > 0); pp->pdu_cnt++; @@ -1708,6 +1814,7 @@ static isoal_status_t isoal_tx_try_emit_pdu(struct isoal_source *source, pp->pdu_written); pp->payload_number++; pp->sdu_fragments = 0; + pp->pdu_allocated = 0U; } return err; @@ -1751,7 +1858,7 @@ uint16_t isoal_tx_unframed_get_next_payload_number(isoal_source_handle_t source_ time_diff = 0; /* Adjust payload number */ - if (IS_ENABLED(CONFIG_BT_CTLR_ISOAL_SN_STRICT) && session->sn) { + if (IS_ENABLED(CONFIG_BT_CTLR_ISOAL_SN_STRICT) && pp->initialized) { /* Not the first SDU in this session, so reference * information should be valid. At this point, the * current payload number should be at the first PDU of @@ -1806,15 +1913,26 @@ uint16_t isoal_tx_unframed_get_next_payload_number(isoal_source_handle_t source_ return sdus_skipped; } -/* NOTE: Use of target_event and grp_ref_point as input from upper layer. +/** + * @brief Fragment received SDU and produce unframed PDUs + * @details Destination source may have an already partially built PDU * - * For unframed: + * @param[in] source_hdl Destination source handle + * @param[in] tx_sdu SDU with packet boundary information + * + * @return Status + * + * @note + * PSN in SDUs for unframed TX: + * + * @par * Before the modification to use the PSN to decide the position of an SDU in a * stream of SDU, the target event was what was used in deciding the event for * each SDU. This meant that there would possibly have been skews on the - * receiver for each SDU and we had trouble with LL/CIS/PER/BV-39-C which + * receiver for each SDU and there were problems with LL/CIS/PER/BV-39-C which * expects clustering within an event. * + * @par * After the change, the PSN is used to decide the position of an SDU in the * stream anchored at the first PSN received. However for the first SDU * (assume that PSN=0), it will be the target event that decides which event @@ -1823,6 +1941,7 @@ uint16_t isoal_tx_unframed_get_next_payload_number(isoal_source_handle_t source_ * impacts the event chosen for the first SDU and all subsequent SDUs will be * decided relative to the first. * + * @par * The target event and related group reference point is still used to provide * the ISO-AL with a notion of time, for example when storing information * required for the TX Sync command. For example if for PSN 4, target event is @@ -1832,26 +1951,17 @@ uint16_t isoal_tx_unframed_get_next_payload_number(isoal_source_handle_t source_ * for event 7. It is also expected that this value is the latest reference and * is drift compensated. * - * The PSN alone is not sufficient for this because as far as I am aware, host - * and controller have no common reference time for when CIG/BIG event 0 starts. - * Therefore I would expect it is possible to receive PSN 0 in event 2 for - * example. If the target event provided is event 3, then PSN 0 will be - * fragmented into payloads for event 3 and that will serve as the anchor for - * the stream and subsequent SDUs. If for example target event provided was - * event 2 instead, then it could very well be that PSN 0 might not be - * transmitted as is was received midway through event 2 and the payloads - * expired. If this happens then subsequent SDUs might also all be late for - * their transmission slots as they are positioned relative to PSN 0. - */ - -/** - * @brief Fragment received SDU and produce unframed PDUs - * @details Destination source may have an already partially built PDU - * - * @param source_hdl[in] Destination source handle - * @param tx_sdu[in] SDU with packet boundary information - * - * @return Status + * @par + * The PSN alone is not sufficient for this because host and controller have no + * common reference time for when CIG / BIG event 0 starts. Therefore it is + * expected that it is possible to receive PSN 0 in event 2 for example. If the + * target event provided is event 3, then PSN 0 will be fragmented into payloads + * for event 3 and that will serve as the anchor for the stream and subsequent + * SDUs. If for example target event provided was event 2 instead, then it could + * very well be that PSN 0 might not be transmitted as is was received midway + * through event 2 and the payloads expired. If this happens then subsequent + * SDUs might also all be late for their transmission slots as they are + * positioned relative to PSN 0. */ static isoal_status_t isoal_tx_unframed_produce(isoal_source_handle_t source_hdl, const struct isoal_sdu_tx *tx_sdu) @@ -1881,7 +1991,6 @@ static isoal_status_t isoal_tx_unframed_produce(isoal_source_handle_t source_hdl if (tx_sdu->sdu_state == BT_ISO_START || tx_sdu->sdu_state == BT_ISO_SINGLE) { - /* Initialize to info provided in SDU */ uint32_t actual_grp_ref_point; uint64_t next_payload_number; uint16_t sdus_skipped; @@ -2062,6 +2171,8 @@ static isoal_status_t isoal_tx_unframed_produce(isoal_source_handle_t source_hdl zero_length_sdu = false; } + pp->initialized = 1U; + return err; } @@ -2126,6 +2237,9 @@ static isoal_status_t isoal_insert_seg_header_timeoffset(struct isoal_source *so pp->pdu_written += write_size; pp->pdu_available -= write_size; + ISOAL_LOG_DBGV("[%p] Seg header write size=%u sc=%u cmplt=%u TO=%u len=%u", + source, write_size, sc, cmplt, time_offset, seg_hdr.len); + return err; } @@ -2163,6 +2277,209 @@ static isoal_status_t isoal_update_seg_header_cmplt_length(struct isoal_source * pp->last_seg_hdr_loc, (uint8_t *) &seg_hdr, PDU_ISO_SEG_HDR_SIZE); + + ISOAL_LOG_DBGV("[%p] Seg header write size=%u sc=%u cmplt=%u len=%u", + source, PDU_ISO_SEG_HDR_SIZE, seg_hdr.sc, cmplt, seg_hdr.len); +} + +/** + * Find the earliest feasible event for transmission capacity is not wasted and + * return information based on that event. + * + * @param[in] *source_ctx Destination source context + * @param[in] tx_sdu SDU with meta data information + * @param[out] payload_number Updated payload number for the selected event + * @param[out] grp_ref_point Group reference point for the selected event + * @param[out] time_offset Segmentation Time offset to selected event + * @return The number SDUs skipped from the last + */ +static uint16_t isoal_tx_framed_find_correct_tx_event(const struct isoal_source *source_ctx, + const struct isoal_sdu_tx *tx_sdu, + uint64_t *payload_number, + uint32_t *grp_ref_point, + uint32_t *time_offset) +{ + const struct isoal_source_session *session; + const struct isoal_pdu_production *pp; + uint32_t actual_grp_ref_point; + uint64_t next_payload_number; + uint16_t sdus_skipped; + uint64_t actual_event; + bool time_diff_valid; + uint32_t time_diff; + uint32_t time_stamp_selected; + + session = &source_ctx->session; + pp = &source_ctx->pdu_production; + + sdus_skipped = 0U; + time_diff = 0U; + + /* Continue with the current payload unless there is need to change */ + next_payload_number = pp->payload_number; + actual_event = pp->payload_number / session->burst_number; + + ISOAL_LOG_DBGV("[%p] Start PL=%llu Evt=%lu.", source_ctx, next_payload_number, + actual_event); + + /* Get the drift updated group reference point for this event based on + * the actual event being set. This might introduce some errors as the + * group refernce point for future events could drift. However as the + * time offset calculation requires an absolute value, this seems to be + * the best candidate. + */ + if (actual_event != tx_sdu->target_event) { + actual_grp_ref_point = isoal_get_wrapped_time_us(tx_sdu->grp_ref_point, + ((actual_event - tx_sdu->target_event) * session->iso_interval * + ISO_INT_UNIT_US)); + } else { + actual_grp_ref_point = tx_sdu->grp_ref_point; + } + + ISOAL_LOG_DBGV("[%p] Current PL=%llu Evt=%llu Ref=%lu", + source_ctx, next_payload_number, actual_event, actual_grp_ref_point); + + if (tx_sdu->sdu_state == BT_ISO_START || + tx_sdu->sdu_state == BT_ISO_SINGLE) { + /* Start of a new SDU */ + + const bool time_stamp_is_valid = isoal_is_time_stamp_valid(source_ctx, + tx_sdu->cntr_time_stamp, + tx_sdu->time_stamp); + + /* Adjust payload number */ + if (pp->initialized) { + /* Not the first SDU in this session, so reference + * information should be valid. . + */ + + time_diff_valid = isoal_get_time_diff(session->last_input_time_stamp, + tx_sdu->time_stamp, + &time_diff); + + /* Priority is given to the sequence number */ + if (tx_sdu->packet_sn > session->last_input_sn + 1) { + ISOAL_LOG_DBGV("[%p] Using packet_sn for skipped SDUs", source_ctx); + sdus_skipped = (tx_sdu->packet_sn - session->last_input_sn) - 1; + + } else if (tx_sdu->packet_sn == session->last_input_sn && + time_diff_valid && time_diff > session->sdu_interval) { + ISOAL_LOG_DBGV("[%p] Using time_stamp for skipped SDUs", + source_ctx); + /* Round at mid-point */ + sdus_skipped = ((time_diff + (session->sdu_interval / 2)) / + session->sdu_interval) - 1; + } else { + /* SDU is next in sequence */ + } + + if (time_stamp_is_valid) { + /* Use provided time stamp for time offset + * calcutation + */ + time_stamp_selected = tx_sdu->time_stamp; + ISOAL_LOG_DBGV("[%p] Selecting Time Stamp (%lu) from SDU", + source_ctx, time_stamp_selected); + } else if (time_diff_valid) { + /* Project a time stamp based on the last time + * stamp and the difference in input time stamps + */ + time_stamp_selected = isoal_get_wrapped_time_us( + session->tx_time_stamp, + time_diff - session->tx_time_offset); + ISOAL_LOG_DBGV("[%p] Projecting Time Stamp (%lu) from SDU delta", + source_ctx, time_stamp_selected); + } else { + /* Project a time stamp based on the last time + * stamp and the number of skipped SDUs + */ + time_stamp_selected = isoal_get_wrapped_time_us( + session->tx_time_stamp, + ((sdus_skipped + 1) * session->sdu_interval) + - session->tx_time_offset); + ISOAL_LOG_DBGV("[%p] Projecting Time Stamp (%lu) from skipped SDUs", + source_ctx, time_stamp_selected); + } + + } else { + /* First SDU, align with target event */ + if (actual_event < tx_sdu->target_event) { + actual_event = tx_sdu->target_event; + actual_grp_ref_point = tx_sdu->grp_ref_point; + } + + ISOAL_LOG_DBGV("[%p] Use target_event", source_ctx); + + if (time_stamp_is_valid) { + /* Time stamp is within valid range - + * use provided time stamp + */ + time_stamp_selected = tx_sdu->time_stamp; + ISOAL_LOG_DBGV("[%p] Selecting Time Stamp (%lu) from SDU", + source_ctx, time_stamp_selected); + } else { + /* Time stamp is out of range - + * use controller's capture time + */ + time_stamp_selected = tx_sdu->cntr_time_stamp; + ISOAL_LOG_DBGV("[%p] Selecting Time Stamp (%lu) from controller", + source_ctx, time_stamp_selected); + } + } + + /* Selecting the event for transmission is done solely based on + * the time stamp and the ability to calculate a valid time + * offset. + */ + + /* Check if time stamp on packet is later than the group + * reference point and find next feasible event for transmission. + * + * BT Core V5.3 : Vol 6 Low Energy Controller : Part G IS0-AL: + * 3.1 Time_Offset in framed PDUs : + * The Time_Offset shall be a positive value. + */ + while (!isoal_get_time_diff(time_stamp_selected, actual_grp_ref_point, &time_diff) + || time_diff == 0) { + /* Advance target to next event */ + actual_event++; + actual_grp_ref_point = isoal_get_wrapped_time_us(actual_grp_ref_point, + session->iso_interval * ISO_INT_UNIT_US); + } + + ISOAL_LOG_DBGV("[%p] Chosen PL=%llu Evt=%llu Ref=%lu", + source_ctx, (actual_event * session->burst_number), actual_event, + actual_grp_ref_point); + + /* If the event selected is the last event segmented for, then + * it is possible that that some payloads have already been + * released for this event. Segmentation should continue from + * that payload. + */ + next_payload_number = MAX(pp->payload_number, + (actual_event * session->burst_number)); + + ISOAL_LOG_DBGV("[%p] Final Evt=%llu (PL=%llu) Ref.=%lu Next PL=%llu", + source, actual_event, (actual_event * session->burst_number), + actual_grp_ref_point, next_payload_number); + + /* Calculate the time offset */ + time_diff_valid = isoal_get_time_diff(time_stamp_selected, + actual_grp_ref_point, &time_diff); + + LL_ASSERT(time_diff_valid); + LL_ASSERT(time_diff > 0); + /* Time difference must be less than the maximum possible + * time-offset of 24-bits. + */ + LL_ASSERT(time_diff <= 0x00FFFFFF); + } + + *payload_number = next_payload_number; + *grp_ref_point = actual_grp_ref_point; + *time_offset = time_diff; + + return sdus_skipped; } /** @@ -2186,7 +2503,6 @@ static isoal_status_t isoal_tx_framed_produce(isoal_source_handle_t source_hdl, bool zero_length_sdu; isoal_status_t err; bool padding_pdu; - uint8_t ll_id; source = &isoal_global.source_state[source_hdl]; session = &source->session; @@ -2202,15 +2518,58 @@ static isoal_status_t isoal_tx_framed_produce(isoal_source_handle_t source_hdl, zero_length_sdu = (packet_available == 0 && tx_sdu->sdu_state == BT_ISO_SINGLE); + ISOAL_LOG_DBGV("[%p] SDU %u len=%u TS=%lu Ref=%lu Evt=%llu Frag=%u", + source, tx_sdu->packet_sn, tx_sdu->iso_sdu_length, tx_sdu->time_stamp, + tx_sdu->grp_ref_point, tx_sdu->target_event, tx_sdu->sdu_state); + if (tx_sdu->sdu_state == BT_ISO_START || tx_sdu->sdu_state == BT_ISO_SINGLE) { + uint32_t actual_grp_ref_point; + uint64_t next_payload_number; + uint16_t sdus_skipped; + bool time_diff_valid; + uint32_t time_diff; + /* Start of a new SDU */ + time_diff_valid = isoal_get_time_diff(session->last_input_time_stamp, + tx_sdu->time_stamp, + &time_diff); - /* Initialize to info provided in SDU */ - uint32_t actual_grp_ref_point = tx_sdu->grp_ref_point; - uint64_t actual_event = tx_sdu->target_event; - bool time_diff_valid = false; - uint32_t time_diff = 0; + /* Find the best transmission event */ + sdus_skipped = isoal_tx_framed_find_correct_tx_event(source, tx_sdu, + &next_payload_number, + &actual_grp_ref_point, + &time_offset); + + ISOAL_LOG_DBGV("[%p] %u SDUs skipped.", source, sdus_skipped); + ISOAL_LOG_DBGV("[%p] Starting SDU %u PL=(%llu->%llu) Grp Ref=%lu TO=%lu", + source, tx_sdu->packet_sn, pp->payload_number, next_payload_number, + actual_grp_ref_point, time_offset); + + + if (next_payload_number > pp->payload_number) { + /* Moving to a new payload */ + if (pp->pdu_allocated) { + /* Current PDU in production should be released before + * moving to new event. + */ + ISOAL_LOG_DBGV("[%p] Pending PDU released.\n"); + err |= isoal_tx_try_emit_pdu(source, true, PDU_BIS_LLID_FRAMED); + } + + while (err == ISOAL_STATUS_OK && next_payload_number > pp->payload_number && + (pp->payload_number % session->burst_number)) { + /* Release padding PDUs for this event */ + err |= isoal_tx_allocate_pdu(source, tx_sdu); + err |= isoal_tx_try_emit_pdu(source, true, PDU_BIS_LLID_FRAMED); + } + } + + /* Reset PDU production state */ + pp->pdu_state = BT_ISO_START; + + /* Update to new payload number */ + pp->payload_number = next_payload_number; /* Update sequence number for received SDU * @@ -2224,67 +2583,8 @@ static isoal_status_t isoal_tx_framed_produce(isoal_source_handle_t source_hdl, * with the sequence number in the ISOAL once the Datapath is * configured and the link is established. */ - session->sn++; - - /* Reset PDU production state */ - pp->pdu_state = BT_ISO_START; - - /* Update payload counter in case time has passed since the last - * SDU. This should mean that event count * burst number should - * be greater than the current payload number. In the event of - * an SDU interval smaller than the ISO interval, multiple SDUs - * will be sent in the same event. As such the current payload - * number should be retained. Payload numbers are indexed at 0 - * and valid until the PDU is emitted. - */ - pp->payload_number = MAX(pp->payload_number, - (tx_sdu->target_event * session->burst_number)); - - /* Get actual event for this payload number */ - actual_event = pp->payload_number / session->burst_number; - - /* Get group reference point for this PDU based on the actual - * event being set. This might introduce some errors as the - * group refernce point for future events could drift. However - * as the time offset calculation requires an absolute value, - * this seems to be the best candidate. - */ - if (actual_event > tx_sdu->target_event) { - actual_grp_ref_point = isoal_get_wrapped_time_us(tx_sdu->grp_ref_point, - ((actual_event - tx_sdu->target_event) * session->iso_interval * - ISO_INT_UNIT_US)); - } - - /* Check if time stamp on packet is later than the group - * reference point and adjust targets. This could happen if the - * SDU has been time-stampped at the controller when received - * via HCI. - * - * BT Core V5.3 : Vol 6 Low Energy Controller : Part G IS0-AL: - * 3.1 Time_Offset in framed PDUs : - * The Time_Offset shall be a positive value. - */ - if (!isoal_get_time_diff(tx_sdu->time_stamp, actual_grp_ref_point, &time_diff) || - time_diff == 0) { - /* Advance target to next event */ - actual_event++; - actual_grp_ref_point = isoal_get_wrapped_time_us(actual_grp_ref_point, - session->iso_interval * ISO_INT_UNIT_US); - - /* Set payload number */ - pp->payload_number = actual_event * session->burst_number; - } - /* Calculate the time offset */ - time_diff_valid = isoal_get_time_diff(tx_sdu->time_stamp, - actual_grp_ref_point, &time_diff); - LL_ASSERT(time_diff_valid); - LL_ASSERT(time_diff > 0); - /* Time difference must be less than the maximum possible - * time-offset of 24-bits. - */ - LL_ASSERT(time_diff <= 0x00FFFFFF); - time_offset = time_diff; + session->sn += sdus_skipped + 1; /* Store timing info for TX Sync command */ session->tx_time_stamp = actual_grp_ref_point; @@ -2292,6 +2592,25 @@ static isoal_status_t isoal_tx_framed_produce(isoal_source_handle_t source_hdl, /* Reset PDU fragmentation count for this SDU */ pp->pdu_cnt = 0; + + /* Update input packet number and time stamp */ + session->last_input_sn = tx_sdu->packet_sn; + + if (pp->initialized && tx_sdu->time_stamp == tx_sdu->cntr_time_stamp && + (!time_diff_valid || time_diff < session->sdu_interval)) { + /* If the time-stamp is invalid or the difference is + * less than an SDU interval, then set the reference + * time stamp to what should have been received. This is + * done to avoid incorrectly detecting a gap in time + * stamp inputs should there be a burst of SDUs + * clustered together. + */ + session->last_input_time_stamp = isoal_get_wrapped_time_us( + session->last_input_time_stamp, + session->sdu_interval); + } else { + session->last_input_time_stamp = tx_sdu->time_stamp; + } } /* PDUs should be created until the SDU fragment has been fragmented or if @@ -2305,13 +2624,14 @@ static isoal_status_t isoal_tx_framed_produce(isoal_source_handle_t source_hdl, err |= err_alloc; + ISOAL_LOG_DBGV("[%p] State %s", source, STATE_TO_STR(pp->pdu_state)); if (pp->pdu_state == BT_ISO_START) { /* Start of a new SDU. Segmentation header and time-offset * should be inserted. */ err |= isoal_insert_seg_header_timeoffset(source, false, false, - time_offset); + sys_cpu_to_le24(time_offset)); pp->pdu_state = BT_ISO_CONT; } else if (!padding_pdu && pp->pdu_state == BT_ISO_CONT && pp->pdu_written == 0) { /* Continuing an SDU in a new PDU. Segmentation header @@ -2319,7 +2639,7 @@ static isoal_status_t isoal_tx_framed_produce(isoal_source_handle_t source_hdl, */ err |= isoal_insert_seg_header_timeoffset(source, true, false, - 0); + sys_cpu_to_le24(0)); } /* @@ -2366,15 +2686,13 @@ static isoal_status_t isoal_tx_framed_produce(isoal_source_handle_t source_hdl, /* Update complete flag in last segmentation header */ err |= isoal_update_seg_header_cmplt_length(source, end_of_sdu, consume_len); - /* LLID is fixed for framed PDUs */ - ll_id = PDU_BIS_LLID_FRAMED; - /* If there isn't sufficient usable space then release the * PDU when the end of the SDU is reached, instead of waiting * for the next SDU. */ bool release_pdu = end_of_sdu && (pp->pdu_available <= ISOAL_TX_SEGMENT_MIN_SIZE); - const isoal_status_t err_emit = isoal_tx_try_emit_pdu(source, release_pdu, ll_id); + const isoal_status_t err_emit = isoal_tx_try_emit_pdu(source, release_pdu, + PDU_BIS_LLID_FRAMED); err |= err_emit; @@ -2392,6 +2710,8 @@ static isoal_status_t isoal_tx_framed_produce(isoal_source_handle_t source_hdl, zero_length_sdu = false; } + pp->initialized = 1U; + return err; } @@ -2424,9 +2744,9 @@ static isoal_status_t isoal_tx_framed_event_prepare_handle(isoal_source_handle_t first_event_payload = (session->burst_number * event_count); last_event_payload = (session->burst_number * (event_count + 1ULL)) - 1ULL; - if (pp->pdu_available > 0 && - pp->payload_number <= last_event_payload) { + if (pp->pdu_allocated && pp->payload_number <= last_event_payload) { /* Pending PDU that should be released for framed TX */ + ISOAL_LOG_DBGV("[%p] Prepare PDU released.", source); err = isoal_tx_try_emit_pdu(source, true, PDU_BIS_LLID_FRAMED); } @@ -2462,6 +2782,7 @@ static isoal_status_t isoal_tx_framed_event_prepare_handle(isoal_source_handle_t if (release_padding) { while (!err && !err_alloc && (pp->payload_number < last_event_payload + 1ULL)) { + ISOAL_LOG_DBGV("[%p] Prepare padding PDU release.", source); err_alloc = isoal_tx_allocate_pdu(source, NULL); err = isoal_tx_try_emit_pdu(source, true, PDU_BIS_LLID_FRAMED); @@ -2473,6 +2794,7 @@ static isoal_status_t isoal_tx_framed_event_prepare_handle(isoal_source_handle_t if (pp->payload_number < last_event_payload + 1ULL) { pp->payload_number = last_event_payload + 1ULL; + ISOAL_LOG_DBGV("[%p] Prepare PL updated to %lu.", source, pp->payload_number); } return err; @@ -2500,7 +2822,7 @@ isoal_status_t isoal_tx_sdu_fragment(isoal_source_handle_t source_hdl, /* Set source context active to mutually exclude ISO Event prepare * kick. */ - source->context_active = true; + source->context_active = 1U; if (source->pdu_production.mode != ISOAL_PRODUCTION_MODE_DISABLED) { /* BT Core V5.3 : Vol 6 Low Energy Controller : Part G IS0-AL: @@ -2520,11 +2842,12 @@ isoal_status_t isoal_tx_sdu_fragment(isoal_source_handle_t source_hdl, } } - source->context_active = false; + source->context_active = 0U; if (source->timeout_trigger) { - source->timeout_trigger = false; + source->timeout_trigger = 0U; if (session->framed) { + ISOAL_LOG_DBGV("[%p] Prepare cb flag trigger", source); isoal_tx_framed_event_prepare_handle(source_hdl, source->timeout_event_count); } @@ -2599,13 +2922,14 @@ void isoal_tx_event_prepare(isoal_source_handle_t source_hdl, * is active. */ source->timeout_event_count = event_count; - source->timeout_trigger = true; + source->timeout_trigger = 1U; if (source->context_active) { return; } - source->timeout_trigger = false; + source->timeout_trigger = 0U; if (session->framed) { + ISOAL_LOG_DBGV("[%p] Prepare call back", source); isoal_tx_framed_event_prepare_handle(source_hdl, event_count); } } diff --git a/subsys/bluetooth/controller/ll_sw/isoal.h b/subsys/bluetooth/controller/ll_sw/isoal.h index 44f3504f1a6..61d3773c43d 100644 --- a/subsys/bluetooth/controller/ll_sw/isoal.h +++ b/subsys/bluetooth/controller/ll_sw/isoal.h @@ -189,6 +189,8 @@ struct isoal_sdu_tx { uint16_t iso_sdu_length; /** Time stamp from HCI or vendor specific path (us) */ uint32_t time_stamp; + /** Capture time stamp from controller (us) */ + uint32_t cntr_time_stamp; /** CIG Reference of target event (us, compensated for drift) */ uint32_t grp_ref_point; /** Target Event of SDU */ @@ -376,8 +378,6 @@ struct isoal_source_session { uint8_t burst_number; uint8_t pdus_per_sdu; uint8_t max_pdu_size; - int32_t latency_unframed; - int32_t latency_framed; }; struct isoal_pdu_production { @@ -392,6 +392,8 @@ struct isoal_pdu_production { uint64_t seg_hdr_sc:1; uint64_t seg_hdr_length:8; uint64_t sdu_fragments:8; + uint64_t initialized:1; + uint64_t pdu_allocated:1; isoal_pdu_len_t pdu_written; isoal_pdu_len_t pdu_available; /* Location (byte index) of last segmentation header */ diff --git a/subsys/bluetooth/controller/ll_sw/ll_feat.c b/subsys/bluetooth/controller/ll_sw/ll_feat.c index a1aaa99ba6d..d2ba2fefba0 100644 --- a/subsys/bluetooth/controller/ll_sw/ll_feat.c +++ b/subsys/bluetooth/controller/ll_sw/ll_feat.c @@ -70,6 +70,11 @@ uint8_t ll_set_host_feature(uint8_t bit_number, uint8_t bit_value) return BT_HCI_ERR_SUCCESS; } +void ll_feat_reset(void) +{ + host_features = 0U; +} + uint64_t ll_feat_get(void) { return LL_FEAT | (host_features & LL_FEAT_HOST_BIT_MASK); diff --git a/subsys/bluetooth/controller/ll_sw/ll_feat_internal.h b/subsys/bluetooth/controller/ll_sw/ll_feat_internal.h new file mode 100644 index 00000000000..7c23cb1a4e5 --- /dev/null +++ b/subsys/bluetooth/controller/ll_sw/ll_feat_internal.h @@ -0,0 +1,7 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +void ll_feat_reset(void); diff --git a/subsys/bluetooth/controller/ll_sw/lll_conn_iso.h b/subsys/bluetooth/controller/ll_sw/lll_conn_iso.h index 7fdf6bf48d5..8a85ca7c6c9 100644 --- a/subsys/bluetooth/controller/ll_sw/lll_conn_iso.h +++ b/subsys/bluetooth/controller/ll_sw/lll_conn_iso.h @@ -15,9 +15,10 @@ struct lll_conn_iso_stream_rxtx { uint64_t ft:8; /* Flush timeout (FT) */ uint64_t bn:4; /* Burst number (BN) */ uint64_t phy:3; /* PHY */ - uint64_t rfu:1; - uint8_t bn_curr:4; /* Current burst number */ + uint64_t rfu0:1; + uint8_t bn_curr:4; /* Current burst number */ + uint8_t rfu1:4; #if defined(CONFIG_BT_CTLR_LE_ENC) struct ccm ccm; @@ -74,17 +75,26 @@ struct lll_conn_iso_group { struct lll_hdr hdr; uint16_t handle; /* CIG handle (internal) */ - uint8_t num_cis:5; /* Number of CISes in this CIG */ - uint8_t role:1; /* 0: CENTRAL, 1: PERIPHERAL*/ - uint8_t paused:1; /* 1: CIG is paused */ + + /* Resumption information */ + uint16_t resume_cis; /* CIS handle to schedule at resume */ + + /* ISO group information */ + uint32_t num_cis:5; /* Number of CISes in this CIG */ + uint32_t role:1; /* 0: CENTRAL, 1: PERIPHERAL*/ + uint32_t paused:1; /* 1: CIG is paused */ + uint32_t rfu0:1; + + /* ISO interval to calculate timestamp under FT > 1, + * maximum ISO interval of 4 seconds can be represented in 22-bits. + */ + uint32_t iso_interval_us:22; + uint32_t rfu1:2; /* Accumulates LLL prepare callback latencies */ uint16_t latency_prepare; uint16_t latency_event; - /* Resumption information */ - uint16_t resume_cis; /* CIS handle to schedule at resume */ - #if defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) /* Window widening. Relies on vendor specific conversion macros, e.g. * EVENT_US_FRAC_TO_TICKS(). diff --git a/subsys/bluetooth/controller/ll_sw/lll_sync_iso.h b/subsys/bluetooth/controller/ll_sw/lll_sync_iso.h index 2426f51105e..c47aa433c63 100644 --- a/subsys/bluetooth/controller/ll_sw/lll_sync_iso.h +++ b/subsys/bluetooth/controller/ll_sw/lll_sync_iso.h @@ -74,7 +74,6 @@ struct lll_sync_iso { struct node_rx_pdu *payload[BT_CTLR_SYNC_ISO_STREAM_MAX] [PDU_BIG_PAYLOAD_COUNT_MAX]; uint8_t payload_count_max; - uint8_t payload_head; uint8_t payload_tail; uint32_t window_widening_periodic_us; diff --git a/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio.c b/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio.c index 7b72b1a4b93..c6becbe322f 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio.c +++ b/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio.c @@ -311,7 +311,7 @@ void radio_phy_set(uint8_t phy, uint8_t flags) void radio_tx_power_set(int8_t power) { -#if defined(CONFIG_SOC_SERIES_NRF53X) +#if defined(CONFIG_SOC_COMPATIBLE_NRF53X) uint32_t value; /* NOTE: TXPOWER register only accepts upto 0dBm, hence use the HAL @@ -322,12 +322,12 @@ void radio_tx_power_set(int8_t power) NRF_RADIO->TXPOWER = value; hal_radio_tx_power_high_voltage_set(power); -#else /* !CONFIG_SOC_SERIES_NRF53X */ +#else /* !CONFIG_SOC_COMPATIBLE_NRF53X */ /* NOTE: valid value range is passed by Kconfig define. */ NRF_RADIO->TXPOWER = (uint32_t)power; -#endif /* !CONFIG_SOC_SERIES_NRF53X */ +#endif /* !CONFIG_SOC_COMPATIBLE_NRF53X */ } void radio_tx_power_max_set(void) @@ -345,25 +345,25 @@ int8_t radio_tx_power_min_get(void) int8_t radio_tx_power_max_get(void) { -#if defined(CONFIG_SOC_SERIES_NRF53X) +#if defined(CONFIG_SOC_COMPATIBLE_NRF53X) return RADIO_TXPOWER_TXPOWER_Pos3dBm; -#else /* !CONFIG_SOC_SERIES_NRF53X */ +#else /* !CONFIG_SOC_COMPATIBLE_NRF53X */ return (int8_t)hal_radio_tx_power_max_get(); -#endif /* !CONFIG_SOC_SERIES_NRF53X */ +#endif /* !CONFIG_SOC_COMPATIBLE_NRF53X */ } int8_t radio_tx_power_floor(int8_t power) { -#if defined(CONFIG_SOC_SERIES_NRF53X) +#if defined(CONFIG_SOC_COMPATIBLE_NRF53X) /* NOTE: TXPOWER register only accepts upto 0dBm, +3dBm permitted by * use of high voltage being set for radio when TXPOWER register is set. */ if (power >= (int8_t)RADIO_TXPOWER_TXPOWER_Pos3dBm) { return RADIO_TXPOWER_TXPOWER_Pos3dBm; } -#endif /* CONFIG_SOC_SERIES_NRF53X */ +#endif /* CONFIG_SOC_COMPATIBLE_NRF53X */ return (int8_t)hal_radio_tx_power_floor(power); } @@ -382,7 +382,7 @@ void radio_whiten_iv_set(uint32_t iv) RADIO_PCNF1_WHITEEN_Msk; } -void radio_aa_set(uint8_t *aa) +void radio_aa_set(const uint8_t *aa) { NRF_RADIO->TXADDRESS = (((0UL) << RADIO_TXADDRESS_TXADDRESS_Pos) & @@ -413,7 +413,7 @@ void radio_pkt_configure(uint8_t bits_len, uint8_t max_len, uint8_t flags) bits_s1 = RADIO_PKT_CONF_LENGTH_8BIT - bits_len; #elif defined(CONFIG_SOC_COMPATIBLE_NRF52X) || \ - defined(CONFIG_SOC_SERIES_NRF53X) + defined(CONFIG_SOC_COMPATIBLE_NRF53X) extra = 0U; phy = RADIO_PKT_CONF_PHY_GET(flags); @@ -510,7 +510,7 @@ uint32_t radio_rx_chain_delay_get(uint8_t phy, uint8_t flags) void radio_rx_enable(void) { #if !defined(CONFIG_BT_CTLR_TIFS_HW) -#if defined(CONFIG_SOC_SERIES_NRF53X) +#if defined(CONFIG_SOC_COMPATIBLE_NRF53X) /* NOTE: Timer clear DPPI configuration is needed only for nRF53 * because of calls to radio_disable() and * radio_switch_complete_and_disable() inside a radio event call @@ -523,7 +523,7 @@ void radio_rx_enable(void) * radio event but when the radio event is done. */ hal_sw_switch_timer_clear_ppi_config(); -#endif /* CONFIG_SOC_SERIES_NRF53X */ +#endif /* CONFIG_SOC_COMPATIBLE_NRF53X */ #endif /* !CONFIG_BT_CTLR_TIFS_HW */ nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_RXEN); @@ -532,7 +532,7 @@ void radio_rx_enable(void) void radio_tx_enable(void) { #if !defined(CONFIG_BT_CTLR_TIFS_HW) -#if defined(CONFIG_SOC_SERIES_NRF53X) +#if defined(CONFIG_SOC_COMPATIBLE_NRF53X) /* NOTE: Timer clear DPPI configuration is needed only for nRF53 * because of calls to radio_disable() and * radio_switch_complete_and_disable() inside a radio event call @@ -545,7 +545,7 @@ void radio_tx_enable(void) * radio event but when the radio event is done. */ hal_sw_switch_timer_clear_ppi_config(); -#endif /* CONFIG_SOC_SERIES_NRF53X */ +#endif /* CONFIG_SOC_COMPATIBLE_NRF53X */ #endif /* !CONFIG_BT_CTLR_TIFS_HW */ nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_TXEN); @@ -568,18 +568,18 @@ void radio_status_reset(void) * register value, PPI task will be triggered. Hence, other * EVENT_* registers are not reset to save code and CPU time. */ - NRF_RADIO->EVENTS_READY = 0; - NRF_RADIO->EVENTS_END = 0; + nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_READY); + nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_END); #if defined(CONFIG_BT_CTLR_DF_SUPPORT) && !defined(CONFIG_ZTEST) /* Clear it only for SoCs supporting DF extension */ - NRF_RADIO->EVENTS_PHYEND = 0; - NRF_RADIO->EVENTS_CTEPRESENT = 0; - NRF_RADIO->EVENTS_BCMATCH = 0; + nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_PHYEND); + nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_CTEPRESENT); + nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_BCMATCH); #endif /* CONFIG_BT_CTLR_DF_SUPPORT && !CONFIG_ZTEST */ - NRF_RADIO->EVENTS_DISABLED = 0; + nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_DISABLED); #if defined(CONFIG_BT_CTLR_PHY_CODED) #if defined(CONFIG_HAS_HW_NRF_RADIO_BLE_CODED) - NRF_RADIO->EVENTS_RATEBOOST = 0; + nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_RATEBOOST); #endif /* CONFIG_HAS_HW_NRF_RADIO_BLE_CODED */ #endif /* CONFIG_BT_CTLR_PHY_CODED */ } @@ -718,7 +718,7 @@ void sw_switch(uint8_t dir_curr, uint8_t dir_next, uint8_t phy_curr, uint8_t fla #endif /* CONFIG_BT_CTLR_DF_PHYEND_OFFSET_COMPENSATION_ENABLE */ uint32_t delay; - hal_radio_sw_switch_setup(cc, ppi, sw_tifs_toggle); + hal_radio_sw_switch_setup(sw_tifs_toggle); /* NOTE: As constants are passed to dir_curr and dir_next, the * compiler should optimize out the redundant code path @@ -736,7 +736,7 @@ void sw_switch(uint8_t dir_curr, uint8_t dir_next, uint8_t phy_curr, uint8_t fla hal_radio_tx_chain_delay_ns_get(phy_curr, flags_curr)); - hal_radio_b2b_txen_on_sw_switch(ppi); + hal_radio_b2b_txen_on_sw_switch(cc, ppi); } else { /* If RX PHY is LE Coded, calculate for S8 coding. * Assumption being, S8 has higher delay. @@ -746,7 +746,7 @@ void sw_switch(uint8_t dir_curr, uint8_t dir_next, uint8_t phy_curr, uint8_t fla flags_next) + hal_radio_rx_chain_delay_ns_get(phy_curr, 1)); - hal_radio_txen_on_sw_switch(ppi); + hal_radio_txen_on_sw_switch(cc, ppi); } #if defined(CONFIG_BT_CTLR_DF_PHYEND_OFFSET_COMPENSATION_ENABLE) @@ -839,7 +839,7 @@ void sw_switch(uint8_t dir_curr, uint8_t dir_next, uint8_t phy_curr, uint8_t fla flags_curr)) + (EVENT_CLOCK_JITTER_US << 1); - hal_radio_rxen_on_sw_switch(ppi); + hal_radio_rxen_on_sw_switch(cc, ppi); } else { delay = HAL_RADIO_NS2US_CEIL( hal_radio_rx_ready_delay_ns_get(phy_next, @@ -848,7 +848,7 @@ void sw_switch(uint8_t dir_curr, uint8_t dir_next, uint8_t phy_curr, uint8_t fla flags_curr)) + (EVENT_CLOCK_JITTER_US << 1); - hal_radio_b2b_rxen_on_sw_switch(ppi); + hal_radio_b2b_rxen_on_sw_switch(cc, ppi); } @@ -891,13 +891,13 @@ void sw_switch(uint8_t dir_curr, uint8_t dir_next, uint8_t phy_curr, uint8_t fla * time-stamp. */ hal_radio_end_time_capture_ppi_config(); -#if !defined(CONFIG_SOC_SERIES_NRF53X) +#if !defined(CONFIG_SOC_COMPATIBLE_NRF53X) /* The function is not called for nRF5340 single timer configuration because * HAL_SW_SWITCH_TIMER_CLEAR_PPI is equal to HAL_RADIO_END_TIME_CAPTURE_PPI, * so channel is already enabled. */ hal_radio_nrf_ppi_channels_enable(BIT(HAL_RADIO_END_TIME_CAPTURE_PPI)); -#endif /* !CONFIG_SOC_SERIES_NRF53X */ +#endif /* !CONFIG_SOC_COMPATIBLE_NRF53X */ #endif /* CONFIG_BT_CTLR_SW_SWITCH_SINGLE_TIMER */ sw_tifs_toggle += 1U; @@ -982,6 +982,26 @@ void radio_switch_complete_and_b2b_rx(uint8_t phy_curr, uint8_t flags_curr, #endif /* !CONFIG_BT_CTLR_TIFS_HW */ } +void radio_switch_complete_and_b2b_tx_disable(void) +{ +#if defined(CONFIG_BT_CTLR_TIFS_HW) + NRF_RADIO->SHORTS = (RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_END_DISABLE_Msk); +#else /* CONFIG_BT_CTLR_TIFS_HW */ + NRF_RADIO->SHORTS = (RADIO_SHORTS_READY_START_Msk | NRF_RADIO_SHORTS_PDU_END_DISABLE); + hal_radio_sw_switch_b2b_tx_disable(sw_tifs_toggle); +#endif /* !CONFIG_BT_CTLR_TIFS_HW */ +} + +void radio_switch_complete_and_b2b_rx_disable(void) +{ +#if defined(CONFIG_BT_CTLR_TIFS_HW) + NRF_RADIO->SHORTS = (RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_END_DISABLE_Msk); +#else /* CONFIG_BT_CTLR_TIFS_HW */ + NRF_RADIO->SHORTS = (RADIO_SHORTS_READY_START_Msk | NRF_RADIO_SHORTS_PDU_END_DISABLE); + hal_radio_sw_switch_b2b_rx_disable(sw_tifs_toggle); +#endif /* !CONFIG_BT_CTLR_TIFS_HW */ +} + void radio_switch_complete_and_disable(void) { #if defined(CONFIG_BT_CTLR_TIFS_HW) @@ -1019,7 +1039,7 @@ uint32_t radio_rssi_get(void) void radio_rssi_status_reset(void) { - NRF_RADIO->EVENTS_RSSIEND = 0; + nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_RSSIEND); } uint32_t radio_rssi_is_ready(void) @@ -1051,7 +1071,7 @@ void radio_filter_disable(void) void radio_filter_status_reset(void) { - NRF_RADIO->EVENTS_DEVMATCH = 0; + nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_DEVMATCH); } uint32_t radio_filter_has_match(void) @@ -1072,7 +1092,7 @@ void radio_bc_configure(uint32_t n) void radio_bc_status_reset(void) { - NRF_RADIO->EVENTS_BCMATCH = 0; + nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_BCMATCH); } uint32_t radio_bc_has_match(void) @@ -1084,6 +1104,8 @@ void radio_tmr_status_reset(void) { nrf_rtc_event_disable(NRF_RTC0, RTC_EVTENCLR_COMPARE2_Msk); + hal_trigger_crypt_ppi_disable(); + hal_radio_nrf_ppi_channels_disable( BIT(HAL_RADIO_ENABLE_TX_ON_TICK_PPI) | BIT(HAL_RADIO_ENABLE_RX_ON_TICK_PPI) | @@ -1116,6 +1138,8 @@ void radio_tmr_tx_status_reset(void) { nrf_rtc_event_disable(NRF_RTC0, RTC_EVTENCLR_COMPARE2_Msk); + hal_trigger_crypt_ppi_disable(); + hal_radio_nrf_ppi_channels_disable( #if (HAL_RADIO_ENABLE_TX_ON_TICK_PPI != HAL_RADIO_ENABLE_RX_ON_TICK_PPI) && \ !defined(DPPI_PRESENT) @@ -1152,6 +1176,8 @@ void radio_tmr_rx_status_reset(void) { nrf_rtc_event_disable(NRF_RTC0, RTC_EVTENCLR_COMPARE2_Msk); + hal_trigger_crypt_ppi_disable(); + hal_radio_nrf_ppi_channels_disable( #if (HAL_RADIO_ENABLE_TX_ON_TICK_PPI != HAL_RADIO_ENABLE_RX_ON_TICK_PPI) && \ !defined(DPPI_PRESENT) @@ -1186,38 +1212,38 @@ void radio_tmr_rx_status_reset(void) void radio_tmr_tx_enable(void) { -#if defined(CONFIG_SOC_SERIES_NRF53X) -#else /* !CONFIG_SOC_SERIES_NRF53X */ +#if defined(CONFIG_SOC_COMPATIBLE_NRF53X) +#else /* !CONFIG_SOC_COMPATIBLE_NRF53X */ #if (HAL_RADIO_ENABLE_TX_ON_TICK_PPI == HAL_RADIO_ENABLE_RX_ON_TICK_PPI) hal_radio_enable_on_tick_ppi_config_and_enable(1U); #endif /* HAL_RADIO_ENABLE_TX_ON_TICK_PPI == HAL_RADIO_ENABLE_RX_ON_TICK_PPI */ -#endif /* !CONFIG_SOC_SERIES_NRF53X */ +#endif /* !CONFIG_SOC_COMPATIBLE_NRF53X */ } void radio_tmr_rx_enable(void) { -#if defined(CONFIG_SOC_SERIES_NRF53X) -#else /* !CONFIG_SOC_SERIES_NRF53X */ +#if defined(CONFIG_SOC_COMPATIBLE_NRF53X) +#else /* !CONFIG_SOC_COMPATIBLE_NRF53X */ #if (HAL_RADIO_ENABLE_TX_ON_TICK_PPI == HAL_RADIO_ENABLE_RX_ON_TICK_PPI) hal_radio_enable_on_tick_ppi_config_and_enable(0U); #endif /* HAL_RADIO_ENABLE_TX_ON_TICK_PPI == HAL_RADIO_ENABLE_RX_ON_TICK_PPI */ -#endif /* !CONFIG_SOC_SERIES_NRF53X */ +#endif /* !CONFIG_SOC_COMPATIBLE_NRF53X */ } void radio_tmr_tx_disable(void) { -#if defined(CONFIG_SOC_SERIES_NRF53X) +#if defined(CONFIG_SOC_COMPATIBLE_NRF53X) nrf_radio_subscribe_clear(NRF_RADIO, NRF_RADIO_TASK_TXEN); -#else /* !CONFIG_SOC_SERIES_NRF53X */ -#endif /* !CONFIG_SOC_SERIES_NRF53X */ +#else /* !CONFIG_SOC_COMPATIBLE_NRF53X */ +#endif /* !CONFIG_SOC_COMPATIBLE_NRF53X */ } void radio_tmr_rx_disable(void) { -#if defined(CONFIG_SOC_SERIES_NRF53X) +#if defined(CONFIG_SOC_COMPATIBLE_NRF53X) nrf_radio_subscribe_clear(NRF_RADIO, NRF_RADIO_TASK_RXEN); -#else /* !CONFIG_SOC_SERIES_NRF53X */ -#endif /* !CONFIG_SOC_SERIES_NRF53X */ +#else /* !CONFIG_SOC_COMPATIBLE_NRF53X */ +#endif /* !CONFIG_SOC_COMPATIBLE_NRF53X */ } void radio_tmr_tifs_set(uint32_t tifs) @@ -1304,7 +1330,7 @@ uint32_t radio_tmr_start_tick(uint8_t trx, uint32_t tick) #if defined(CONFIG_BT_CTLR_SW_SWITCH_SINGLE_TIMER) last_pdu_end_us = 0U; #endif /* CONFIG_BT_CTLR_SW_SWITCH_SINGLE_TIMER */ -#if defined(CONFIG_SOC_SERIES_NRF53X) +#if defined(CONFIG_SOC_COMPATIBLE_NRF53X) /* NOTE: Timer clear DPPI configuration is needed only for nRF53 * because of calls to radio_disable() and * radio_switch_complete_and_disable() inside a radio event call @@ -1317,7 +1343,7 @@ uint32_t radio_tmr_start_tick(uint8_t trx, uint32_t tick) * radio event but when the radio event is done. */ hal_sw_switch_timer_clear_ppi_config(); -#endif /* CONFIG_SOC_SERIES_NRF53X */ +#endif /* CONFIG_SOC_COMPATIBLE_NRF53X */ #endif /* !CONFIG_BT_CTLR_TIFS_HW */ return remainder_us; @@ -1331,7 +1357,7 @@ uint32_t radio_tmr_start_us(uint8_t trx, uint32_t start_us) #if defined(CONFIG_BT_CTLR_SW_SWITCH_SINGLE_TIMER) last_pdu_end_us = 0U; #endif /* CONFIG_BT_CTLR_SW_SWITCH_SINGLE_TIMER */ -#if defined(CONFIG_SOC_SERIES_NRF53X) +#if defined(CONFIG_SOC_COMPATIBLE_NRF53X) /* NOTE: Timer clear DPPI configuration is needed only for nRF53 * because of calls to radio_disable() and * radio_switch_complete_and_disable() inside a radio event call @@ -1344,7 +1370,7 @@ uint32_t radio_tmr_start_us(uint8_t trx, uint32_t start_us) * radio event but when the radio event is done. */ hal_sw_switch_timer_clear_ppi_config(); -#endif /* CONFIG_SOC_SERIES_NRF53X */ +#endif /* CONFIG_SOC_COMPATIBLE_NRF53X */ #endif /* !CONFIG_BT_CTLR_TIFS_HW */ /* start_us could be the current count in the timer */ @@ -1356,7 +1382,7 @@ uint32_t radio_tmr_start_us(uint8_t trx, uint32_t start_us) start_us = (now_us << 1) - start_us; /* Setup compare event with min. 1 us offset */ - EVENT_TIMER->EVENTS_COMPARE[0] = 0U; + nrf_timer_event_clear(EVENT_TIMER, NRF_TIMER_EVENT_COMPARE0); nrf_timer_cc_set(EVENT_TIMER, 0, start_us + 1U); /* Capture the current time */ @@ -1464,12 +1490,12 @@ void radio_tmr_end_capture(void) * hal_sw_switch_timer_clear_ppi_config() and sw_switch(). There is no need to * configure the channel again in this function. */ -#if !defined(CONFIG_SOC_SERIES_NRF53X) || \ - (defined(CONFIG_SOC_SERIES_NRF53X) && !defined(CONFIG_BT_CTLR_SW_SWITCH_SINGLE_TIMER)) +#if !defined(CONFIG_SOC_COMPATIBLE_NRF53X) || \ + (defined(CONFIG_SOC_COMPATIBLE_NRF53X) && !defined(CONFIG_BT_CTLR_SW_SWITCH_SINGLE_TIMER)) hal_radio_end_time_capture_ppi_config(); hal_radio_nrf_ppi_channels_enable(BIT(HAL_RADIO_END_TIME_CAPTURE_PPI)); -#endif /* !CONFIG_SOC_SERIES_NRF53X || - * (CONFIG_SOC_SERIES_NRF53X && !CONFIG_BT_CTLR_SW_SWITCH_SINGLE_TIMER) +#endif /* !CONFIG_SOC_COMPATIBLE_NRF53X || + * (CONFIG_SOC_COMPATIBLE_NRF53X && !CONFIG_BT_CTLR_SW_SWITCH_SINGLE_TIMER) */ } @@ -1749,9 +1775,9 @@ static void *radio_ccm_ext_rx_pkt_set(struct ccm *cnf, uint8_t phy, uint8_t pdu_ NRF_CCM->OUTPTR = (uint32_t)pkt; NRF_CCM->SCRATCHPTR = (uint32_t)_ccm_scratch; NRF_CCM->SHORTS = 0; - NRF_CCM->EVENTS_ENDKSGEN = 0; - NRF_CCM->EVENTS_ENDCRYPT = 0; - NRF_CCM->EVENTS_ERROR = 0; + nrf_ccm_event_clear(NRF_CCM, NRF_CCM_EVENT_ENDKSGEN); + nrf_ccm_event_clear(NRF_CCM, NRF_CCM_EVENT_ENDCRYPT); + nrf_ccm_event_clear(NRF_CCM, NRF_CCM_EVENT_ERROR); nrf_ccm_task_trigger(NRF_CCM, NRF_CCM_TASK_KSGEN); @@ -1776,7 +1802,7 @@ static void *radio_ccm_ext_tx_pkt_set(struct ccm *cnf, uint8_t pdu_type, void *p NRF_CCM->ENABLE = CCM_ENABLE_ENABLE_Enabled; mode = (CCM_MODE_MODE_Encryption << CCM_MODE_MODE_Pos) & CCM_MODE_MODE_Msk; -#if defined(CONFIG_SOC_COMPATIBLE_NRF52X) || defined(CONFIG_SOC_SERIES_NRF53X) +#if defined(CONFIG_SOC_COMPATIBLE_NRF52X) || defined(CONFIG_SOC_COMPATIBLE_NRF53X) /* Enable CCM support for 8-bit length field PDUs. */ mode |= (CCM_MODE_LENGTH_Extended << CCM_MODE_LENGTH_Pos) & CCM_MODE_LENGTH_Msk; @@ -1820,9 +1846,9 @@ static void *radio_ccm_ext_tx_pkt_set(struct ccm *cnf, uint8_t pdu_type, void *p NRF_CCM->OUTPTR = (uint32_t)_pkt_scratch; NRF_CCM->SCRATCHPTR = (uint32_t)_ccm_scratch; NRF_CCM->SHORTS = CCM_SHORTS_ENDKSGEN_CRYPT_Msk; - NRF_CCM->EVENTS_ENDKSGEN = 0; - NRF_CCM->EVENTS_ENDCRYPT = 0; - NRF_CCM->EVENTS_ERROR = 0; + nrf_ccm_event_clear(NRF_CCM, NRF_CCM_EVENT_ENDKSGEN); + nrf_ccm_event_clear(NRF_CCM, NRF_CCM_EVENT_ENDCRYPT); + nrf_ccm_event_clear(NRF_CCM, NRF_CCM_EVENT_ERROR); nrf_ccm_task_trigger(NRF_CCM, NRF_CCM_TASK_KSGEN); @@ -1899,9 +1925,9 @@ void radio_ar_configure(uint32_t nirk, void *irk, uint8_t flags) NRF_AAR->ADDRPTR = addrptr; NRF_AAR->SCRATCHPTR = (uint32_t)&_aar_scratch[0]; - NRF_AAR->EVENTS_END = 0; - NRF_AAR->EVENTS_RESOLVED = 0; - NRF_AAR->EVENTS_NOTRESOLVED = 0; + nrf_aar_event_clear(NRF_AAR, NRF_AAR_EVENT_END); + nrf_aar_event_clear(NRF_AAR, NRF_AAR_EVENT_RESOLVED); + nrf_aar_event_clear(NRF_AAR, NRF_AAR_EVENT_NOTRESOLVED); radio_bc_configure(bcc); radio_bc_status_reset(); @@ -1957,9 +1983,9 @@ uint8_t radio_ar_resolve(const uint8_t *addr) NRF_AAR->ADDRPTR = (uint32_t)addr - 3; - NRF_AAR->EVENTS_END = 0; - NRF_AAR->EVENTS_RESOLVED = 0; - NRF_AAR->EVENTS_NOTRESOLVED = 0; + nrf_aar_event_clear(NRF_AAR, NRF_AAR_EVENT_END); + nrf_aar_event_clear(NRF_AAR, NRF_AAR_EVENT_RESOLVED); + nrf_aar_event_clear(NRF_AAR, NRF_AAR_EVENT_NOTRESOLVED); NVIC_ClearPendingIRQ(nrfx_get_irq_number(NRF_AAR)); diff --git a/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio.h b/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio.h index 600d7e95481..fc355af1edc 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio.h +++ b/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio.h @@ -70,7 +70,7 @@ int8_t radio_tx_power_max_get(void); int8_t radio_tx_power_floor(int8_t power); void radio_freq_chan_set(uint32_t chan); void radio_whiten_iv_set(uint32_t iv); -void radio_aa_set(uint8_t *aa); +void radio_aa_set(const uint8_t *aa); void radio_pkt_configure(uint8_t bits_len, uint8_t max_len, uint8_t flags); void radio_pkt_rx_set(void *rx_packet); void radio_pkt_tx_set(void *tx_packet); @@ -106,6 +106,8 @@ void radio_switch_complete_and_b2b_tx(uint8_t phy_curr, uint8_t flags_curr, uint8_t phy_next, uint8_t flags_next); void radio_switch_complete_and_b2b_rx(uint8_t phy_curr, uint8_t flags_curr, uint8_t phy_next, uint8_t flags_next); +void radio_switch_complete_and_b2b_tx_disable(void); +void radio_switch_complete_and_b2b_rx_disable(void); void radio_switch_complete_and_disable(void); uint8_t radio_phy_flags_rx_get(void); diff --git a/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio_nrf5.h b/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio_nrf5.h index a03e55f519d..842b4189184 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio_nrf5.h +++ b/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio_nrf5.h @@ -15,8 +15,10 @@ #define HAL_RADIO_NS2US_ROUND(ns) ((ns + 500)/1000) /* SoC specific defines */ -#if defined(CONFIG_SOC_SERIES_BSIM_NRFXX) -#include "radio_sim_nrfxx.h" +#if defined(CONFIG_BOARD_NRF52_BSIM) +#include "radio_sim_nrf52.h" +#elif defined(CONFIG_BOARD_NRF5340BSIM_NRF5340_CPUNET) +#include "radio_sim_nrf5340.h" #elif defined(CONFIG_SOC_SERIES_NRF51X) #include "radio_nrf51.h" #elif defined(CONFIG_SOC_NRF52805) diff --git a/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio_nrf5340.h b/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio_nrf5340.h index e0f43014508..68267490129 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio_nrf5340.h +++ b/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio_nrf5340.h @@ -5,6 +5,20 @@ * SPDX-License-Identifier: Apache-2.0 */ +/* Override EVENT_TIMER_ID from 4 to 0, as nRF5340 does not have 4 timer + * instances. + */ +#if defined(CONFIG_BT_CTLR_SW_SWITCH_SINGLE_TIMER) +#undef EVENT_TIMER_ID +#define EVENT_TIMER_ID 0 + +#undef EVENT_TIMER +#define EVENT_TIMER _CONCAT(NRF_TIMER, EVENT_TIMER_ID) + +#undef SW_SWITCH_TIMER +#define SW_SWITCH_TIMER EVENT_TIMER +#endif /* CONFIG_BT_CTLR_SW_SWITCH_SINGLE_TIMER */ + /* NRF Radio HW timing constants * - provided in US and NS (for higher granularity) * - based on empirical measurements and sniffer logs diff --git a/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio_nrf5_dppi.h b/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio_nrf5_dppi.h index 085b881d47e..1fdf7d90d30 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio_nrf5_dppi.h +++ b/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio_nrf5_dppi.h @@ -127,6 +127,14 @@ static inline void hal_trigger_crypt_ppi_config(void) nrf_ccm_subscribe_set(NRF_CCM, NRF_CCM_TASK_CRYPT, HAL_RADIO_RECV_TIMEOUT_CANCEL_PPI); } +/******************************************************************************* + * Disable trigger encryption task + */ +static inline void hal_trigger_crypt_ppi_disable(void) +{ + nrf_ccm_subscribe_clear(NRF_CCM, NRF_CCM_TASK_CRYPT); +} + #if defined(CONFIG_BT_CTLR_DF_CONN_CTE_RX) /******************************************************************************* * Trigger encryption task on Bit counter match: @@ -317,9 +325,23 @@ static inline void hal_sw_switch_timer_clear_ppi_config(void) #define SW_SWITCH_TIMER_S2_EVTS_COMP(index) \ (SW_SWITCH_TIMER_EVTS_COMP_S2_BASE + (index)) +/* + * Convert a dppi channel group number into the *enable* task enumerate value + * the nrfx hal accepts + */ +#define HAL_SW_DPPI_TASK_EN_FROM_IDX(index) \ + (NRF_DPPI_TASK_CHG0_EN + ((index) * (NRF_DPPI_TASK_CHG1_EN - NRF_DPPI_TASK_CHG0_EN))) + +/* + * Convert a dppi channel group number into the *disable* task enumerate value + * the nrfx hal accepts + */ +#define HAL_SW_DPPI_TASK_DIS_FROM_IDX(index) \ + (NRF_DPPI_TASK_CHG0_DIS + ((index) * (NRF_DPPI_TASK_CHG1_EN - NRF_DPPI_TASK_CHG0_EN))) + /* Wire a SW SWITCH TIMER EVENTS_COMPARE[] event * to a PPI GROUP TASK DISABLE task (PPI group with index ). - * 2 adjacent PPIs (8 & 9) and 2 adjacent PPI groups are used for this wiring; + * 2 adjacent PPIs (14 & 15) and 2 adjacent PPI groups are used for this wiring; * must be 0 or 1. must be a valid TIMER CC register offset. */ #define HAL_SW_SWITCH_GROUP_TASK_DISABLE_PPI(index) \ @@ -332,14 +354,10 @@ static inline void hal_sw_switch_timer_clear_ppi_config(void) & TIMER_PUBLISH_COMPARE_CHIDX_Msk) | \ ((TIMER_PUBLISH_COMPARE_EN_Enabled << TIMER_PUBLISH_COMPARE_EN_Pos) \ & TIMER_PUBLISH_COMPARE_EN_Msk)) -#define HAL_SW_SWITCH_GROUP_TASK_DISABLE_PPI_REGISTER_TASK(index) \ - NRF_DPPIC->SUBSCRIBE_CHG[SW_SWITCH_TIMER_TASK_GROUP(index)].DIS -#define HAL_SW_SWITCH_GROUP_TASK_DISABLE_PPI_TASK(chan) \ - (((chan << DPPIC_SUBSCRIBE_CHG_DIS_CHIDX_Pos) \ - & DPPIC_SUBSCRIBE_CHG_DIS_CHIDX_Msk) | \ - ((DPPIC_SUBSCRIBE_CHG_DIS_EN_Enabled << \ - DPPIC_SUBSCRIBE_CHG_DIS_EN_Pos) \ - & DPPIC_SUBSCRIBE_CHG_DIS_EN_Msk)) +#define HAL_SW_SWITCH_GROUP_TASK_DISABLE_PPI_REGISTER_TASK(index, channel) \ + nrf_dppi_subscribe_set(NRF_DPPIC, \ + HAL_SW_DPPI_TASK_DIS_FROM_IDX(SW_SWITCH_TIMER_TASK_GROUP(index)), \ + channel); /* Enable the SW Switch PPI Group on RADIO END Event. * @@ -353,15 +371,6 @@ static inline void hal_sw_switch_timer_clear_ppi_config(void) & RADIO_PUBLISH_END_CHIDX_Msk) | \ ((RADIO_PUBLISH_END_EN_Enabled << RADIO_PUBLISH_END_EN_Pos) \ & RADIO_PUBLISH_END_EN_Msk)) -#define HAL_SW_SWITCH_GROUP_TASK_ENABLE_PPI_REGISTER_TASK(index) \ - (NRF_DPPIC->SUBSCRIBE_CHG[SW_SWITCH_TIMER_TASK_GROUP(index)].EN) -#define HAL_SW_SWITCH_GROUP_TASK_ENABLE_PPI_TASK \ - (((HAL_SW_SWITCH_GROUP_TASK_ENABLE_PPI << \ - DPPIC_SUBSCRIBE_CHG_EN_CHIDX_Pos) \ - & DPPIC_SUBSCRIBE_CHG_EN_CHIDX_Msk) | \ - ((DPPIC_SUBSCRIBE_CHG_EN_EN_Enabled << \ - DPPIC_SUBSCRIBE_CHG_EN_EN_Pos) \ - & DPPIC_SUBSCRIBE_CHG_EN_EN_Msk)) /* Enable Radio on SW Switch timer event. * Wire a SW SWITCH TIMER EVENTS_COMPARE[] event @@ -387,21 +396,6 @@ static inline void hal_sw_switch_timer_clear_ppi_config(void) & TIMER_PUBLISH_COMPARE_CHIDX_Msk) | \ ((TIMER_PUBLISH_COMPARE_EN_Enabled << TIMER_PUBLISH_COMPARE_EN_Pos) \ & TIMER_PUBLISH_COMPARE_EN_Msk)) -#define HAL_SW_SWITCH_RADIO_ENABLE_PPI_REGISTER_TASK_TX \ - NRF_RADIO->SUBSCRIBE_TXEN -#define HAL_SW_SWITCH_RADIO_ENABLE_PPI_REGISTER_TASK_RX \ - NRF_RADIO->SUBSCRIBE_RXEN -#define HAL_SW_SWITCH_RADIO_ENABLE_PPI_TASK_TX_SET(chan) \ - (((chan << RADIO_SUBSCRIBE_TXEN_CHIDX_Pos) \ - & RADIO_SUBSCRIBE_TXEN_CHIDX_Msk) | \ - ((RADIO_SUBSCRIBE_TXEN_EN_Enabled << \ - RADIO_SUBSCRIBE_TXEN_EN_Pos) \ - & RADIO_SUBSCRIBE_TXEN_EN_Msk)) -#define HAL_SW_SWITCH_RADIO_ENABLE_PPI_TASK_RX_SET(chan) \ - (((chan << RADIO_SUBSCRIBE_RXEN_CHIDX_Pos) \ - & RADIO_SUBSCRIBE_RXEN_CHIDX_Msk) | \ - ((RADIO_SUBSCRIBE_RXEN_EN_Enabled << RADIO_SUBSCRIBE_RXEN_EN_Pos) \ - & RADIO_SUBSCRIBE_RXEN_EN_Msk)) /* Cancel the SW switch timer running considering S8 timing: * wire the RADIO EVENTS_RATEBOOST event to SW_SWITCH_TIMER TASKS_CAPTURE task. @@ -435,10 +429,10 @@ static inline void hal_sw_switch_timer_clear_ppi_config(void) (SW_SWITCH_TIMER_EVTS_COMP_PHYEND_DELAY_COMPENSATION_BASE + (index)) #define HAL_SW_SWITCH_RADIO_ENABLE_PHYEND_DELAY_COMPENSATION_PPI(index) \ HAL_SW_SWITCH_GROUP_TASK_DISABLE_PPI(index) + /* Cancel the SW switch timer running considering PHYEND delay compensation timing: * wire the RADIO EVENTS_CTEPRESENT event to SW_SWITCH_TIMER TASKS_CAPTURE task. */ -#define HAL_SW_SWITCH_TIMER_PHYEND_DELAY_COMPENSATION_DISABLE_PPI 16 #define HAL_SW_SWITCH_TIMER_PHYEND_DELAY_COMPENSATION_DISABLE_PPI_REGISTER_EVT \ NRF_RADIO->PUBLISH_CTEPRESENT @@ -461,10 +455,7 @@ static inline void hal_sw_switch_timer_clear_ppi_config(void) #endif /* CONFIG_BT_CTLR_DF_PHYEND_OFFSET_COMPENSATION_ENABLE */ -static inline void hal_radio_sw_switch_setup( - uint8_t compare_reg, - uint8_t radio_enable_ppi, - uint8_t ppi_group_index) +static inline void hal_radio_sw_switch_setup(uint8_t ppi_group_index) { /* Set up software switch mechanism for next Radio switch. */ @@ -472,62 +463,80 @@ static inline void hal_radio_sw_switch_setup( * over PPI[] */ HAL_SW_SWITCH_GROUP_TASK_ENABLE_PPI_REGISTER_EVT = - HAL_SW_SWITCH_GROUP_TASK_ENABLE_PPI_EVT; - HAL_SW_SWITCH_GROUP_TASK_ENABLE_PPI_REGISTER_TASK(ppi_group_index) = - HAL_SW_SWITCH_GROUP_TASK_ENABLE_PPI_TASK; + HAL_SW_SWITCH_GROUP_TASK_ENABLE_PPI_EVT; + nrf_dppi_subscribe_set(NRF_DPPIC, + HAL_SW_DPPI_TASK_EN_FROM_IDX(SW_SWITCH_TIMER_TASK_GROUP(ppi_group_index)), + HAL_SW_SWITCH_GROUP_TASK_ENABLE_PPI); /* We need to un-subscribe the other group from the PPI channel. */ - HAL_SW_SWITCH_GROUP_TASK_ENABLE_PPI_REGISTER_TASK( - (ppi_group_index + 1) & 0x01) = 0; + uint8_t other_grp = (ppi_group_index + 1) & 0x01; + + nrf_dppi_subscribe_clear(NRF_DPPIC, + HAL_SW_DPPI_TASK_EN_FROM_IDX(SW_SWITCH_TIMER_TASK_GROUP(other_grp))); +} +static inline void hal_radio_txen_on_sw_switch(uint8_t compare_reg_index, uint8_t radio_enable_ppi) +{ /* Wire SW Switch timer event to the * PPI[] for enabling Radio. Do * not wire the task; it is done by the caller of * the function depending on the desired direction * (TX/RX). */ - HAL_SW_SWITCH_RADIO_ENABLE_PPI_REGISTER_EVT(compare_reg) = + HAL_SW_SWITCH_RADIO_ENABLE_PPI_REGISTER_EVT(compare_reg_index) = HAL_SW_SWITCH_RADIO_ENABLE_PPI_EVT(radio_enable_ppi); -} -static inline void hal_radio_txen_on_sw_switch(uint8_t ppi) -{ - nrf_radio_subscribe_set(NRF_RADIO, NRF_RADIO_TASK_TXEN, ppi); + nrf_radio_subscribe_set(NRF_RADIO, NRF_RADIO_TASK_TXEN, radio_enable_ppi); } -static inline void hal_radio_b2b_txen_on_sw_switch(uint8_t ppi) +static inline void hal_radio_b2b_txen_on_sw_switch(uint8_t compare_reg_index, + uint8_t radio_enable_ppi) { - /* NOTE: Calling radio_tmr_start/radio_tmr_start_us/radio_tmr_start_now - * after the radio_switch_complete_and_b2b_tx() call would have - * changed the PPI channel to HAL_RADIO_ENABLE_ON_TICK_PPI as we - * cannot double buffer the subscribe buffer. Hence, lets have - * both DPPI channel enabled (other one was enabled by the DPPI - * group when the Radio End occurred) so that when both timer - * trigger one of the DPPI is correct in the radio tx - * subscription. + /* Wire SW Switch timer event to the + * PPI[] for enabling Radio. Do + * not wire the task; it is done by the caller of + * the function depending on the desired direction + * (TX/RX). */ - nrf_radio_subscribe_set(NRF_RADIO, NRF_RADIO_TASK_TXEN, ppi); - nrf_dppi_channels_enable(NRF_DPPIC, BIT(ppi)); + HAL_SW_SWITCH_RADIO_ENABLE_PPI_REGISTER_EVT(compare_reg_index) = + HAL_SW_SWITCH_RADIO_ENABLE_PPI_EVT(radio_enable_ppi); + + uint8_t prev_ppi_idx = (compare_reg_index + 0x01) & 0x01; + + radio_enable_ppi = HAL_SW_SWITCH_RADIO_ENABLE_PPI(prev_ppi_idx); + nrf_radio_subscribe_set(NRF_RADIO, NRF_RADIO_TASK_TXEN, radio_enable_ppi); } -static inline void hal_radio_rxen_on_sw_switch(uint8_t ppi) +static inline void hal_radio_rxen_on_sw_switch(uint8_t compare_reg_index, uint8_t radio_enable_ppi) { - nrf_radio_subscribe_set(NRF_RADIO, NRF_RADIO_TASK_RXEN, ppi); + /* Wire SW Switch timer event to the + * PPI[] for enabling Radio. Do + * not wire the task; it is done by the caller of + * the function depending on the desired direction + * (TX/RX). + */ + HAL_SW_SWITCH_RADIO_ENABLE_PPI_REGISTER_EVT(compare_reg_index) = + HAL_SW_SWITCH_RADIO_ENABLE_PPI_EVT(radio_enable_ppi); + + nrf_radio_subscribe_set(NRF_RADIO, NRF_RADIO_TASK_RXEN, radio_enable_ppi); } -static inline void hal_radio_b2b_rxen_on_sw_switch(uint8_t ppi) +static inline void hal_radio_b2b_rxen_on_sw_switch(uint8_t compare_reg_index, + uint8_t radio_enable_ppi) { - /* NOTE: Calling radio_tmr_start/radio_tmr_start_us/radio_tmr_start_now - * after the radio_switch_complete_and_b2b_rx() call would have - * changed the PPI channel to HAL_RADIO_ENABLE_ON_TICK_PPI as we - * cannot double buffer the subscribe buffer. Hence, lets have - * both DPPI channel enabled (other one was enabled by the DPPI - * group when the Radio End occurred) so that when both timer - * trigger one of the DPPI is correct in the radio rx - * subscription. + /* Wire SW Switch timer event to the + * PPI[] for enabling Radio. Do + * not wire the task; it is done by the caller of + * the function depending on the desired direction + * (TX/RX). */ - nrf_radio_subscribe_set(NRF_RADIO, NRF_RADIO_TASK_RXEN, ppi); - nrf_dppi_channels_enable(NRF_DPPIC, BIT(ppi)); + HAL_SW_SWITCH_RADIO_ENABLE_PPI_REGISTER_EVT(compare_reg_index) = + HAL_SW_SWITCH_RADIO_ENABLE_PPI_EVT(radio_enable_ppi); + + uint8_t prev_ppi_idx = (compare_reg_index + 0x01) & 0x01; + + radio_enable_ppi = HAL_SW_SWITCH_RADIO_ENABLE_PPI(prev_ppi_idx); + nrf_radio_subscribe_set(NRF_RADIO, NRF_RADIO_TASK_RXEN, radio_enable_ppi); } static inline void hal_radio_sw_switch_disable(void) @@ -537,8 +546,30 @@ static inline void hal_radio_sw_switch_disable(void) * So we simply cancel the task subscription. */ nrf_timer_subscribe_clear(SW_SWITCH_TIMER, NRF_TIMER_TASK_CLEAR); - HAL_SW_SWITCH_GROUP_TASK_ENABLE_PPI_REGISTER_TASK(0) = 0; - HAL_SW_SWITCH_GROUP_TASK_ENABLE_PPI_REGISTER_TASK(1) = 0; + nrf_dppi_subscribe_clear(NRF_DPPIC, + HAL_SW_DPPI_TASK_EN_FROM_IDX(SW_SWITCH_TIMER_TASK_GROUP(0))); + nrf_dppi_subscribe_clear(NRF_DPPIC, + HAL_SW_DPPI_TASK_EN_FROM_IDX(SW_SWITCH_TIMER_TASK_GROUP(1))); +} + +static inline void hal_radio_sw_switch_b2b_tx_disable(uint8_t compare_reg_index) +{ + hal_radio_sw_switch_disable(); + + uint8_t prev_ppi_idx = (compare_reg_index + 0x01) & 0x01; + uint8_t radio_enable_ppi = HAL_SW_SWITCH_RADIO_ENABLE_PPI(prev_ppi_idx); + + nrf_radio_subscribe_set(NRF_RADIO, NRF_RADIO_TASK_TXEN, radio_enable_ppi); +} + +static inline void hal_radio_sw_switch_b2b_rx_disable(uint8_t compare_reg_index) +{ + hal_radio_sw_switch_disable(); + + uint8_t prev_ppi_idx = (compare_reg_index + 0x01) & 0x01; + uint8_t radio_enable_ppi = HAL_SW_SWITCH_RADIO_ENABLE_PPI(prev_ppi_idx); + + nrf_radio_subscribe_set(NRF_RADIO, NRF_RADIO_TASK_RXEN, radio_enable_ppi); } static inline void hal_radio_sw_switch_cleanup(void) @@ -551,6 +582,7 @@ static inline void hal_radio_sw_switch_cleanup(void) nrf_dppi_group_disable(NRF_DPPIC, SW_SWITCH_TIMER_TASK_GROUP(1)); } +#if defined(CONFIG_BT_CTLR_PHY_CODED) static inline void hal_radio_sw_switch_coded_tx_config_set(uint8_t ppi_en, uint8_t ppi_dis, uint8_t cc_s2, uint8_t group_index) { @@ -566,8 +598,8 @@ static inline void hal_radio_sw_switch_coded_tx_config_set(uint8_t ppi_en, HAL_SW_SWITCH_GROUP_TASK_DISABLE_PPI_REGISTER_EVT(cc_s2) = HAL_SW_SWITCH_GROUP_TASK_DISABLE_PPI_EVT(ppi_dis); - HAL_SW_SWITCH_GROUP_TASK_DISABLE_PPI_REGISTER_TASK(group_index) = - HAL_SW_SWITCH_GROUP_TASK_DISABLE_PPI_TASK(ppi_dis); + HAL_SW_SWITCH_GROUP_TASK_DISABLE_PPI_REGISTER_TASK(group_index, + ppi_dis); /* Capture CC to cancel the timer that has assumed * S8 reception, if packet will be received in S2. @@ -582,6 +614,7 @@ static inline void hal_radio_sw_switch_coded_tx_config_set(uint8_t ppi_en, BIT(HAL_SW_SWITCH_TIMER_S8_DISABLE_PPI)); } +#if defined(CONFIG_BT_CTLR_PHY_CODED) && defined(CONFIG_HAS_HW_NRF_RADIO_BLE_CODED) static inline void hal_radio_sw_switch_coded_config_clear(uint8_t ppi_en, uint8_t ppi_dis, uint8_t cc_reg, uint8_t group_index) { @@ -594,6 +627,7 @@ static inline void hal_radio_sw_switch_coded_config_clear(uint8_t ppi_en, HAL_SW_SWITCH_RADIO_ENABLE_PPI_REGISTER_EVT( SW_SWITCH_TIMER_S2_EVTS_COMP(group_index)) = 0; } +#endif /* CONFIG_BT_CTLR_PHY_CODED && CONFIG_HAS_HW_NRF_RADIO_BLE_CODED */ static inline void hal_radio_sw_switch_disable_group_clear(uint8_t ppi_dis, uint8_t cc_reg, uint8_t group_index) @@ -606,10 +640,9 @@ static inline void hal_radio_sw_switch_disable_group_clear(uint8_t ppi_dis, uint HAL_SW_SWITCH_GROUP_TASK_DISABLE_PPI_EVT( ppi_dis); HAL_SW_SWITCH_GROUP_TASK_DISABLE_PPI_REGISTER_TASK( - group_index) = - HAL_SW_SWITCH_GROUP_TASK_DISABLE_PPI_TASK( - ppi_dis); + group_index, ppi_dis); } +#endif /* defined(CONFIG_BT_CTLR_PHY_CODED) */ static inline void hal_radio_sw_switch_ppi_group_setup(void) { @@ -620,21 +653,33 @@ static inline void hal_radio_sw_switch_ppi_group_setup(void) * registers are written, therefore, we clear the task registers * here. */ - NRF_DPPIC->SUBSCRIBE_CHG[SW_SWITCH_TIMER_TASK_GROUP(0)].EN = 0; - NRF_DPPIC->SUBSCRIBE_CHG[SW_SWITCH_TIMER_TASK_GROUP(0)].DIS = 0; - NRF_DPPIC->SUBSCRIBE_CHG[SW_SWITCH_TIMER_TASK_GROUP(1)].EN = 0; - NRF_DPPIC->SUBSCRIBE_CHG[SW_SWITCH_TIMER_TASK_GROUP(1)].DIS = 0; - - NRF_DPPIC->TASKS_CHG[SW_SWITCH_TIMER_TASK_GROUP(0)].DIS = 1; - NRF_DPPIC->TASKS_CHG[SW_SWITCH_TIMER_TASK_GROUP(1)].DIS = 1; + nrf_dppi_subscribe_clear(NRF_DPPIC, + HAL_SW_DPPI_TASK_EN_FROM_IDX(SW_SWITCH_TIMER_TASK_GROUP(0))); + nrf_dppi_subscribe_clear(NRF_DPPIC, + HAL_SW_DPPI_TASK_DIS_FROM_IDX(SW_SWITCH_TIMER_TASK_GROUP(0))); + nrf_dppi_subscribe_clear(NRF_DPPIC, + HAL_SW_DPPI_TASK_EN_FROM_IDX(SW_SWITCH_TIMER_TASK_GROUP(1))); + nrf_dppi_subscribe_clear(NRF_DPPIC, + HAL_SW_DPPI_TASK_DIS_FROM_IDX(SW_SWITCH_TIMER_TASK_GROUP(1))); + + nrf_dppi_task_trigger(NRF_DPPIC, + HAL_SW_DPPI_TASK_DIS_FROM_IDX(SW_SWITCH_TIMER_TASK_GROUP(0))); + nrf_dppi_task_trigger(NRF_DPPIC, + HAL_SW_DPPI_TASK_DIS_FROM_IDX(SW_SWITCH_TIMER_TASK_GROUP(1))); /* Include the appropriate PPI channels in the two PPI Groups. */ - NRF_DPPIC->CHG[SW_SWITCH_TIMER_TASK_GROUP(0)] = + nrf_dppi_group_clear(NRF_DPPIC, + SW_SWITCH_TIMER_TASK_GROUP(0)); + nrf_dppi_channels_include_in_group(NRF_DPPIC, BIT(HAL_SW_SWITCH_GROUP_TASK_DISABLE_PPI(0)) | - BIT(HAL_SW_SWITCH_RADIO_ENABLE_PPI(0)); - NRF_DPPIC->CHG[SW_SWITCH_TIMER_TASK_GROUP(1)] = + BIT(HAL_SW_SWITCH_RADIO_ENABLE_PPI(0)), + SW_SWITCH_TIMER_TASK_GROUP(0)); + nrf_dppi_group_clear(NRF_DPPIC, + SW_SWITCH_TIMER_TASK_GROUP(1)); + nrf_dppi_channels_include_in_group(NRF_DPPIC, BIT(HAL_SW_SWITCH_GROUP_TASK_DISABLE_PPI(1)) | - BIT(HAL_SW_SWITCH_RADIO_ENABLE_PPI(1)); + BIT(HAL_SW_SWITCH_RADIO_ENABLE_PPI(1)), + SW_SWITCH_TIMER_TASK_GROUP(1)); /* Sanity build-time check that RADIO Enable and Group Disable * tasks are going to be subscribed on the same PPIs. @@ -659,8 +704,7 @@ static inline void hal_radio_group_task_disable_ppi_setup(void) SW_SWITCH_TIMER_EVTS_COMP(0)) = HAL_SW_SWITCH_GROUP_TASK_DISABLE_PPI_EVT( HAL_SW_SWITCH_GROUP_TASK_DISABLE_PPI(0)); - HAL_SW_SWITCH_GROUP_TASK_DISABLE_PPI_REGISTER_TASK(0) = - HAL_SW_SWITCH_GROUP_TASK_DISABLE_PPI_TASK( + HAL_SW_SWITCH_GROUP_TASK_DISABLE_PPI_REGISTER_TASK(0, HAL_SW_SWITCH_GROUP_TASK_DISABLE_PPI(0)); /* Wire SW SWITCH TIMER event to @@ -670,9 +714,8 @@ static inline void hal_radio_group_task_disable_ppi_setup(void) SW_SWITCH_TIMER_EVTS_COMP(1)) = HAL_SW_SWITCH_GROUP_TASK_DISABLE_PPI_EVT( HAL_SW_SWITCH_GROUP_TASK_DISABLE_PPI(1)); - HAL_SW_SWITCH_GROUP_TASK_DISABLE_PPI_REGISTER_TASK(1) = - HAL_SW_SWITCH_GROUP_TASK_DISABLE_PPI_TASK( - HAL_SW_SWITCH_GROUP_TASK_DISABLE_PPI(1)); + HAL_SW_SWITCH_GROUP_TASK_DISABLE_PPI_REGISTER_TASK(1, + HAL_SW_SWITCH_GROUP_TASK_DISABLE_PPI(1)); } #if defined(CONFIG_BT_CTLR_DF_PHYEND_OFFSET_COMPENSATION_ENABLE) diff --git a/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio_nrf5_dppi_resources.h b/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio_nrf5_dppi_resources.h index 74fb56d9b5c..a8bbe8e63b4 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio_nrf5_dppi_resources.h +++ b/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio_nrf5_dppi_resources.h @@ -3,7 +3,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -#if defined(CONFIG_SOC_NRF5340_CPUNET) || defined(DPPI_PRESENT) +#if defined(CONFIG_SOC_COMPATIBLE_NRF5340_CPUNET) || defined(DPPI_PRESENT) /******************************************************************************* * Enable Radio on Event Timer tick: @@ -98,7 +98,7 @@ /* Wire a SW SWITCH TIMER EVENTS_COMPARE[] event * to a PPI GROUP TASK DISABLE task (PPI group with index ). - * 2 adjacent PPIs (8 & 9) and 2 adjacent PPI groups are used for this wiring; + * 2 adjacent PPIs (14 & 15) and 2 adjacent PPI groups are used for this wiring; * must be 0 or 1. must be a valid TIMER CC register offset. */ #define HAL_SW_SWITCH_GROUP_TASK_DISABLE_PPI_BASE 14 @@ -157,4 +157,4 @@ #define SW_SWITCH_TIMER_TASK_GROUP_BASE 0 #endif /* !CONFIG_BT_CTLR_TIFS_HW */ -#endif /* CONFIG_SOC_NRF5340_CPUNET || DPPI_PRESENT */ +#endif /* CONFIG_SOC_COMPATIBLE_NRF5340_CPUNET || DPPI_PRESENT */ diff --git a/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio_nrf5_ppi.h b/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio_nrf5_ppi.h index 866769c95eb..44cec4621a3 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio_nrf5_ppi.h +++ b/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio_nrf5_ppi.h @@ -205,6 +205,16 @@ static inline void hal_trigger_crypt_ppi_config(void) /* No need to configure anything for the pre-programmed channel. */ } +/******************************************************************************* + * Disable trigger encryption task + */ +static inline void hal_trigger_crypt_ppi_disable(void) +{ + /* No need to disable anything as ppi channel will be disabled in a + * separate disable ppi call by the caller of this function. + */ +} + #if defined(CONFIG_BT_CTLR_DF_CONN_CTE_RX) /******************************************************************************* * Trigger encryption task on Bit counter match: @@ -410,10 +420,7 @@ static inline void hal_sw_switch_timer_clear_ppi_config(void) #define HAL_SW_SWITCH_RADIO_ENABLE_PPI_TASK_RX \ ((uint32_t)&(NRF_RADIO->TASKS_RXEN)) -static inline void hal_radio_sw_switch_setup( - uint8_t compare_reg, - uint8_t radio_enable_ppi, - uint8_t ppi_group_index) +static inline void hal_radio_sw_switch_setup(uint8_t ppi_group_index) { /* Set up software switch mechanism for next Radio switch. */ @@ -425,53 +432,58 @@ static inline void hal_radio_sw_switch_setup( HAL_SW_SWITCH_GROUP_TASK_ENABLE_PPI, HAL_SW_SWITCH_GROUP_TASK_ENABLE_PPI_EVT, HAL_SW_SWITCH_GROUP_TASK_ENABLE_PPI_TASK(ppi_group_index)); +} +static inline void hal_radio_txen_on_sw_switch(uint8_t compare_reg_index, uint8_t radio_enable_ppi) +{ /* Wire SW Switch timer event to the * PPI[] for enabling Radio. Do * not wire the task; it is done by the caller of * the function depending on the desired direction * (TX/RX). */ - nrf_ppi_event_endpoint_setup( - NRF_PPI, - radio_enable_ppi, - HAL_SW_SWITCH_RADIO_ENABLE_PPI_EVT(compare_reg)); -} + nrf_ppi_event_endpoint_setup(NRF_PPI, radio_enable_ppi, + HAL_SW_SWITCH_RADIO_ENABLE_PPI_EVT(compare_reg_index)); -static inline void hal_radio_txen_on_sw_switch(uint8_t ppi) -{ - nrf_ppi_task_endpoint_setup( - NRF_PPI, - ppi, - HAL_SW_SWITCH_RADIO_ENABLE_PPI_TASK_TX); + nrf_ppi_task_endpoint_setup(NRF_PPI, radio_enable_ppi, + HAL_SW_SWITCH_RADIO_ENABLE_PPI_TASK_TX); } -static inline void hal_radio_b2b_txen_on_sw_switch(uint8_t ppi) +static inline void hal_radio_b2b_txen_on_sw_switch(uint8_t compare_reg_index, + uint8_t radio_enable_ppi) { /* NOTE: As independent PPI are used to trigger the Radio Tx task, * double buffers implementation works for sw_switch using PPIs, * simply reuse the hal_radio_txen_on_sw_switch() functon to set * the next PPIs task to be Radio Tx enable. */ - hal_radio_txen_on_sw_switch(ppi); + hal_radio_txen_on_sw_switch(compare_reg_index, radio_enable_ppi); } -static inline void hal_radio_rxen_on_sw_switch(uint8_t ppi) +static inline void hal_radio_rxen_on_sw_switch(uint8_t compare_reg_index, uint8_t radio_enable_ppi) { - nrf_ppi_task_endpoint_setup( - NRF_PPI, - ppi, - HAL_SW_SWITCH_RADIO_ENABLE_PPI_TASK_RX); + /* Wire SW Switch timer event to the + * PPI[] for enabling Radio. Do + * not wire the task; it is done by the caller of + * the function depending on the desired direction + * (TX/RX). + */ + nrf_ppi_event_endpoint_setup(NRF_PPI, radio_enable_ppi, + HAL_SW_SWITCH_RADIO_ENABLE_PPI_EVT(compare_reg_index)); + + nrf_ppi_task_endpoint_setup(NRF_PPI, radio_enable_ppi, + HAL_SW_SWITCH_RADIO_ENABLE_PPI_TASK_RX); } -static inline void hal_radio_b2b_rxen_on_sw_switch(uint8_t ppi) +static inline void hal_radio_b2b_rxen_on_sw_switch(uint8_t compare_reg_index, + uint8_t radio_enable_ppi) { - /* NOTE: As independent PPI are used to trigger the Radio Rx task, + /* NOTE: As independent PPI are used to trigger the Radio Tx task, * double buffers implementation works for sw_switch using PPIs, - * simply reuse the hal_radio_rxen_on_sw_switch() functon to set - * the next PPIs task to be Radio Rx enable. + * simply reuse the hal_radio_txen_on_sw_switch() functon to set + * the next PPIs task to be Radio Tx enable. */ - hal_radio_rxen_on_sw_switch(ppi); + hal_radio_rxen_on_sw_switch(compare_reg_index, radio_enable_ppi); } static inline void hal_radio_sw_switch_disable(void) @@ -486,6 +498,16 @@ static inline void hal_radio_sw_switch_disable(void) BIT(HAL_SW_SWITCH_GROUP_TASK_ENABLE_PPI)); } +static inline void hal_radio_sw_switch_b2b_tx_disable(uint8_t compare_reg_index) +{ + hal_radio_sw_switch_disable(); +} + +static inline void hal_radio_sw_switch_b2b_rx_disable(uint8_t compare_reg_index) +{ + hal_radio_sw_switch_disable(); +} + static inline void hal_radio_sw_switch_cleanup(void) { hal_radio_sw_switch_disable(); diff --git a/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio_nrf5_ppi_resources.h b/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio_nrf5_ppi_resources.h index 5c5093f4b52..ab196e17d58 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio_nrf5_ppi_resources.h +++ b/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio_nrf5_ppi_resources.h @@ -143,9 +143,6 @@ #define HAL_SW_SWITCH_RADIO_ENABLE_PPI_BASE 12 #endif -#if defined(CONFIG_BT_CTLR_PHY_CODED) && \ - defined(CONFIG_HAS_HW_NRF_RADIO_BLE_CODED) - /* Wire the SW SWITCH TIMER EVENTS_COMPARE[] event * to RADIO TASKS_TXEN/RXEN task. */ @@ -156,8 +153,6 @@ */ #define HAL_SW_SWITCH_TIMER_S8_DISABLE_PPI 19 -#endif /* CONFIG_HAS_HW_NRF_RADIO_BLE_CODED */ - #if defined(CONFIG_BT_CTLR_DF_PHYEND_OFFSET_COMPENSATION_ENABLE) /* Wire the SW SWITCH PHYEND delay compensation TIMER EVENTS_COMPARE[] event to software * switch TIMER0->CLEAR taks task. diff --git a/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio_sim_nrfxx.h b/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio_sim_nrf52.h similarity index 92% rename from subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio_sim_nrfxx.h rename to subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio_sim_nrf52.h index a767fe1ab30..1b8c1583547 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio_sim_nrfxx.h +++ b/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio_sim_nrf52.h @@ -14,7 +14,10 @@ /* NRF Radio HW timing constants * - provided in US and NS (for higher granularity) - * - based on empirical measurements and sniffer logs + * - based on the timings configured in the HW models, which are based + * on the product specification + * - Note that this timings are approx. the same as in the real HW, + * but tend to be rounded to the nearest microsecond */ /* TXEN->TXIDLE + TXIDLE->TX (with fast Radio ramp-up mode) @@ -185,14 +188,10 @@ static inline void hal_radio_reset(void) { - /* TODO: Add any required setup for each radio event - */ } static inline void hal_radio_stop(void) { - /* TODO: Add any required cleanup of actions taken in hal_radio_reset() - */ } static inline void hal_radio_ram_prio_setup(void) @@ -226,25 +225,39 @@ static inline uint32_t hal_radio_tx_power_min_get(void) static inline uint32_t hal_radio_tx_power_max_get(void) { -#if defined(RADIO_TXPOWER_TXPOWER_Pos4dBm) - return RADIO_TXPOWER_TXPOWER_Pos4dBm; -#else - return RADIO_TXPOWER_TXPOWER_0dBm; -#endif + return RADIO_TXPOWER_TXPOWER_Pos8dBm; } static inline uint32_t hal_radio_tx_power_floor(int8_t tx_power_lvl) { -#if defined(RADIO_TXPOWER_TXPOWER_Pos4dBm) + if (tx_power_lvl >= (int8_t)RADIO_TXPOWER_TXPOWER_Pos8dBm) { + return RADIO_TXPOWER_TXPOWER_Pos8dBm; + } + + if (tx_power_lvl >= (int8_t)RADIO_TXPOWER_TXPOWER_Pos7dBm) { + return RADIO_TXPOWER_TXPOWER_Pos7dBm; + } + + if (tx_power_lvl >= (int8_t)RADIO_TXPOWER_TXPOWER_Pos6dBm) { + return RADIO_TXPOWER_TXPOWER_Pos6dBm; + } + + if (tx_power_lvl >= (int8_t)RADIO_TXPOWER_TXPOWER_Pos5dBm) { + return RADIO_TXPOWER_TXPOWER_Pos5dBm; + } + if (tx_power_lvl >= (int8_t)RADIO_TXPOWER_TXPOWER_Pos4dBm) { return RADIO_TXPOWER_TXPOWER_Pos4dBm; } -#endif -#if defined(RADIO_TXPOWER_TXPOWER_Pos3dBm) + if (tx_power_lvl >= (int8_t)RADIO_TXPOWER_TXPOWER_Pos3dBm) { return RADIO_TXPOWER_TXPOWER_Pos3dBm; } -#endif + + if (tx_power_lvl >= (int8_t)RADIO_TXPOWER_TXPOWER_Pos2dBm) { + return RADIO_TXPOWER_TXPOWER_Pos2dBm; + } + if (tx_power_lvl >= (int8_t)RADIO_TXPOWER_TXPOWER_0dBm) { return RADIO_TXPOWER_TXPOWER_0dBm; } @@ -269,10 +282,7 @@ static inline uint32_t hal_radio_tx_power_floor(int8_t tx_power_lvl) return RADIO_TXPOWER_TXPOWER_Neg20dBm; } - if (tx_power_lvl >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg30dBm) { - return RADIO_TXPOWER_TXPOWER_Neg30dBm; - } - + /* Note: The -30 dBm power level is deprecated so ignore it! */ return RADIO_TXPOWER_TXPOWER_Neg40dBm; } @@ -354,7 +364,7 @@ static inline uint32_t hal_radio_tx_chain_delay_ns_get(uint8_t phy, uint8_t flag ARG_UNUSED(phy); ARG_UNUSED(flags); - return HAL_RADIO_NRF52833_TX_CHAIN_DELAY_US; + return HAL_RADIO_NRF52833_TX_CHAIN_DELAY_NS; } static inline uint32_t hal_radio_rx_chain_delay_ns_get(uint8_t phy, uint8_t flags) diff --git a/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio_sim_nrf5340.h b/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio_sim_nrf5340.h new file mode 100644 index 00000000000..c0cd88b30b0 --- /dev/null +++ b/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/radio/radio_sim_nrf5340.h @@ -0,0 +1,431 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * Copyright (c) 2019 Ioannis Glaropoulos + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +/* NRF Radio HW timing constants + * - provided in US and NS (for higher granularity) + * - based on the timings configured in the HW models, which are based + * on the product specification + * - Note that this timings are approx. the same as in the real HW, + * but tend to be rounded to the nearest microsecond + */ + +/* Override EVENT_TIMER_ID from 4 to 0, as nRF5340 does not have 4 timer + * instances. + */ +#if defined(CONFIG_BT_CTLR_SW_SWITCH_SINGLE_TIMER) +#undef EVENT_TIMER_ID +#define EVENT_TIMER_ID 0 + +#undef EVENT_TIMER +#define EVENT_TIMER _CONCAT(NRF_TIMER, EVENT_TIMER_ID) + +#undef SW_SWITCH_TIMER +#define SW_SWITCH_TIMER EVENT_TIMER +#endif /* CONFIG_BT_CTLR_SW_SWITCH_SINGLE_TIMER */ + +/* TXEN->TXIDLE + TXIDLE->TX (with fast Radio ramp-up mode) + * in microseconds for LE 1M PHY. + */ +#define HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_1M_FAST_NS 41000 +#define HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_1M_FAST_US \ + HAL_RADIO_NS2US_ROUND(HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_1M_FAST_NS) + +/* TXEN->TXIDLE + TXIDLE->TX (with default Radio ramp-up mode) + * in microseconds for LE 1M PHY. + */ +#define HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_1M_DEFAULT_NS 141000 +#define HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_1M_DEFAULT_US \ + HAL_RADIO_NS2US_ROUND(HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_1M_DEFAULT_NS) + +/* TXEN->TXIDLE + TXIDLE->TX (with default Radio ramp-up mode + * and no HW TIFS auto-switch) in microseconds for LE 1M PHY. + */ + /* 129.5 + 0.8 */ +#define HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_1M_DEFAULT_NO_HW_TIFS_NS 130000 +#define HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_1M_DEFAULT_NO_HW_TIFS_US \ + HAL_RADIO_NS2US_ROUND( \ + HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_1M_DEFAULT_NO_HW_TIFS_NS) + +/* TXEN->TXIDLE + TXIDLE->TX (with fast Radio ramp-up mode) + * in microseconds for LE 2M PHY. + */ +#define HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_2M_FAST_NS 40000 +#define HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_2M_FAST_US \ + HAL_RADIO_NS2US_ROUND(HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_2M_FAST_NS) + +/* TXEN->TXIDLE + TXIDLE->TX (with default Radio ramp-up mode) + * in microseconds for LE 2M PHY. + */ +#define HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_2M_DEFAULT_NS 140000 +#define HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_2M_DEFAULT_US \ + HAL_RADIO_NS2US_ROUND(HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_2M_DEFAULT_NS) + +/* TXEN->TXIDLE + TXIDLE->TX (with default Radio ramp-up mode and + * no HW TIFS auto-switch) in microseconds for LE 2M PHY. + */ +#define HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_2M_DEFAULT_NO_HW_TIFS_NS 129000 +#define HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_2M_DEFAULT_NO_HW_TIFS_US \ + HAL_RADIO_NS2US_ROUND( \ + HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_2M_DEFAULT_NO_HW_TIFS_NS) + +/* RXEN->RXIDLE + RXIDLE->RX (with fast Radio ramp-up mode) + * in microseconds for LE 1M PHY. + */ +#define HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_1M_FAST_NS 40000 +#define HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_1M_FAST_US \ + HAL_RADIO_NS2US_CEIL(HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_1M_FAST_NS) + +/* RXEN->RXIDLE + RXIDLE->RX (with default Radio ramp-up mode) + * in microseconds for LE 1M PHY. + */ +#define HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_1M_DEFAULT_NS 140000 +#define HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_1M_DEFAULT_US \ + HAL_RADIO_NS2US_CEIL(HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_1M_DEFAULT_NS) + +/* RXEN->RXIDLE + RXIDLE->RX (with default Radio ramp-up mode and + * no HW TIFS auto-switch) in microseconds for LE 1M PHY. + */ +/* 129.5 + 0.2 */ +#define HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_1M_DEFAULT_NO_HW_TIFS_NS 129000 +#define HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_1M_DEFAULT_NO_HW_TIFS_US \ + HAL_RADIO_NS2US_CEIL( \ + HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_1M_DEFAULT_NO_HW_TIFS_NS) + +/* RXEN->RXIDLE + RXIDLE->RX (with fast Radio ramp-up mode) + * in microseconds for LE 2M PHY. + */ +#define HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_2M_FAST_NS 40000 +#define HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_2M_FAST_US \ + HAL_RADIO_NS2US_CEIL(HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_2M_FAST_NS) + +/* RXEN->RXIDLE + RXIDLE->RX (with default Radio ramp-up mode) + * in microseconds for LE 2M PHY. + */ +#define HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_2M_DEFAULT_NS 140000 +#define HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_2M_DEFAULT_US \ + HAL_RADIO_NS2US_CEIL(HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_2M_DEFAULT_NS) + +/* RXEN->RXIDLE + RXIDLE->RX (with default Radio ramp-up mode and + * no HW TIFS auto-switch) in microseconds for LE 2M PHY. + */ +/* 129.5 + 0.2 */ +#define HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_2M_DEFAULT_NO_HW_TIFS_NS 129000 +#define HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_2M_DEFAULT_NO_HW_TIFS_US \ + HAL_RADIO_NS2US_CEIL( \ + HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_2M_DEFAULT_NO_HW_TIFS_NS) + +#define HAL_RADIO_NRF5340_TX_CHAIN_DELAY_NS 1000 +#define HAL_RADIO_NRF5340_TX_CHAIN_DELAY_US \ + HAL_RADIO_NS2US_CEIL(HAL_RADIO_NRF5340_TX_CHAIN_DELAY_NS) + +#define HAL_RADIO_NRF5340_RX_CHAIN_DELAY_1M_US 9 +#define HAL_RADIO_NRF5340_RX_CHAIN_DELAY_1M_NS 9000 +#define HAL_RADIO_NRF5340_RX_CHAIN_DELAY_2M_US 5 +#define HAL_RADIO_NRF5340_RX_CHAIN_DELAY_2M_NS 5000 + +#if defined(CONFIG_BT_CTLR_RADIO_ENABLE_FAST) +#define HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_1M_US \ + HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_1M_FAST_US +#define HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_1M_NS \ + HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_1M_FAST_NS + +#define HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_2M_US \ + HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_2M_FAST_US +#define HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_2M_NS \ + HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_2M_FAST_NS + +#define HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_1M_US \ + HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_1M_FAST_US +#define HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_1M_NS \ + HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_1M_FAST_NS + +#define HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_2M_US \ + HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_2M_FAST_US +#define HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_2M_NS \ + HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_2M_FAST_NS + +#else /* !CONFIG_BT_CTLR_RADIO_ENABLE_FAST */ +#if defined(CONFIG_BT_CTLR_TIFS_HW) +#define HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_1M_US \ + HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_1M_DEFAULT_US +#define HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_1M_NS \ + HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_1M_DEFAULT_NS + +#define HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_2M_US \ + HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_2M_DEFAULT_US +#define HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_2M_NS \ + HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_2M_DEFAULT_NS + +#define HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_1M_US \ + HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_1M_DEFAULT_US +#define HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_1M_NS \ + HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_1M_DEFAULT_NS + +#define HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_2M_US \ + HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_2M_DEFAULT_US +#define HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_2M_NS \ + HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_2M_DEFAULT_NS + +#else /* !CONFIG_BT_CTLR_TIFS_HW */ +#define HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_1M_US \ + HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_1M_DEFAULT_NO_HW_TIFS_US +#define HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_1M_NS \ + HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_1M_DEFAULT_NO_HW_TIFS_NS + +#define HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_2M_US \ + HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_2M_DEFAULT_NO_HW_TIFS_US +#define HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_2M_NS \ + HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_2M_DEFAULT_NO_HW_TIFS_NS + +#define HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_1M_US \ + HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_1M_DEFAULT_NO_HW_TIFS_US +#define HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_1M_NS \ + HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_1M_DEFAULT_NO_HW_TIFS_NS + +#define HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_2M_US \ + HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_2M_DEFAULT_NO_HW_TIFS_US +#define HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_2M_NS \ + HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_2M_DEFAULT_NO_HW_TIFS_NS + +#endif /* !CONFIG_BT_CTLR_TIFS_HW */ +#endif /* !CONFIG_BT_CTLR_RADIO_ENABLE_FAST */ + +/* nRF5340 supports +3dBm Tx Power using high voltage request, define +3dBm + * value for Controller use. + */ +#ifndef RADIO_TXPOWER_TXPOWER_Pos3dBm +#define RADIO_TXPOWER_TXPOWER_Pos3dBm (0x03UL) +#endif + +/* SoC specific NRF_RADIO power-on reset value. Refer to Product Specification, + * RADIO Registers section for the documented reset values. + * + * NOTE: Only implementation used values defined here. + * In the future if MDK or nRFx header include these, use them instead. + */ +#define HAL_RADIO_RESET_VALUE_DFEMODE 0x00000000UL +#define HAL_RADIO_RESET_VALUE_CTEINLINECONF 0x00002800UL + +static inline void hal_radio_tx_power_high_voltage_clear(void); + +static inline void hal_radio_reset(void) +{ +} + +static inline void hal_radio_stop(void) +{ + /* If +3dBm Tx power was used, then turn off high voltage when radio not + * used. + */ + hal_radio_tx_power_high_voltage_clear(); +} + +static inline void hal_radio_ram_prio_setup(void) +{ +} + +static inline uint32_t hal_radio_phy_mode_get(uint8_t phy, uint8_t flags) +{ + uint32_t mode; + + switch (phy) { + case BIT(0): + default: + mode = RADIO_MODE_MODE_Ble_1Mbit; + break; + + case BIT(1): + mode = RADIO_MODE_MODE_Ble_2Mbit; + break; + +#if defined(CONFIG_BT_CTLR_PHY_CODED) + case BIT(2): + if (flags & 0x01) { + mode = RADIO_MODE_MODE_Ble_LR125Kbit; + } else { + mode = RADIO_MODE_MODE_Ble_LR500Kbit; + } + break; +#endif /* CONFIG_BT_CTLR_PHY_CODED */ + } + + return mode; +} + +static inline uint32_t hal_radio_tx_power_max_get(void) +{ + return RADIO_TXPOWER_TXPOWER_0dBm; +} + +static inline uint32_t hal_radio_tx_power_min_get(void) +{ + return RADIO_TXPOWER_TXPOWER_Neg40dBm; +} + +static inline uint32_t hal_radio_tx_power_floor(int8_t tx_power_lvl) +{ + if (tx_power_lvl >= (int8_t)RADIO_TXPOWER_TXPOWER_0dBm) { + return RADIO_TXPOWER_TXPOWER_0dBm; + } + + if (tx_power_lvl >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg1dBm) { + return RADIO_TXPOWER_TXPOWER_Neg1dBm; + } + + if (tx_power_lvl >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg2dBm) { + return RADIO_TXPOWER_TXPOWER_Neg2dBm; + } + + if (tx_power_lvl >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg3dBm) { + return RADIO_TXPOWER_TXPOWER_Neg3dBm; + } + + if (tx_power_lvl >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg4dBm) { + return RADIO_TXPOWER_TXPOWER_Neg4dBm; + } + + if (tx_power_lvl >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg5dBm) { + return RADIO_TXPOWER_TXPOWER_Neg5dBm; + } + + if (tx_power_lvl >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg6dBm) { + return RADIO_TXPOWER_TXPOWER_Neg6dBm; + } + + if (tx_power_lvl >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg7dBm) { + return RADIO_TXPOWER_TXPOWER_Neg7dBm; + } + + if (tx_power_lvl >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg8dBm) { + return RADIO_TXPOWER_TXPOWER_Neg8dBm; + } + + if (tx_power_lvl >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg12dBm) { + return RADIO_TXPOWER_TXPOWER_Neg12dBm; + } + + if (tx_power_lvl >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg16dBm) { + return RADIO_TXPOWER_TXPOWER_Neg16dBm; + } + + if (tx_power_lvl >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg20dBm) { + return RADIO_TXPOWER_TXPOWER_Neg20dBm; + } + + /* Note: The -30 dBm power level is deprecated so ignore it! */ + return RADIO_TXPOWER_TXPOWER_Neg40dBm; +} + +static inline void hal_radio_tx_power_high_voltage_set(int8_t tx_power_lvl) +{ + if (tx_power_lvl >= (int8_t)RADIO_TXPOWER_TXPOWER_Pos3dBm) { + nrf_vreqctrl_radio_high_voltage_set(NRF_VREQCTRL, true); + } +} + +static inline void hal_radio_tx_power_high_voltage_clear(void) +{ + nrf_vreqctrl_radio_high_voltage_set(NRF_VREQCTRL, false); +} + +static inline uint32_t hal_radio_tx_ready_delay_us_get(uint8_t phy, uint8_t flags) +{ + ARG_UNUSED(flags); + + switch (phy) { + default: + case BIT(0): + return HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_1M_US; + case BIT(1): + return HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_2M_US; + } +} + +static inline uint32_t hal_radio_rx_ready_delay_us_get(uint8_t phy, uint8_t flags) +{ + ARG_UNUSED(flags); + + switch (phy) { + default: + case BIT(0): + return HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_1M_US; + case BIT(1): + return HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_2M_US; + } +} + +static inline uint32_t hal_radio_tx_chain_delay_us_get(uint8_t phy, uint8_t flags) +{ + ARG_UNUSED(phy); + ARG_UNUSED(flags); + + return HAL_RADIO_NRF5340_TX_CHAIN_DELAY_US; +} + +static inline uint32_t hal_radio_rx_chain_delay_us_get(uint8_t phy, uint8_t flags) +{ + ARG_UNUSED(flags); + + switch (phy) { + default: + case BIT(0): + return HAL_RADIO_NRF5340_RX_CHAIN_DELAY_1M_US; + case BIT(1): + return HAL_RADIO_NRF5340_RX_CHAIN_DELAY_2M_US; + } +} + +static inline uint32_t hal_radio_tx_ready_delay_ns_get(uint8_t phy, uint8_t flags) +{ + ARG_UNUSED(flags); + + switch (phy) { + default: + case BIT(0): + return HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_1M_NS; + case BIT(1): + return HAL_RADIO_NRF5340_TXEN_TXIDLE_TX_2M_NS; + } +} + +static inline uint32_t hal_radio_rx_ready_delay_ns_get(uint8_t phy, uint8_t flags) +{ + ARG_UNUSED(flags); + + switch (phy) { + default: + case BIT(0): + return HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_1M_NS; + case BIT(1): + return HAL_RADIO_NRF5340_RXEN_RXIDLE_RX_2M_NS; + } +} + +static inline uint32_t hal_radio_tx_chain_delay_ns_get(uint8_t phy, uint8_t flags) +{ + ARG_UNUSED(phy); + ARG_UNUSED(flags); + + return HAL_RADIO_NRF5340_TX_CHAIN_DELAY_NS; +} + +static inline uint32_t hal_radio_rx_chain_delay_ns_get(uint8_t phy, uint8_t flags) +{ + ARG_UNUSED(flags); + + switch (phy) { + default: + case BIT(0): + return HAL_RADIO_NRF5340_RX_CHAIN_DELAY_1M_NS; + case BIT(1): + return HAL_RADIO_NRF5340_RX_CHAIN_DELAY_2M_NS; + + } +} diff --git a/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/swi.h b/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/swi.h index eb9da5652ff..6b364fd349a 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/swi.h +++ b/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/swi.h @@ -18,10 +18,10 @@ #endif /* nRF53 Series IRQ mapping */ -#elif defined(CONFIG_SOC_SERIES_NRF53X) +#elif defined(CONFIG_SOC_COMPATIBLE_NRF53X) /* nRF53 Series Engineering D and Revision 1 IRQ mapping */ -#if defined(CONFIG_SOC_NRF5340_CPUNET) +#if defined(CONFIG_SOC_COMPATIBLE_NRF5340_CPUNET) #define HAL_SWI_RADIO_IRQ SWI2_IRQn #define HAL_SWI_WORKER_IRQ RTC0_IRQn @@ -33,9 +33,9 @@ #define HAL_SWI_JOB_IRQ SWI3_IRQn #endif -#endif /* CONFIG_SOC_NRF5340_CPUNET */ +#endif /* CONFIG_SOC_COMPATIBLE_NRF5340_CPUNET */ -#endif /* CONFIG_SOC_SERIES_NRF53X */ +#endif /* CONFIG_SOC_COMPATIBLE_NRF53X */ static inline void hal_swi_init(void) { diff --git a/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/ticker.h b/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/ticker.h index 5845d15145a..10d780a559c 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/ticker.h +++ b/subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/ticker.h @@ -5,8 +5,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -#define HAL_TICKER_CNTR_CLK_FREQ_HZ 32768U -#define HAL_TICKER_CNTR_CLK_UNIT_FS 30517578125UL +#define HAL_TICKER_CNTR_CLK_FREQ_HZ 32768U +#define HAL_TICKER_CNTR_CLK_UNIT_FSEC 30517578125UL +#define HAL_TICKER_FSEC_PER_USEC 1000000000UL +#define HAL_TICKER_PSEC_PER_USEC 1000000UL +#define HAL_TICKER_FSEC_PER_PSEC 1000UL /* Macro defining the minimum counter compare offset */ #define HAL_TICKER_CNTR_CMP_OFFSET_MIN 3 @@ -25,33 +28,44 @@ */ #define HAL_TICKER_US_TO_TICKS(x) \ ( \ - ((uint32_t)(((uint64_t) (x) * 1000000000UL) / \ - HAL_TICKER_CNTR_CLK_UNIT_FS)) & HAL_TICKER_CNTR_MASK \ + ((uint32_t)(((uint64_t) (x) * HAL_TICKER_FSEC_PER_USEC) / \ + HAL_TICKER_CNTR_CLK_UNIT_FSEC)) & \ + HAL_TICKER_CNTR_MASK \ + ) + +/* Macro to translate microseconds to tick units. + * NOTE: This returns the ceil value. + */ +#define HAL_TICKER_US_TO_TICKS_CEIL(x) \ + ( \ + DIV_ROUND_UP(((uint64_t) (x) * HAL_TICKER_FSEC_PER_USEC), \ + HAL_TICKER_CNTR_CLK_UNIT_FSEC) & \ + HAL_TICKER_CNTR_MASK \ ) /* Macro to translate tick units to microseconds. */ #define HAL_TICKER_TICKS_TO_US(x) \ ( \ - ((uint32_t)(((uint64_t)(x) * HAL_TICKER_CNTR_CLK_UNIT_FS) / \ - 1000000000UL)) \ + ((uint32_t)(((uint64_t)(x) * HAL_TICKER_CNTR_CLK_UNIT_FSEC) / \ + HAL_TICKER_FSEC_PER_USEC)) \ ) /* Macro returning remainder in picoseconds (to fit in 32-bits) */ #define HAL_TICKER_REMAINDER(x) \ ( \ ( \ - ((uint64_t) (x) * 1000000000UL) \ + ((uint64_t) (x) * HAL_TICKER_FSEC_PER_USEC) \ - ((uint64_t)HAL_TICKER_US_TO_TICKS(x) * \ - HAL_TICKER_CNTR_CLK_UNIT_FS) \ + HAL_TICKER_CNTR_CLK_UNIT_FSEC) \ ) \ - / 1000UL \ + / HAL_TICKER_FSEC_PER_PSEC \ ) /* Macro defining the remainder resolution/range * ~ 1000000 * HAL_TICKER_TICKS_TO_US(1) */ #define HAL_TICKER_REMAINDER_RANGE \ - HAL_TICKER_TICKS_TO_US(1000000) + HAL_TICKER_TICKS_TO_US(HAL_TICKER_PSEC_PER_USEC) /* Macro defining the margin for positioning re-scheduled nodes */ #define HAL_TICKER_RESCHEDULE_MARGIN \ @@ -62,24 +76,24 @@ static inline void hal_ticker_remove_jitter(uint32_t *ticks, uint32_t *remainder) { /* Is remainder less than 1 us */ - if ((*remainder & BIT(31)) || !(*remainder / 1000000UL)) { + if ((*remainder & BIT(31)) || !(*remainder / HAL_TICKER_PSEC_PER_USEC)) { *ticks -= 1U; - *remainder += HAL_TICKER_CNTR_CLK_UNIT_FS / 1000UL; + *remainder += HAL_TICKER_CNTR_CLK_UNIT_FSEC / HAL_TICKER_FSEC_PER_PSEC; } /* pico seconds to micro seconds unit */ - *remainder /= 1000000UL; + *remainder /= HAL_TICKER_PSEC_PER_USEC; } /* Add ticks and return positive remainder value in microseconds */ static inline void hal_ticker_add_jitter(uint32_t *ticks, uint32_t *remainder) { /* Is remainder less than 1 us */ - if ((*remainder & BIT(31)) || !(*remainder / 1000000UL)) { + if ((*remainder & BIT(31)) || !(*remainder / HAL_TICKER_PSEC_PER_USEC)) { *ticks += 1U; - *remainder += HAL_TICKER_CNTR_CLK_UNIT_FS / 1000UL; + *remainder += HAL_TICKER_CNTR_CLK_UNIT_FSEC / HAL_TICKER_FSEC_PER_PSEC; } /* pico seconds to micro seconds unit */ - *remainder /= 1000000UL; + *remainder /= HAL_TICKER_PSEC_PER_USEC; } diff --git a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll.c b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll.c index bc71bbcd5a6..db8f25a374c 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll.c +++ b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll.c @@ -8,6 +8,8 @@ #include #include +#include + #include #include @@ -63,7 +65,9 @@ static int init_reset(void); #if defined(CONFIG_BT_CTLR_LOW_LAT_ULL_DONE) static inline void done_inc(void); #endif /* CONFIG_BT_CTLR_LOW_LAT_ULL_DONE */ -static struct lll_event *resume_enqueue(lll_prepare_cb_t resume_cb); +static inline bool is_done_sync(void); +static inline struct lll_event *prepare_dequeue_iter_ready_get(uint8_t *idx); +static inline struct lll_event *resume_enqueue(lll_prepare_cb_t resume_cb); static void isr_race(void *param); #if !defined(CONFIG_BT_CTLR_LOW_LAT) @@ -106,7 +110,7 @@ static void rtc0_nrf5_isr(const void *arg) /* On compare0 run ticker worker instance0 */ if (NRF_RTC0->EVENTS_COMPARE[0]) { - NRF_RTC0->EVENTS_COMPARE[0] = 0; + nrf_rtc_event_clear(NRF_RTC0, NRF_RTC_EVENT_COMPARE_0); ticker_trigger(0); } @@ -641,52 +645,27 @@ void lll_isr_early_abort(void *param) lll_done(NULL); } -static int init_reset(void) -{ - return 0; -} - -#if defined(CONFIG_BT_CTLR_LOW_LAT_ULL_DONE) -static inline void done_inc(void) -{ - event.done.lll_count++; - LL_ASSERT(event.done.lll_count != event.done.ull_count); -} -#endif /* CONFIG_BT_CTLR_LOW_LAT_ULL_DONE */ - -static inline bool is_done_sync(void) -{ -#if defined(CONFIG_BT_CTLR_LOW_LAT_ULL_DONE) - return event.done.lll_count == event.done.ull_count; -#else /* !CONFIG_BT_CTLR_LOW_LAT_ULL_DONE */ - return true; -#endif /* !CONFIG_BT_CTLR_LOW_LAT_ULL_DONE */ -} - int lll_prepare_resolve(lll_is_abort_cb_t is_abort_cb, lll_abort_cb_t abort_cb, lll_prepare_cb_t prepare_cb, struct lll_prepare_param *prepare_param, uint8_t is_resume, uint8_t is_dequeue) { - struct lll_event *p; + struct lll_event *ready; + struct lll_event *next; uint8_t idx; int err; /* Find the ready prepare in the pipeline */ idx = UINT8_MAX; - p = ull_prepare_dequeue_iter(&idx); - while (p && (p->is_aborted || p->is_resume)) { - p = ull_prepare_dequeue_iter(&idx); - } + ready = prepare_dequeue_iter_ready_get(&idx); /* Current event active or another prepare is ready in the pipeline */ if ((!is_dequeue && !is_done_sync()) || event.curr.abort_cb || - (p && is_resume)) { + (ready && is_resume)) { #if defined(CONFIG_BT_CTLR_LOW_LAT) lll_prepare_cb_t resume_cb; #endif /* CONFIG_BT_CTLR_LOW_LAT */ - struct lll_event *next; if (IS_ENABLED(CONFIG_BT_CTLR_LOW_LAT) && event.curr.param) { /* early abort */ @@ -704,29 +683,28 @@ int lll_prepare_resolve(lll_is_abort_cb_t is_abort_cb, lll_abort_cb_t abort_cb, } /* Always start preempt timeout for first prepare in pipeline */ - struct lll_event *first = p ? p : next; + struct lll_event *first = ready ? ready : next; uint32_t ret; /* Start the preempt timeout */ - ret = preempt_ticker_start(first, p, next); + ret = preempt_ticker_start(first, ready, next); LL_ASSERT((ret == TICKER_STATUS_SUCCESS) || (ret == TICKER_STATUS_BUSY)); #else /* CONFIG_BT_CTLR_LOW_LAT */ next = NULL; - while (p) { - if (!p->is_aborted) { - if (event.curr.param == - p->prepare_param.param) { - p->is_aborted = 1; - p->abort_cb(&p->prepare_param, - p->prepare_param.param); + while (ready) { + if (!ready->is_aborted) { + if (event.curr.param == ready->prepare_param.param) { + ready->is_aborted = 1; + ready->abort_cb(&ready->prepare_param, + ready->prepare_param.param); } else { - next = p; + next = ready; } } - p = ull_prepare_dequeue_iter(&idx); + ready = ull_prepare_dequeue_iter(&idx); } if (next) { @@ -747,7 +725,7 @@ int lll_prepare_resolve(lll_is_abort_cb_t is_abort_cb, lll_abort_cb_t abort_cb, return -EINPROGRESS; } - LL_ASSERT(!p || &p->prepare_param == prepare_param); + LL_ASSERT(!ready || &ready->prepare_param == prepare_param); event.curr.param = prepare_param->param; event.curr.is_abort_cb = is_abort_cb; @@ -772,15 +750,13 @@ int lll_prepare_resolve(lll_is_abort_cb_t is_abort_cb, lll_abort_cb_t abort_cb, */ /* Find next prepare needing preempt timeout to be setup */ - do { - p = ull_prepare_dequeue_iter(&idx); - if (!p) { - return err; - } - } while (p->is_aborted || p->is_resume); + next = prepare_dequeue_iter_ready_get(&idx); + if (!next) { + return err; + } /* Start the preempt timeout */ - ret = preempt_ticker_start(p, NULL, p); + ret = preempt_ticker_start(next, NULL, next); LL_ASSERT((ret == TICKER_STATUS_SUCCESS) || (ret == TICKER_STATUS_BUSY)); #endif /* !CONFIG_BT_CTLR_LOW_LAT */ @@ -788,7 +764,40 @@ int lll_prepare_resolve(lll_is_abort_cb_t is_abort_cb, lll_abort_cb_t abort_cb, return err; } -static struct lll_event *resume_enqueue(lll_prepare_cb_t resume_cb) +static int init_reset(void) +{ + return 0; +} + +#if defined(CONFIG_BT_CTLR_LOW_LAT_ULL_DONE) +static inline void done_inc(void) +{ + event.done.lll_count++; + LL_ASSERT(event.done.lll_count != event.done.ull_count); +} +#endif /* CONFIG_BT_CTLR_LOW_LAT_ULL_DONE */ + +static inline bool is_done_sync(void) +{ +#if defined(CONFIG_BT_CTLR_LOW_LAT_ULL_DONE) + return event.done.lll_count == event.done.ull_count; +#else /* !CONFIG_BT_CTLR_LOW_LAT_ULL_DONE */ + return true; +#endif /* !CONFIG_BT_CTLR_LOW_LAT_ULL_DONE */ +} + +static inline struct lll_event *prepare_dequeue_iter_ready_get(uint8_t *idx) +{ + struct lll_event *ready; + + do { + ready = ull_prepare_dequeue_iter(idx); + } while (ready && (ready->is_aborted || ready->is_resume)); + + return ready; +} + +static inline struct lll_event *resume_enqueue(lll_prepare_cb_t resume_cb) { struct lll_prepare_param prepare_param = {0}; @@ -833,11 +842,17 @@ static void ticker_start_op_cb(uint32_t status, void *param) ARG_UNUSED(param); LL_ASSERT(status == TICKER_STATUS_SUCCESS); - LL_ASSERT(preempt_start_req != preempt_start_ack); - preempt_start_ack++; - + /* Increase preempt requested count before acknowledging that the + * ticker start operation for the preempt timeout has been handled. + */ LL_ASSERT(preempt_req == preempt_ack); preempt_req++; + + /* Increase preempt start ack count, to acknowledge that the ticker + * start operation has been handled. + */ + LL_ASSERT(preempt_start_req != preempt_start_ack); + preempt_start_ack++; } static uint32_t preempt_ticker_start(struct lll_event *first, @@ -852,11 +867,22 @@ static uint32_t preempt_ticker_start(struct lll_event *first, uint32_t preempt_to; uint32_t ret; - /* Do not request to start preempt timeout if already requested */ + /* Do not request to start preempt timeout if already requested. + * + * Check if there is pending preempt timeout start requested or if + * preempt timeout ticker has already been scheduled. + */ if ((preempt_start_req != preempt_start_ack) || (preempt_req != preempt_ack)) { uint32_t diff; + /* preempt timeout already started but no role/state in the head + * of prepare pipeline. + */ + if (!prev || prev->is_aborted) { + return TICKER_STATUS_SUCCESS; + } + /* Calc the preempt timeout */ p = &next->prepare_param; ull = HDR_LLL2ULL(p->param); @@ -869,9 +895,9 @@ static uint32_t preempt_ticker_start(struct lll_event *first, ticks_at_preempt_new &= HAL_TICKER_CNTR_MASK; /* Check for short preempt timeouts */ - diff = ticks_at_preempt_new - ticks_at_preempt; - if (!prev || prev->is_aborted || - ((diff & BIT(HAL_TICKER_CNTR_MSBIT)) == 0U)) { + diff = ticker_ticks_diff_get(ticks_at_preempt_new, + ticks_at_preempt); + if ((diff & BIT(HAL_TICKER_CNTR_MSBIT)) == 0U) { return TICKER_STATUS_SUCCESS; } @@ -880,15 +906,6 @@ static uint32_t preempt_ticker_start(struct lll_event *first, LL_ASSERT((ret == TICKER_STATUS_SUCCESS) || (ret == TICKER_STATUS_BUSY)); - /* Set early as we get called again through the call to - * abort_cb(). - */ - ticks_at_preempt = ticks_at_preempt_new; - - /* Abort previous prepare that set the preempt timeout */ - prev->is_aborted = 1U; - prev->abort_cb(&prev->prepare_param, prev->prepare_param.param); - /* Schedule short preempt timeout */ first = next; } else { @@ -968,7 +985,7 @@ static void preempt_ticker_cb(uint32_t ticks_at_expire, uint32_t ticks_drift, static void preempt(void *param) { lll_prepare_cb_t resume_cb; - struct lll_event *next; + struct lll_event *ready; uint8_t idx; int err; @@ -977,43 +994,67 @@ static void preempt(void *param) return; } - /* Check if any prepare in pipeline */ - idx = UINT8_MAX; - next = ull_prepare_dequeue_iter(&idx); - if (!next) { - return; - } - /* Find a prepare that is ready and not a resume */ - while (next && (next->is_aborted || next->is_resume)) { - next = ull_prepare_dequeue_iter(&idx); - } - - /* No ready prepare */ - if (!next) { + idx = UINT8_MAX; + ready = prepare_dequeue_iter_ready_get(&idx); + if (!ready) { + /* No ready prepare */ return; } /* Preemptor not in pipeline */ - if (next->prepare_param.param != param) { + if (ready->prepare_param.param != param) { + struct lll_event *ready_next = NULL; + struct lll_event *preemptor; uint32_t ret; - /* Start the preempt timeout */ - ret = preempt_ticker_start(next, NULL, next); - LL_ASSERT((ret == TICKER_STATUS_SUCCESS) || - (ret == TICKER_STATUS_BUSY)); + /* Find if a short prepare request in the pipeline */ + do { + preemptor = ull_prepare_dequeue_iter(&idx); + if (!ready_next && preemptor && !preemptor->is_aborted && + !preemptor->is_resume) { + ready_next = preemptor; + } + } while (preemptor && (preemptor->is_aborted || preemptor->is_resume || + (preemptor->prepare_param.param != param))); - return; + /* No short prepare request in pipeline */ + if (!preemptor) { + /* Start the preempt timeout for ready event */ + ret = preempt_ticker_start(ready, NULL, ready); + LL_ASSERT((ret == TICKER_STATUS_SUCCESS) || + (ret == TICKER_STATUS_BUSY)); + + return; + } + + /* FIXME: Abort all events in pipeline before the short + * prepare event. For now, lets assert when many + * enqueued prepares need aborting. + */ + LL_ASSERT(preemptor == ready_next); + + /* Abort the prepare that is present before the short prepare */ + ready->is_aborted = 1; + ready->abort_cb(&ready->prepare_param, ready->prepare_param.param); + + /* As the prepare queue has been refreshed due to the call of + * abort_cb which invokes the lll_done, find the latest prepare + */ + idx = UINT8_MAX; + ready = prepare_dequeue_iter_ready_get(&idx); + if (!ready) { + /* No ready prepare */ + return; + } } /* Check if current event want to continue */ - err = event.curr.is_abort_cb(next->prepare_param.param, - event.curr.param, - &resume_cb); + err = event.curr.is_abort_cb(ready->prepare_param.param, event.curr.param, &resume_cb); if (!err) { /* Let preemptor LLL know about the cancelled prepare */ - next->is_aborted = 1; - next->abort_cb(&next->prepare_param, next->prepare_param.param); + ready->is_aborted = 1; + ready->abort_cb(&ready->prepare_param, ready->prepare_param.param); return; } diff --git a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_adv_aux.c b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_adv_aux.c index ca566a04db4..6fa0204fb56 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_adv_aux.c +++ b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_adv_aux.c @@ -394,7 +394,7 @@ static void isr_tx_chain(void *param) lll->phy_s, lll->phy_flags); } else { radio_isr_set(isr_done, lll_aux); - radio_switch_complete_and_disable(); + radio_switch_complete_and_b2b_tx_disable(); } radio_pkt_tx_set(pdu); diff --git a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_adv_iso.c b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_adv_iso.c index 87849d0e1fd..20e3fe528e1 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_adv_iso.c +++ b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_adv_iso.c @@ -717,7 +717,7 @@ static void isr_tx_common(void *param, pkt_flags); } - radio_switch_complete_and_disable(); + radio_switch_complete_and_b2b_tx_disable(); radio_isr_set(isr_done_term, lll); } else { diff --git a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_adv_sync.c b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_adv_sync.c index 1e56f70493c..79a8c6b25cd 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_adv_sync.c +++ b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_adv_sync.c @@ -367,7 +367,7 @@ static void isr_tx(void *param) switch_radio_complete_and_b2b_tx(lll_sync, lll->phy_s); } else { radio_isr_set(isr_done, lll_sync); - radio_switch_complete_and_disable(); + radio_switch_complete_and_b2b_tx_disable(); } radio_pkt_tx_set(pdu); diff --git a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_central_iso.c b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_central_iso.c index 6135cc9d863..751c3915fc2 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_central_iso.c +++ b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_central_iso.c @@ -40,14 +40,15 @@ #include "hal/debug.h" static int init_reset(void); -static inline void lll_flush_tx(struct lll_conn_iso_stream *cis_lll); -static inline void lll_flush_rx(struct lll_conn_iso_stream *cis_lll); static int prepare_cb(struct lll_prepare_param *p); static void abort_cb(struct lll_prepare_param *prepare_param, void *param); static void isr_tx(void *param); static void isr_rx(void *param); static void isr_prepare_subevent(void *param); static void isr_done(void *param); +static void payload_count_flush(struct lll_conn_iso_stream *cis_lll); +static void payload_count_flush_or_inc_on_close(struct lll_conn_iso_stream *cis_lll); +static void payload_count_lazy_update(struct lll_conn_iso_stream *cis_lll, uint16_t lazy); static uint16_t next_cis_chan_remap_idx; static uint16_t next_cis_chan_prn_s; @@ -118,24 +119,6 @@ static int init_reset(void) return 0; } -static inline void lll_flush_tx(struct lll_conn_iso_stream *cis_lll) -{ - /* sn and nesn are 1-bit, only Least Significant bit is needed */ - uint8_t sn_update = cis_lll->tx.bn + 1U - cis_lll->tx.bn_curr; - - /* we'll re-use sn_update when implementing flush timeout */ - cis_lll->sn += sn_update; -} - -static inline void lll_flush_rx(struct lll_conn_iso_stream *cis_lll) -{ - /* sn and nesn are 1-bit, only Least Significant bit is needed */ - uint8_t nesn_update = cis_lll->rx.bn + 1U - cis_lll->rx.bn_curr; - - /* we'll re-use sn_update when implementing flush timeout */ - cis_lll->nesn += nesn_update; -} - static int prepare_cb(struct lll_prepare_param *p) { struct lll_conn_iso_group *cig_lll = p->param; @@ -197,30 +180,16 @@ static int prepare_cb(struct lll_prepare_param *p) /* Reset accumulated latencies */ cig_lll->latency_prepare = 0U; - /* Adjust the SN and NESN for skipped CIG events */ - if (cis_lll->event_count) { - uint16_t cis_lazy; - - if (lazy > cis_lll->event_count) { - cis_lazy = lazy - cis_lll->event_count; - } else { - cis_lazy = lazy; - } - - /* sn and nesn are 1-bit, only Least Significant bit is needed */ - cis_lll->sn += cis_lll->tx.bn * cis_lazy; - cis_lll->nesn += cis_lll->rx.bn * cis_lazy; - } - se_curr = 1U; - cis_lll->tx.bn_curr = 1U; - cis_lll->rx.bn_curr = 1U; + + /* Adjust the SN and NESN for skipped CIG events */ + payload_count_lazy_update(cis_lll, lazy); /* Start setting up of Radio h/w */ radio_reset(); #if defined(CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL) - radio_tx_power_set(cis_lll->tx_pwr_lvl); + radio_tx_power_set(conn_lll->tx_pwr_lvl); #else /* !CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL */ radio_tx_power_set(RADIO_TXP_DEFAULT); #endif /* !CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL */ @@ -248,7 +217,8 @@ static int prepare_cb(struct lll_prepare_param *p) struct node_tx_iso *node_tx; memq_link_t *link; - payload_count = cis_lll->event_count * cis_lll->tx.bn; + payload_count = cis_lll->tx.payload_count + + cis_lll->tx.bn_curr - 1U; do { link = memq_peek(cis_lll->memq_tx.head, @@ -399,33 +369,12 @@ static int prepare_cb(struct lll_prepare_param *p) do { cis_lll = ull_conn_iso_lll_stream_get_by_group(cig_lll, &cis_handle); if (cis_lll && cis_lll->active) { - if (cis_lll->event_count) { - uint16_t cis_lazy; + /* Adjust sn and nesn for skipped CIG events */ + payload_count_lazy_update(cis_lll, lazy); - if (lazy > cis_lll->event_count) { - cis_lazy = lazy - cis_lll->event_count; - } else { - cis_lazy = lazy; - } - - /* sn and nesn are 1-bit, only Least Significant bit is needed */ - cis_lll->sn += cis_lll->tx.bn * cis_lazy; - cis_lll->nesn += cis_lll->rx.bn * cis_lazy; - - /* Adjust sn and nesn for canceled events */ - if (err) { - /* Adjust sn when flushing Tx */ - /* FIXME: When Flush Timeout is implemented */ - if (cis_lll->tx.bn_curr <= cis_lll->tx.bn) { - lll_flush_tx(cis_lll); - } - - /* Adjust nesn when flushing Rx */ - /* FIXME: When Flush Timeout is implemented */ - if (cis_lll->rx.bn_curr <= cis_lll->rx.bn) { - lll_flush_rx(cis_lll); - } - } + /* Adjust sn and nesn for canceled events */ + if (err) { + payload_count_flush_or_inc_on_close(cis_lll); } } } while (cis_lll); @@ -450,9 +399,21 @@ static void abort_cb(struct lll_prepare_param *prepare_param, void *param) /* NOTE: This is not a prepare being cancelled */ if (!prepare_param) { + struct lll_conn_iso_stream *next_cis_lll; struct lll_conn_iso_stream *cis_lll; + struct lll_conn_iso_group *cig_lll; cis_lll = ull_conn_iso_lll_stream_get(cis_handle_curr); + cig_lll = param; + + /* Adjust the SN, NESN and payload_count on abort for CISes */ + do { + next_cis_lll = ull_conn_iso_lll_stream_get_by_group(cig_lll, + &cis_handle_curr); + if (next_cis_lll && next_cis_lll->active) { + payload_count_flush_or_inc_on_close(next_cis_lll); + } + } while (next_cis_lll); /* Perform event abort here. * After event has been cleanly aborted, clean up resources @@ -488,14 +449,17 @@ static void isr_tx(void *param) /* Get reference to CIS LLL context */ cis_lll = param; + /* Acquire rx node for reception */ + node_rx = ull_iso_pdu_rx_alloc_peek(1U); + LL_ASSERT(node_rx); + #if defined(CONFIG_BT_CTLR_LE_ENC) /* Get reference to ACL context */ const struct lll_conn *conn_lll = ull_conn_lll_get(cis_lll->acl_handle); #endif /* CONFIG_BT_CTLR_LE_ENC */ - /* Acquire rx node for reception */ - node_rx = ull_iso_pdu_rx_alloc_peek(1U); - LL_ASSERT(node_rx); + /* PHY */ + radio_phy_set(cis_lll->rx.phy, PHY_FLAGS_S8); /* Encryption */ if (false) { @@ -505,8 +469,8 @@ static void isr_tx(void *param) uint64_t payload_count; uint8_t pkt_flags; - payload_count = (cis_lll->event_count * cis_lll->rx.bn) + - (cis_lll->rx.bn_curr - 1U); + payload_count = cis_lll->rx.payload_count + + cis_lll->rx.bn_curr - 1U; cis_lll->rx.ccm.counter = payload_count; @@ -649,7 +613,9 @@ static void isr_tx(void *param) cis_lll = next_cis_lll; /* Tx Ack stale ISO Data */ - payload_count = cis_lll->event_count * cis_lll->tx.bn; + payload_count = cis_lll->tx.payload_count + + cis_lll->tx.bn_curr - 1U; + do { link = memq_peek(cis_lll->memq_tx.head, cis_lll->memq_tx.tail, @@ -684,8 +650,6 @@ static void isr_tx(void *param) static void isr_rx(void *param) { struct lll_conn_iso_stream *cis_lll; - struct node_rx_pdu *node_rx; - struct pdu_cis *pdu_rx; uint8_t ack_pending; uint8_t trx_done; uint8_t crc_ok; @@ -710,7 +674,25 @@ static void isr_rx(void *param) cis_lll = param; /* No Rx */ - if (!trx_done) { + if (!trx_done || +#if defined(CONFIG_TEST_FT_CEN_SKIP_SUBEVENTS) + /* Used by test code, + * to skip a number of events in every 3 event count when current subevent is less than + * or equal to 2 or when current subevent has completed all its NSE number of subevents. + * OR + * to skip a (number + 1) of events in every 3 event count when current subevent is less + * than or equal to 1 or when current subevent has completed all its NSE number of + * subevents. + */ + ((((cis_lll->event_count % 3U) < CONFIG_TEST_FT_CEN_SKIP_EVENTS_COUNT) && + ((se_curr > cis_lll->nse) || (se_curr <= 2U))) || + + (((cis_lll->event_count % 3U) < (CONFIG_TEST_FT_CEN_SKIP_EVENTS_COUNT + 1U)) && + ((se_curr > cis_lll->nse) || (se_curr <= 1U)))) || +#endif /* CONFIG_TEST_FT_CEN_SKIP_SUBEVENTS */ + false) { + payload_count_flush(cis_lll); + goto isr_rx_next_subevent; } @@ -720,23 +702,23 @@ static void isr_rx(void *param) /* Set the bit corresponding to CIS index */ trx_performed_bitmask |= (1U << LL_CIS_IDX_FROM_HANDLE(cis_lll->handle)); - /* Get reference to received PDU */ - node_rx = ull_iso_pdu_rx_alloc_peek(1U); - LL_ASSERT(node_rx); + if (crc_ok) { + struct node_rx_pdu *node_rx; + struct pdu_cis *pdu_rx; - pdu_rx = (void *)node_rx->pdu; + /* Get reference to received PDU */ + node_rx = ull_iso_pdu_rx_alloc_peek(1U); + LL_ASSERT(node_rx); + pdu_rx = (void *)node_rx->pdu; - if (crc_ok) { /* Tx ACK */ - if (pdu_rx->nesn != cis_lll->sn) { - /* Increment sequence number */ + if ((pdu_rx->nesn != cis_lll->sn) && (cis_lll->tx.bn_curr <= cis_lll->tx.bn)) { cis_lll->sn++; - - /* Increment burst number */ - if (cis_lll->tx.bn_curr <= cis_lll->tx.bn) { - - cis_lll->tx.bn_curr++; - + cis_lll->tx.bn_curr++; + if ((cis_lll->tx.bn_curr > cis_lll->tx.bn) && + ((cis_lll->tx.payload_count / cis_lll->tx.bn) < cis_lll->event_count)) { + cis_lll->tx.payload_count += cis_lll->tx.bn; + cis_lll->tx.bn_curr = 1U; } /* TODO: Implement early Tx Ack. Currently Tx Ack @@ -750,9 +732,9 @@ static void isr_rx(void *param) (cis_lll->rx.bn_curr <= cis_lll->rx.bn) && (pdu_rx->sn == cis_lll->nesn) && ull_iso_pdu_rx_alloc_peek(2U)) { + struct lll_conn_iso_group *cig_lll; struct node_rx_iso_meta *iso_meta; - /* Increment next expected sequence number */ cis_lll->nesn++; #if defined(CONFIG_BT_CTLR_LE_ENC) @@ -783,12 +765,15 @@ static void isr_rx(void *param) node_rx->hdr.type = NODE_RX_TYPE_ISO_PDU; node_rx->hdr.handle = cis_lll->handle; iso_meta = &node_rx->hdr.rx_iso_meta; - iso_meta->payload_number = (cis_lll->event_count * - cis_lll->rx.bn) + - (cis_lll->rx.bn_curr - 1U); + iso_meta->payload_number = cis_lll->rx.payload_count + + cis_lll->rx.bn_curr - 1U; iso_meta->timestamp = HAL_TICKER_TICKS_TO_US(radio_tmr_start_get()) + radio_tmr_ready_restore(); + cig_lll = ull_conn_iso_lll_group_get_by_stream(cis_lll); + iso_meta->timestamp -= (cis_lll->event_count - + (cis_lll->rx.payload_count / cis_lll->rx.bn)) * + cig_lll->iso_interval_us; iso_meta->timestamp %= HAL_TICKER_TICKS_TO_US(BIT(HAL_TICKER_CNTR_MSBIT + 1U)); iso_meta->status = 0U; @@ -800,36 +785,23 @@ static void isr_rx(void *param) iso_rx_sched(); #endif /* CONFIG_BT_CTLR_LOW_LAT_ULL */ - /* Increment burst number */ cis_lll->rx.bn_curr++; + if ((cis_lll->rx.bn_curr > cis_lll->rx.bn) && + ((cis_lll->rx.payload_count / cis_lll->rx.bn) < cis_lll->event_count)) { + cis_lll->rx.payload_count += cis_lll->rx.bn; + cis_lll->rx.bn_curr = 1U; + } /* Need to be acked */ ack_pending = 1U; - - /* Handle NULL PDU indication received */ - } else if (pdu_rx->npi) { - /* Source could not send ISO data, increment NESN as if - * we received and expect to receive the next PDU in the - * burst. - */ - if (cis_lll->rx.bn_curr <= cis_lll->rx.bn) { - /* Increment next expected serial number */ - cis_lll->nesn++; - - /* Increment burst number */ - cis_lll->rx.bn_curr++; - } - - /* Not NPI, or more than the BN, or no free Rx ISO PDU buffers. - */ - } else { - /* Do nothing, ignore the Rx buffer */ } /* Close Isochronous Event */ cie = cie || pdu_rx->cie; } + payload_count_flush(cis_lll); + /* Close Isochronous Event */ cie = cie || ((cis_lll->rx.bn_curr > cis_lll->rx.bn) && (cis_lll->tx.bn_curr > cis_lll->tx.bn) && @@ -837,12 +809,11 @@ static void isr_rx(void *param) isr_rx_next_subevent: if (cie || (se_curr == cis_lll->nse)) { - struct lll_conn_iso_stream *old_cis_lll; struct lll_conn_iso_stream *next_cis_lll; + struct lll_conn_iso_stream *old_cis_lll; struct lll_conn_iso_group *cig_lll; struct lll_conn *next_conn_lll; uint8_t phy; - uint8_t bn; /* Fetch next CIS */ /* TODO: Use a new ull_conn_iso_lll_stream_get_active_by_group() @@ -858,18 +829,6 @@ static void isr_rx(void *param) goto isr_rx_done; } - /* Adjust sn when flushing Tx */ - /* FIXME: When Flush Timeout is implemented */ - if (cis_lll->tx.bn_curr <= cis_lll->tx.bn) { - lll_flush_tx(cis_lll); - } - - /* Adjust nesn when flushing Rx */ - /* FIXME: When Flush Timeout is implemented */ - if (cis_lll->rx.bn_curr <= cis_lll->rx.bn) { - lll_flush_rx(cis_lll); - } - /* Get reference to ACL context */ next_conn_lll = ull_conn_lll_get(next_cis_lll->acl_handle); @@ -903,7 +862,8 @@ static void isr_rx(void *param) old_cis_lll = cis_lll; cis_lll = next_cis_lll; - payload_count = cis_lll->event_count * cis_lll->tx.bn; + payload_count = cis_lll->tx.payload_count + + cis_lll->tx.bn_curr - 1U; do { link = memq_peek(cis_lll->memq_tx.head, @@ -937,53 +897,13 @@ static void isr_rx(void *param) cis_lll = old_cis_lll; } - /* Generate ISO Data Invalid Status */ - bn = cis_lll->rx.bn_curr; - while (bn <= cis_lll->rx.bn) { - struct node_rx_iso_meta *iso_meta; - struct node_rx_pdu *status_node_rx; - - /* Ensure there is always one free for reception - * of ISO PDU by the radio h/w DMA, hence peek - * for two available ISO PDU when using one for - * generating invalid ISO data. - */ - status_node_rx = ull_iso_pdu_rx_alloc_peek(2U); - if (!status_node_rx) { - break; - } - - status_node_rx->hdr.type = NODE_RX_TYPE_ISO_PDU; - status_node_rx->hdr.handle = cis_lll->handle; - iso_meta = &status_node_rx->hdr.rx_iso_meta; - iso_meta->payload_number = (cis_lll->event_count * - cis_lll->rx.bn) + (bn - 1U); - iso_meta->timestamp = - HAL_TICKER_TICKS_TO_US(radio_tmr_start_get()) + - radio_tmr_ready_restore(); - iso_meta->timestamp %= - HAL_TICKER_TICKS_TO_US(BIT(HAL_TICKER_CNTR_MSBIT + 1U)); - iso_meta->status = 1U; - - ull_iso_pdu_rx_alloc(); - iso_rx_put(status_node_rx->hdr.link, status_node_rx); - - bn++; - } - -#if !defined(CONFIG_BT_CTLR_LOW_LAT_ULL) - if (bn != cis_lll->rx.bn_curr) { - iso_rx_sched(); - } -#endif /* CONFIG_BT_CTLR_LOW_LAT_ULL */ + payload_count_flush_or_inc_on_close(cis_lll); /* Reset indices for the next CIS */ se_curr = 0U; /* isr_prepare_subevent() will increase se_curr */ - next_cis_lll->tx.bn_curr = 1U; - next_cis_lll->rx.bn_curr = 1U; #if defined(CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL) - radio_tx_power_set(next_cis_lll->tx_pwr_lvl); + radio_tx_power_set(next_conn_lll->tx_pwr_lvl); #else radio_tx_power_set(RADIO_TXP_DEFAULT); #endif @@ -1039,8 +959,7 @@ static void isr_prepare_subevent(void *param) memq_link_t *link; payload_index = cis_lll->tx.bn_curr - 1U; - payload_count = cis_lll->event_count * cis_lll->tx.bn + - payload_index; + payload_count = cis_lll->tx.payload_count + payload_index; link = memq_peek_n(cis_lll->memq_tx.head, cis_lll->memq_tx.tail, payload_index, (void **)&node_tx); @@ -1087,6 +1006,9 @@ static void isr_prepare_subevent(void *param) const struct lll_conn *conn_lll = ull_conn_lll_get(cis_lll->acl_handle); #endif /* CONFIG_BT_CTLR_LE_ENC */ + /* PHY */ + radio_phy_set(cis_lll->tx.phy, cis_lll->tx.phy_flags); + /* Encryption */ if (false) { @@ -1170,74 +1092,211 @@ static void isr_done(void *param) { struct lll_conn_iso_stream *cis_lll; struct event_done_extra *e; - uint8_t bn; lll_isr_status_reset(); /* Get reference to CIS LLL context */ cis_lll = param; - /* Adjust sn when flushing Tx */ - /* FIXME: When Flush Timeout is implemented */ - if (cis_lll->tx.bn_curr <= cis_lll->tx.bn) { - lll_flush_tx(cis_lll); + payload_count_flush_or_inc_on_close(cis_lll); + + e = ull_event_done_extra_get(); + LL_ASSERT(e); + + e->type = EVENT_DONE_EXTRA_TYPE_CIS; + e->trx_performed_bitmask = trx_performed_bitmask; + e->crc_valid = 1U; + +#if defined(CONFIG_BT_CTLR_LE_ENC) + e->mic_state = mic_state; +#endif /* CONFIG_BT_CTLR_LE_ENC */ + + lll_isr_cleanup(param); +} + +static void payload_count_flush(struct lll_conn_iso_stream *cis_lll) +{ + if (cis_lll->tx.bn) { + uint64_t payload_count; + uint8_t u; + + payload_count = cis_lll->tx.payload_count + cis_lll->tx.bn_curr - 1U; + u = cis_lll->nse - ((cis_lll->nse / cis_lll->tx.bn) * + (cis_lll->tx.bn - 1U - + (payload_count % cis_lll->tx.bn))); + if ((((cis_lll->tx.payload_count / cis_lll->tx.bn) + cis_lll->tx.ft) == + (cis_lll->event_count + 1U)) && (u <= se_curr) && + (((cis_lll->tx.bn_curr < cis_lll->tx.bn) && + ((cis_lll->tx.payload_count / cis_lll->tx.bn) <= cis_lll->event_count)) || + ((cis_lll->tx.bn_curr == cis_lll->tx.bn) && + ((cis_lll->tx.payload_count / cis_lll->tx.bn) < cis_lll->event_count)))) { + /* sn and nesn are 1-bit, only Least Significant bit is needed */ + cis_lll->sn++; + cis_lll->tx.bn_curr++; + if (cis_lll->tx.bn_curr > cis_lll->tx.bn) { + cis_lll->tx.payload_count += cis_lll->tx.bn; + cis_lll->tx.bn_curr = 1U; + } + } } - /* Adjust nesn when flushing Rx */ - /* FIXME: When Flush Timeout is implemented */ - if (cis_lll->rx.bn_curr <= cis_lll->rx.bn) { - lll_flush_rx(cis_lll); + if (cis_lll->rx.bn) { + uint64_t payload_count; + uint8_t u; + + payload_count = cis_lll->rx.payload_count + cis_lll->rx.bn_curr - 1U; + u = cis_lll->nse - ((cis_lll->nse / cis_lll->rx.bn) * + (cis_lll->rx.bn - 1U - + (payload_count % cis_lll->rx.bn))); + if ((((cis_lll->rx.payload_count / cis_lll->rx.bn) + cis_lll->rx.ft) == + (cis_lll->event_count + 1U)) && (u <= se_curr) && + (((cis_lll->rx.bn_curr < cis_lll->rx.bn) && + ((cis_lll->rx.payload_count / cis_lll->rx.bn) <= cis_lll->event_count)) || + ((cis_lll->rx.bn_curr == cis_lll->rx.bn) && + ((cis_lll->rx.payload_count / cis_lll->rx.bn) < cis_lll->event_count)))) { + /* sn and nesn are 1-bit, only Least Significant bit is needed */ + cis_lll->nesn++; + cis_lll->rx.bn_curr++; + if (cis_lll->rx.bn_curr > cis_lll->rx.bn) { + cis_lll->rx.payload_count += cis_lll->rx.bn; + cis_lll->rx.bn_curr = 1U; + } + } } +} - /* Generate ISO Data Invalid Status */ - bn = cis_lll->rx.bn_curr; - while (bn <= cis_lll->rx.bn) { - struct node_rx_iso_meta *iso_meta; - struct node_rx_pdu *node_rx; +static void payload_count_flush_or_inc_on_close(struct lll_conn_iso_stream *cis_lll) +{ + if (cis_lll->tx.bn) { + uint64_t payload_count; + uint8_t u; - /* Ensure there is always one free for reception of ISO PDU by - * the radio h/w DMA, hence peek for two available ISO PDU when - * using one for generating invalid ISO data. - */ - node_rx = ull_iso_pdu_rx_alloc_peek(2U); - if (!node_rx) { - break; + if (((cis_lll->tx.payload_count / cis_lll->tx.bn) + cis_lll->tx.bn_curr) > + (cis_lll->event_count + cis_lll->tx.bn)) { + cis_lll->tx.payload_count += cis_lll->tx.bn; + cis_lll->tx.bn_curr = 1U; + + goto payload_count_flush_or_inc_on_close_rx; } - node_rx->hdr.type = NODE_RX_TYPE_ISO_PDU; - node_rx->hdr.handle = cis_lll->handle; - iso_meta = &node_rx->hdr.rx_iso_meta; - iso_meta->payload_number = (cis_lll->event_count * - cis_lll->rx.bn) + (bn - 1U); - iso_meta->timestamp = - HAL_TICKER_TICKS_TO_US(radio_tmr_start_get()) + - radio_tmr_ready_restore(); - iso_meta->timestamp %= - HAL_TICKER_TICKS_TO_US(BIT(HAL_TICKER_CNTR_MSBIT + 1U)); - iso_meta->status = 1U; - - ull_iso_pdu_rx_alloc(); - iso_rx_put(node_rx->hdr.link, node_rx); - - bn++; + payload_count = cis_lll->tx.payload_count + cis_lll->tx.bn_curr - 1U; + u = cis_lll->nse - ((cis_lll->nse / cis_lll->tx.bn) * + (cis_lll->tx.bn - 1U - + (payload_count % cis_lll->tx.bn))); + while ((((cis_lll->tx.payload_count / cis_lll->tx.bn) + cis_lll->tx.ft) < + (cis_lll->event_count + 1U)) || + ((((cis_lll->tx.payload_count / cis_lll->tx.bn) + cis_lll->tx.ft) == + (cis_lll->event_count + 1U)) && (u <= (cis_lll->nse + 1U)))) { + /* sn and nesn are 1-bit, only Least Significant bit is needed */ + cis_lll->sn++; + cis_lll->tx.bn_curr++; + if (cis_lll->tx.bn_curr > cis_lll->tx.bn) { + cis_lll->tx.payload_count += cis_lll->tx.bn; + cis_lll->tx.bn_curr = 1U; + } + + payload_count = cis_lll->tx.payload_count + cis_lll->tx.bn_curr - 1U; + u = cis_lll->nse - ((cis_lll->nse / cis_lll->tx.bn) * + (cis_lll->tx.bn - 1U - + (payload_count % cis_lll->tx.bn))); + } } -#if !defined(CONFIG_BT_CTLR_LOW_LAT_ULL) - if (bn != cis_lll->rx.bn_curr) { - iso_rx_sched(); +payload_count_flush_or_inc_on_close_rx: + if (cis_lll->rx.bn) { + uint64_t payload_count; + uint8_t u; + + if (((cis_lll->rx.payload_count / cis_lll->rx.bn) + cis_lll->rx.bn_curr) > + (cis_lll->event_count + cis_lll->rx.bn)) { + cis_lll->rx.payload_count += cis_lll->rx.bn; + cis_lll->rx.bn_curr = 1U; + + return; + } + + payload_count = cis_lll->rx.payload_count + cis_lll->rx.bn_curr - 1U; + u = cis_lll->nse - ((cis_lll->nse / cis_lll->rx.bn) * + (cis_lll->rx.bn - 1U - + (payload_count % cis_lll->rx.bn))); + while ((((cis_lll->rx.payload_count / cis_lll->rx.bn) + cis_lll->rx.ft) < + (cis_lll->event_count + 1U)) || + ((((cis_lll->rx.payload_count / cis_lll->rx.bn) + cis_lll->rx.ft) == + (cis_lll->event_count + 1U)) && (u <= (cis_lll->nse + 1U)))) { + /* sn and nesn are 1-bit, only Least Significant bit is needed */ + cis_lll->nesn++; + cis_lll->rx.bn_curr++; + if (cis_lll->rx.bn_curr > cis_lll->rx.bn) { + cis_lll->rx.payload_count += cis_lll->rx.bn; + cis_lll->rx.bn_curr = 1U; + } + + payload_count = cis_lll->rx.payload_count + cis_lll->rx.bn_curr - 1U; + u = cis_lll->nse - ((cis_lll->nse / cis_lll->rx.bn) * + (cis_lll->rx.bn - 1U - + (payload_count % cis_lll->rx.bn))); + } } -#endif /* CONFIG_BT_CTLR_LOW_LAT_ULL */ +} - e = ull_event_done_extra_get(); - LL_ASSERT(e); +static void payload_count_lazy_update(struct lll_conn_iso_stream *cis_lll, uint16_t lazy) +{ + if (cis_lll->tx.bn) { + uint16_t tx_lazy; - e->type = EVENT_DONE_EXTRA_TYPE_CIS; - e->trx_performed_bitmask = trx_performed_bitmask; - e->crc_valid = 1U; + tx_lazy = lazy; + while (tx_lazy--) { + uint64_t payload_count; + uint8_t u; + + payload_count = cis_lll->tx.payload_count + cis_lll->tx.bn_curr - 1U; + u = cis_lll->nse - ((cis_lll->nse / cis_lll->tx.bn) * + (cis_lll->tx.bn - 1U - + (payload_count % cis_lll->tx.bn))); + while (((cis_lll->tx.payload_count / cis_lll->tx.bn) + cis_lll->tx.ft) < + (cis_lll->event_count + 1U)) { + /* sn and nesn are 1-bit, only Least Significant bit is needed */ + cis_lll->sn++; + cis_lll->tx.bn_curr++; + if (cis_lll->tx.bn_curr > cis_lll->tx.bn) { + cis_lll->tx.payload_count += cis_lll->tx.bn; + cis_lll->tx.bn_curr = 1U; + } -#if defined(CONFIG_BT_CTLR_LE_ENC) - e->mic_state = mic_state; -#endif /* CONFIG_BT_CTLR_LE_ENC */ + payload_count = cis_lll->tx.payload_count + + cis_lll->tx.bn_curr - 1U; + u = cis_lll->nse - ((cis_lll->nse / cis_lll->tx.bn) * + (cis_lll->tx.bn - 1U - + (payload_count % cis_lll->tx.bn))); + } + } + } - lll_isr_cleanup(param); + if (cis_lll->rx.bn) { + while (lazy--) { + uint64_t payload_count; + uint8_t u; + + payload_count = cis_lll->rx.payload_count + cis_lll->rx.bn_curr - 1U; + u = cis_lll->nse - ((cis_lll->nse / cis_lll->rx.bn) * + (cis_lll->rx.bn - 1U - + (payload_count % cis_lll->rx.bn))); + while (((cis_lll->rx.payload_count / cis_lll->rx.bn) + cis_lll->rx.ft) < + (cis_lll->event_count + 1U)) { + /* sn and nesn are 1-bit, only Least Significant bit is needed */ + cis_lll->nesn++; + cis_lll->rx.bn_curr++; + if (cis_lll->rx.bn_curr > cis_lll->rx.bn) { + cis_lll->rx.payload_count += cis_lll->rx.bn; + cis_lll->rx.bn_curr = 1U; + } + + payload_count = cis_lll->rx.payload_count + + cis_lll->rx.bn_curr - 1U; + u = cis_lll->nse - ((cis_lll->nse / cis_lll->rx.bn) * + (cis_lll->rx.bn - 1U - + (payload_count % cis_lll->rx.bn))); + } + } + } } diff --git a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_peripheral_iso.c b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_peripheral_iso.c index 4796696ce73..27ed4d1e42c 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_peripheral_iso.c +++ b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_peripheral_iso.c @@ -49,8 +49,9 @@ static void isr_prepare_subevent(void *param); static void isr_prepare_subevent_next_cis(void *param); static void isr_prepare_subevent_common(void *param); static void isr_done(void *param); -static inline void lll_flush_tx(struct lll_conn_iso_stream *cis_lll); -static inline void lll_flush_rx(struct lll_conn_iso_stream *cis_lll); +static void payload_count_flush(struct lll_conn_iso_stream *cis_lll); +static void payload_count_rx_flush_or_txrx_inc(struct lll_conn_iso_stream *cis_lll); +static void payload_count_lazy(struct lll_conn_iso_stream *cis_lll, uint16_t lazy); static uint8_t next_chan_use; static uint16_t data_chan_id; @@ -61,7 +62,6 @@ static uint32_t trx_performed_bitmask; static uint16_t cis_offset_first; static uint16_t cis_handle_curr; static uint8_t se_curr; -static uint8_t has_tx; #if defined(CONFIG_BT_CTLR_LE_ENC) static uint8_t mic_state; @@ -135,24 +135,6 @@ static int init_reset(void) return 0; } -static inline void lll_flush_tx(struct lll_conn_iso_stream *cis_lll) -{ - /* sn and nesn are 1-bit, only Least Significant bit is needed */ - uint8_t sn_update = cis_lll->tx.bn + 1U - cis_lll->tx.bn_curr; - - /* TODO we'll re-use sn_update when implementing flush timeout */ - cis_lll->sn += sn_update; -} - -static inline void lll_flush_rx(struct lll_conn_iso_stream *cis_lll) -{ - /* sn and nesn are 1-bit, only Least Significant bit is needed */ - uint8_t nesn_update = cis_lll->rx.bn + 1U - cis_lll->rx.bn_curr; - - /* TODO we'll re-use nesn_update when implementing flush timeout */ - cis_lll->nesn += nesn_update; -} - static int prepare_cb(struct lll_prepare_param *p) { struct lll_conn_iso_group *cig_lll = p->param; @@ -225,26 +207,22 @@ static int prepare_cb(struct lll_prepare_param *p) EVENT_US_TO_US_FRAC(cig_lll->window_widening_max_us); } - /* Adjust sn and nesn for skipped CIG events */ - /* sn and nesn are 1-bit, only Least Significant bit is needed */ - cis_lll->sn += (cis_lll->tx.bn * lazy); - cis_lll->nesn += cis_lll->rx.bn * lazy; - se_curr = 1U; - cis_lll->rx.bn_curr = 1U; - has_tx = 0U; + + /* Adjust sn and nesn for skipped CIG events */ + payload_count_lazy(cis_lll, lazy); /* Start setting up of Radio h/w */ radio_reset(); #if defined(CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL) - radio_tx_power_set(cis_lll->tx_pwr_lvl); + radio_tx_power_set(conn_lll->tx_pwr_lvl); #else /* !CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL */ radio_tx_power_set(RADIO_TXP_DEFAULT); #endif /* !CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL */ phy = cis_lll->rx.phy; - radio_phy_set(phy, cis_lll->rx.phy_flags); + radio_phy_set(phy, PHY_FLAGS_S8); radio_aa_set(cis_lll->access_addr); radio_crc_configure(PDU_CRC_POLYNOMIAL, sys_get_le24(conn_lll->crc_init)); lll_chan_set(data_chan_use); @@ -260,8 +238,9 @@ static int prepare_cb(struct lll_prepare_param *p) uint64_t payload_cnt; uint8_t pkt_flags; - payload_cnt = (cis_lll->event_count * cis_lll->rx.bn) + - (cis_lll->rx.bn_curr - 1U); + payload_cnt = cis_lll->rx.payload_count + + cis_lll->rx.bn_curr - 1U; + cis_lll->rx.ccm.counter = payload_cnt; pkt_flags = RADIO_PKT_CONF_FLAGS(RADIO_PKT_CONF_PDU_TYPE_CIS, @@ -372,9 +351,10 @@ static int prepare_cb(struct lll_prepare_param *p) /* Adjust the SN and NESN for skipped CIG events */ uint16_t cis_handle = cis_handle_curr; - while (true) { - /* FIXME: Update below implementation when supporting Flush Timeout */ - payload_count = cis_lll->event_count * cis_lll->tx.bn; + do { + payload_count = cis_lll->tx.payload_count + + cis_lll->tx.bn_curr - 1U; + do { link = memq_peek(cis_lll->memq_tx.head, cis_lll->memq_tx.tail, (void **)&tx); @@ -392,26 +372,22 @@ static int prepare_cb(struct lll_prepare_param *p) } } while (link); - cis_lll = ull_conn_iso_lll_stream_get_by_group(cig_lll, &cis_handle); + do { + cis_lll = ull_conn_iso_lll_stream_get_by_group(cig_lll, &cis_handle); + } while (cis_lll && !cis_lll->active); + if (!cis_lll) { break; } - if (cis_lll->active) { - /* sn and nesn are 1-bit, only Least Significant bit is needed */ - cis_lll->sn += cis_lll->tx.bn * lazy; - cis_lll->nesn += cis_lll->rx.bn * lazy; - - /* Adjust sn and nesn for canceled events */ - if (err) { - /* Adjust nesn when flushing Rx */ - /* FIXME: When Flush Timeout is implemented */ - if (cis_lll->rx.bn_curr <= cis_lll->rx.bn) { - lll_flush_rx(cis_lll); - } - } + /* Adjust sn and nesn for skipped CIG events */ + payload_count_lazy(cis_lll, lazy); + + /* Adjust sn and nesn for canceled events */ + if (err) { + payload_count_rx_flush_or_txrx_inc(cis_lll); } - }; + } while (cis_lll); /* Return if prepare callback cancelled */ if (err) { @@ -433,24 +409,21 @@ static void abort_cb(struct lll_prepare_param *prepare_param, void *param) /* NOTE: This is not a prepare being cancelled */ if (!prepare_param) { - struct lll_conn_iso_group *cig_lll = param; + struct lll_conn_iso_stream *next_cis_lll; struct lll_conn_iso_stream *cis_lll; + struct lll_conn_iso_group *cig_lll; - cis_lll = ull_conn_iso_lll_stream_get_by_group(cig_lll, NULL); - - /* FIXME: Consider Flush Timeout when resetting current burst number */ - if (!has_tx) { - has_tx = 1U; + cis_lll = ull_conn_iso_lll_stream_get(cis_handle_curr); + cig_lll = param; - /* Adjust nesn when flushing Tx */ - /* FIXME: When Flush Timeout is implemented */ - if (cis_lll->tx.bn_curr <= cis_lll->tx.bn) { - lll_flush_tx(cis_lll); + /* Adjust the SN, NESN and payload_count on abort for CISes */ + do { + next_cis_lll = ull_conn_iso_lll_stream_get_by_group(cig_lll, + &cis_handle_curr); + if (next_cis_lll && next_cis_lll->active) { + payload_count_rx_flush_or_txrx_inc(next_cis_lll); } - - /* Set to last burst number in previous event */ - cis_lll->tx.bn_curr = cis_lll->tx.bn; - } + } while (next_cis_lll); /* Perform event abort here. * After event has been cleanly aborted, clean up resources @@ -499,21 +472,25 @@ static void isr_rx(void *param) cis_lll = param; /* No Rx */ - if (!trx_done) { - /* FIXME: Consider Flush Timeout when resetting current burst number */ - if (!has_tx) { - has_tx = 1U; - - /* Adjust nesn when flushing Tx */ - /* FIXME: When Flush Timeout is implemented */ - if (cis_lll->tx.bn_curr <= cis_lll->tx.bn) { - lll_flush_tx(cis_lll); - } - - /* Start transmitting new burst */ - cis_lll->tx.bn_curr = cis_lll->tx.bn; - } - + if (!trx_done || +#if defined(CONFIG_TEST_FT_PER_SKIP_SUBEVENTS) + /* Used by test code, + * to skip a number of events in every 3 event count when current subevent is less than + * or equal to 2 or when current subevent has completed all its NSE number of subevents. + * OR + * to skip a (number + 1) of events in every 3 event count when current subevent is less + * than or equal to 1 or when current subevent has completed all its NSE number of + * subevents. + */ + ((((cis_lll->event_count % 3U) < CONFIG_TEST_FT_PER_SKIP_EVENTS_COUNT) && + ((se_curr > cis_lll->nse) || (se_curr <= 2U))) || + (((cis_lll->event_count % 3U) < (CONFIG_TEST_FT_PER_SKIP_EVENTS_COUNT + 1U)) && + ((se_curr > cis_lll->nse) || (se_curr <= 1U)))) || +#endif + false) { + payload_count_flush(cis_lll); + + /* Next subevent or next CIS */ if (se_curr < cis_lll->nse) { radio_isr_set(isr_prepare_subevent, param); } else { @@ -562,17 +539,20 @@ static void isr_rx(void *param) pdu_rx = (void *)node_rx->pdu; /* Tx ACK */ - if (pdu_rx->nesn != cis_lll->sn) { - /* Increment sequence number */ + if ((pdu_rx->nesn != cis_lll->sn) && (cis_lll->tx.bn_curr <= cis_lll->tx.bn)) { cis_lll->sn++; - - /* Increment burst number */ - if (cis_lll->tx.bn_curr <= cis_lll->tx.bn) { - cis_lll->tx.bn_curr++; + cis_lll->tx.bn_curr++; + if ((cis_lll->tx.bn_curr > cis_lll->tx.bn) && + ((cis_lll->tx.payload_count / cis_lll->tx.bn) < + cis_lll->event_count)) { + cis_lll->tx.payload_count += cis_lll->tx.bn; + cis_lll->tx.bn_curr = 1U; } - /* TODO: Tx Ack */ - + /* TODO: Implement early Tx Ack. Currently Tx Ack + * generated as stale Tx Ack when payload count + * has elapsed. + */ } /* Handle valid ISO data Rx */ @@ -580,9 +560,9 @@ static void isr_rx(void *param) (cis_lll->rx.bn_curr <= cis_lll->rx.bn) && (pdu_rx->sn == cis_lll->nesn) && ull_iso_pdu_rx_alloc_peek(2U)) { + struct lll_conn_iso_group *cig_lll; struct node_rx_iso_meta *iso_meta; - /* Increment next expected sequence number */ cis_lll->nesn++; #if defined(CONFIG_BT_CTLR_LE_ENC) @@ -614,13 +594,16 @@ static void isr_rx(void *param) node_rx->hdr.type = NODE_RX_TYPE_ISO_PDU; node_rx->hdr.handle = cis_lll->handle; iso_meta = &node_rx->hdr.rx_iso_meta; - iso_meta->payload_number = (cis_lll->event_count * - cis_lll->rx.bn) + - (cis_lll->rx.bn_curr - 1U); + iso_meta->payload_number = cis_lll->rx.payload_count + + cis_lll->rx.bn_curr - 1U; iso_meta->timestamp = cis_lll->offset + HAL_TICKER_TICKS_TO_US(radio_tmr_start_get()) + radio_tmr_aa_restore() - cis_offset_first - addr_us_get(cis_lll->rx.phy); + cig_lll = ull_conn_iso_lll_group_get_by_stream(cis_lll); + iso_meta->timestamp -= (cis_lll->event_count - + (cis_lll->rx.payload_count / cis_lll->rx.bn)) * + cig_lll->iso_interval_us; iso_meta->timestamp %= HAL_TICKER_TICKS_TO_US(BIT(HAL_TICKER_CNTR_MSBIT + 1U)); iso_meta->status = 0U; @@ -632,46 +615,19 @@ static void isr_rx(void *param) iso_rx_sched(); #endif /* CONFIG_BT_CTLR_LOW_LAT_ULL */ - /* Increment burst number */ cis_lll->rx.bn_curr++; - - /* Handle NULL PDU indication received */ - } else if (pdu_rx->npi) { - /* Source could not send ISO data, increment NESN as if - * we received and expect to receive the next PDU in the - * burst. - */ - if (cis_lll->rx.bn_curr <= cis_lll->rx.bn) { - /* Increment next expected serial number */ - cis_lll->nesn++; - - /* Increment burst number */ - cis_lll->rx.bn_curr++; + if ((cis_lll->rx.bn_curr > cis_lll->rx.bn) && + ((cis_lll->rx.payload_count / cis_lll->rx.bn) < cis_lll->event_count)) { + cis_lll->rx.payload_count += cis_lll->rx.bn; + cis_lll->rx.bn_curr = 1U; } - - /* Not NPI, or more than the BN, or no free Rx ISO PDU buffers. - */ - } else { - /* Do nothing, ignore the Rx buffer */ } /* Close Isochronous Event */ cie = cie || pdu_rx->cie; } - /* FIXME: Consider Flush Timeout when resetting current burst number */ - if (!has_tx) { - has_tx = 1U; - - /* Adjust nesn when flushing Tx */ - /* FIXME: When Flush Timeout is implemented */ - if (cis_lll->tx.bn_curr <= cis_lll->tx.bn) { - lll_flush_tx(cis_lll); - } - - /* Start transmitting new burst */ - cis_lll->tx.bn_curr = 1U; - } + payload_count_flush(cis_lll); /* Close Isochronous Event */ cie = cie || ((cis_lll->rx.bn_curr > cis_lll->rx.bn) && @@ -696,8 +652,7 @@ static void isr_rx(void *param) memq_link_t *link; payload_index = cis_lll->tx.bn_curr - 1U; - payload_count = cis_lll->event_count * cis_lll->tx.bn + - payload_index; + payload_count = cis_lll->tx.payload_count + payload_index; link = memq_peek_n(cis_lll->memq_tx.head, cis_lll->memq_tx.tail, payload_index, (void **)&tx); @@ -738,6 +693,9 @@ static void isr_rx(void *param) pdu_tx->rfu0 = 0U; pdu_tx->rfu1 = 0U; + /* PHY */ + radio_phy_set(cis_lll->tx.phy, cis_lll->tx.phy_flags); + /* Encryption */ if (false) { @@ -798,6 +756,7 @@ static void isr_rx(void *param) &data_chan_prn_s, &data_chan_remap_idx); } else { + struct lll_conn_iso_stream *next_cis_lll; struct lll_conn_iso_group *cig_lll; uint16_t event_counter; uint16_t cis_handle; @@ -806,34 +765,34 @@ static void isr_rx(void *param) cig_lll = ull_conn_iso_lll_group_get_by_stream(cis_lll); cis_handle = cis_handle_curr; do { - cis_lll = ull_conn_iso_lll_stream_get_by_group(cig_lll, &cis_handle); - } while (cis_lll && !cis_lll->active); + next_cis_lll = ull_conn_iso_lll_stream_get_by_group(cig_lll, &cis_handle); + } while (next_cis_lll && !next_cis_lll->active); - if (!cis_lll) { + if (!next_cis_lll) { /* ISO Event Done */ radio_isr_set(isr_done, param); return; } + payload_count_rx_flush_or_txrx_inc(cis_lll); + cis_handle_curr = cis_handle; /* Event counter value, 0-15 bit of cisEventCounter */ - event_counter = cis_lll->event_count; + event_counter = next_cis_lll->event_count; /* Calculate the radio channel to use for next CIS ISO event */ - data_chan_id = lll_chan_id(cis_lll->access_addr); + data_chan_id = lll_chan_id(next_cis_lll->access_addr); next_chan_use = lll_chan_iso_event(event_counter, data_chan_id, conn_lll->data_chan_map, conn_lll->data_chan_count, &data_chan_prn_s, &data_chan_remap_idx); - /* Reset indices for the next CIS */ - se_curr = 0U; /* isr_tx() will increase se_curr */ - cis_lll->tx.bn_curr = 1U; /* FIXME: may be this should be previous event value? */ - cis_lll->rx.bn_curr = 1U; - has_tx = 0U; + /* Next CIS, se_curr is incremented in isr_tx() */ + cis_lll = next_cis_lll; + se_curr = 0U; } /* Schedule next subevent reception */ @@ -880,6 +839,9 @@ static void isr_tx(void *param) const struct lll_conn *conn_lll = ull_conn_lll_get(cis_lll->acl_handle); #endif /* CONFIG_BT_CTLR_LE_ENC */ + /* PHY */ + radio_phy_set(cis_lll->rx.phy, PHY_FLAGS_S8); + /* Encryption */ if (false) { @@ -888,8 +850,9 @@ static void isr_tx(void *param) uint64_t payload_count; uint8_t pkt_flags; - payload_count = (cis_lll->event_count * cis_lll->rx.bn) + - (cis_lll->rx.bn_curr - 1U); + payload_count = cis_lll->rx.payload_count + + cis_lll->rx.bn_curr - 1U; + cis_lll->rx.ccm.counter = payload_count; pkt_flags = RADIO_PKT_CONF_FLAGS(RADIO_PKT_CONF_PDU_TYPE_CIS, @@ -990,6 +953,7 @@ static void isr_tx(void *param) static void next_cis_prepare(void *param) { + struct lll_conn_iso_stream *next_cis_lll; struct lll_conn_iso_stream *cis_lll; struct lll_conn_iso_group *cig_lll; uint16_t cis_handle; @@ -999,12 +963,13 @@ static void next_cis_prepare(void *param) /* Check for next active CIS */ cig_lll = ull_conn_iso_lll_group_get_by_stream(cis_lll); + next_cis_lll = cis_lll; cis_handle = cis_handle_curr; do { - cis_lll = ull_conn_iso_lll_stream_get_by_group(cig_lll, &cis_handle); - } while (cis_lll && !cis_lll->active); + next_cis_lll = ull_conn_iso_lll_stream_get_by_group(cig_lll, &cis_handle); + } while (next_cis_lll && !next_cis_lll->active); - if (!cis_lll) { + if (!next_cis_lll) { /* ISO Event Done */ radio_isr_set(isr_done, param); @@ -1013,7 +978,7 @@ static void next_cis_prepare(void *param) cis_handle_curr = cis_handle; - radio_isr_set(isr_prepare_subevent_next_cis, cis_lll); + radio_isr_set(isr_prepare_subevent_next_cis, next_cis_lll); } static void isr_prepare_subevent(void *param) @@ -1065,11 +1030,8 @@ static void isr_prepare_subevent_next_cis(void *param) &data_chan_prn_s, &data_chan_remap_idx); - /* Reset indices for the next CIS */ - se_curr = 0U; /* isr_prepare_subevent_common() will increase se_curr */ - cis_lll->tx.bn_curr = 1U; /* FIXME: may be this should be previous event value? */ - cis_lll->rx.bn_curr = 1U; - has_tx = 0U; + /* se_curr is incremented in isr_prepare_subevent_common() */ + se_curr = 0U; isr_prepare_subevent_common(param); } @@ -1094,6 +1056,9 @@ static void isr_prepare_subevent_common(void *param) const struct lll_conn *conn_lll = ull_conn_lll_get(cis_lll->acl_handle); #endif /* CONFIG_BT_CTLR_LE_ENC */ + /* PHY */ + radio_phy_set(cis_lll->rx.phy, PHY_FLAGS_S8); + /* Encryption */ if (false) { @@ -1102,8 +1067,9 @@ static void isr_prepare_subevent_common(void *param) uint64_t payload_count; uint8_t pkt_flags; - payload_count = (cis_lll->event_count * cis_lll->rx.bn) + - (cis_lll->rx.bn_curr - 1U); + payload_count = cis_lll->rx.payload_count + + cis_lll->rx.bn_curr - 1U; + cis_lll->rx.ccm.counter = payload_count; pkt_flags = RADIO_PKT_CONF_FLAGS(RADIO_PKT_CONF_PDU_TYPE_CIS, @@ -1220,60 +1186,13 @@ static void isr_done(void *param) { struct lll_conn_iso_stream *cis_lll; struct event_done_extra *e; - uint8_t bn; lll_isr_status_reset(); /* Get reference to CIS LLL context */ cis_lll = param; - /* Adjust nesn when flushing Rx */ - /* FIXME: When Flush Timeout is implemented */ - if (cis_lll->rx.bn_curr <= cis_lll->rx.bn) { - lll_flush_rx(cis_lll); - } - - /* Generate ISO Data Invalid Status */ - bn = cis_lll->rx.bn_curr; - while (bn <= cis_lll->rx.bn) { - struct node_rx_iso_meta *iso_meta; - struct node_rx_pdu *node_rx; - - node_rx = ull_iso_pdu_rx_alloc_peek(2U); - if (!node_rx) { - break; - } - - node_rx->hdr.type = NODE_RX_TYPE_ISO_PDU; - node_rx->hdr.handle = cis_lll->handle; - iso_meta = &node_rx->hdr.rx_iso_meta; - iso_meta->payload_number = (cis_lll->event_count * - cis_lll->rx.bn) + (bn - 1U); - if (trx_performed_bitmask) { - iso_meta->timestamp = cis_lll->offset + - HAL_TICKER_TICKS_TO_US(radio_tmr_start_get()) + - radio_tmr_aa_restore() - cis_offset_first - - addr_us_get(cis_lll->rx.phy); - } else { - iso_meta->timestamp = cis_lll->offset + - HAL_TICKER_TICKS_TO_US(radio_tmr_start_get()) + - radio_tmr_ready_restore() - cis_offset_first; - } - iso_meta->timestamp %= - HAL_TICKER_TICKS_TO_US(BIT(HAL_TICKER_CNTR_MSBIT + 1U)); - iso_meta->status = 1U; - - ull_iso_pdu_rx_alloc(); - iso_rx_put(node_rx->hdr.link, node_rx); - - bn++; - } - -#if !defined(CONFIG_BT_CTLR_LOW_LAT_ULL) - if (bn != cis_lll->rx.bn_curr) { - iso_rx_sched(); - } -#endif /* CONFIG_BT_CTLR_LOW_LAT_ULL */ + payload_count_rx_flush_or_txrx_inc(cis_lll); e = ull_event_done_extra_get(); LL_ASSERT(e); @@ -1309,3 +1228,177 @@ static void isr_done(void *param) lll_isr_cleanup(param); } + +static void payload_count_flush(struct lll_conn_iso_stream *cis_lll) +{ + if (cis_lll->tx.bn) { + uint64_t payload_count; + uint8_t u; + + payload_count = cis_lll->tx.payload_count + cis_lll->tx.bn_curr - 1U; + u = cis_lll->nse - ((cis_lll->nse / cis_lll->tx.bn) * + (cis_lll->tx.bn - 1U - + (payload_count % cis_lll->tx.bn))); + while (((((cis_lll->tx.payload_count / cis_lll->tx.bn) + cis_lll->tx.ft) < + (cis_lll->event_count + 1U)) || + ((((cis_lll->tx.payload_count / cis_lll->tx.bn) + cis_lll->tx.ft) == + (cis_lll->event_count + 1U)) && (u < se_curr))) && + (((cis_lll->tx.bn_curr < cis_lll->tx.bn) && + ((cis_lll->tx.payload_count / cis_lll->tx.bn) <= cis_lll->event_count)) || + ((cis_lll->tx.bn_curr == cis_lll->tx.bn) && + ((cis_lll->tx.payload_count / cis_lll->tx.bn) < cis_lll->event_count)))) { + /* sn and nesn are 1-bit, only Least Significant bit is needed */ + cis_lll->sn++; + cis_lll->tx.bn_curr++; + if (cis_lll->tx.bn_curr > cis_lll->tx.bn) { + cis_lll->tx.payload_count += cis_lll->tx.bn; + cis_lll->tx.bn_curr = 1U; + } + + payload_count = cis_lll->tx.payload_count + cis_lll->tx.bn_curr - 1U; + u = cis_lll->nse - ((cis_lll->nse / cis_lll->tx.bn) * + (cis_lll->tx.bn - 1U - + (payload_count % cis_lll->tx.bn))); + } + } + + if (cis_lll->rx.bn) { + uint64_t payload_count; + uint8_t u; + + payload_count = cis_lll->rx.payload_count + cis_lll->rx.bn_curr - 1U; + u = cis_lll->nse - ((cis_lll->nse / cis_lll->rx.bn) * + (cis_lll->rx.bn - 1U - + (payload_count % cis_lll->rx.bn))); + if ((((cis_lll->rx.payload_count / cis_lll->rx.bn) + cis_lll->rx.ft) == + (cis_lll->event_count + 1U)) && (u <= se_curr) && + (((cis_lll->rx.bn_curr < cis_lll->rx.bn) && + ((cis_lll->rx.payload_count / cis_lll->rx.bn) <= cis_lll->event_count)) || + ((cis_lll->rx.bn_curr == cis_lll->rx.bn) && + ((cis_lll->rx.payload_count / cis_lll->rx.bn) < cis_lll->event_count)))) { + /* sn and nesn are 1-bit, only Least Significant bit is needed */ + cis_lll->nesn++; + cis_lll->rx.bn_curr++; + if (cis_lll->rx.bn_curr > cis_lll->rx.bn) { + cis_lll->rx.payload_count += cis_lll->rx.bn; + cis_lll->rx.bn_curr = 1U; + } + } + } +} + +static void payload_count_rx_flush_or_txrx_inc(struct lll_conn_iso_stream *cis_lll) +{ + if (cis_lll->tx.bn) { + if (((cis_lll->tx.payload_count / cis_lll->tx.bn) + cis_lll->tx.bn_curr) > + (cis_lll->event_count + cis_lll->tx.bn)) { + cis_lll->tx.payload_count += cis_lll->tx.bn; + cis_lll->tx.bn_curr = 1U; + } + } + + if (cis_lll->rx.bn) { + uint64_t payload_count; + uint8_t u; + + if (((cis_lll->rx.payload_count / cis_lll->rx.bn) + cis_lll->rx.bn_curr) > + (cis_lll->event_count + cis_lll->rx.bn)) { + cis_lll->rx.payload_count += cis_lll->rx.bn; + cis_lll->rx.bn_curr = 1U; + + return; + } + + payload_count = cis_lll->rx.payload_count + cis_lll->rx.bn_curr - 1U; + u = cis_lll->nse - ((cis_lll->nse / cis_lll->rx.bn) * + (cis_lll->rx.bn - 1U - + (payload_count % cis_lll->rx.bn))); + while ((((cis_lll->rx.payload_count / cis_lll->rx.bn) + cis_lll->rx.ft) < + (cis_lll->event_count + 1U)) || + ((((cis_lll->rx.payload_count / cis_lll->rx.bn) + cis_lll->rx.ft) == + (cis_lll->event_count + 1U)) && (u <= (cis_lll->nse + 1U)))) { + /* sn and nesn are 1-bit, only Least Significant bit is needed */ + cis_lll->nesn++; + cis_lll->rx.bn_curr++; + if (cis_lll->rx.bn_curr > cis_lll->rx.bn) { + cis_lll->rx.payload_count += cis_lll->rx.bn; + cis_lll->rx.bn_curr = 1U; + } + + payload_count = cis_lll->rx.payload_count + cis_lll->rx.bn_curr - 1U; + u = cis_lll->nse - ((cis_lll->nse / cis_lll->rx.bn) * + (cis_lll->rx.bn - 1U - + (payload_count % cis_lll->rx.bn))); + } + } +} + +static void payload_count_lazy(struct lll_conn_iso_stream *cis_lll, uint16_t lazy) +{ + if (cis_lll->tx.bn && lazy) { + uint16_t tx_lazy; + + tx_lazy = lazy; + while (tx_lazy--) { + uint64_t payload_count; + uint8_t u; + + payload_count = cis_lll->tx.payload_count + cis_lll->tx.bn_curr - 1U; + u = cis_lll->nse - ((cis_lll->nse / cis_lll->tx.bn) * + (cis_lll->tx.bn - 1U - + (payload_count % cis_lll->tx.bn))); + while (((((cis_lll->tx.payload_count / cis_lll->tx.bn) + cis_lll->tx.ft) < + (cis_lll->event_count + 1U)) || + ((((cis_lll->tx.payload_count / cis_lll->tx.bn) + cis_lll->tx.ft) == + (cis_lll->event_count + 1U)) && (u < (cis_lll->nse + 1U)))) && + ((cis_lll->tx.payload_count / cis_lll->tx.bn) < + cis_lll->event_count)) { + /* sn and nesn are 1-bit, only Least Significant bit is needed */ + cis_lll->sn++; + cis_lll->tx.bn_curr++; + if (cis_lll->tx.bn_curr > cis_lll->tx.bn) { + cis_lll->tx.payload_count += cis_lll->tx.bn; + cis_lll->tx.bn_curr = 1U; + } + + payload_count = cis_lll->tx.payload_count + + cis_lll->tx.bn_curr - 1U; + u = cis_lll->nse - ((cis_lll->nse / cis_lll->tx.bn) * + (cis_lll->tx.bn - 1U - + (payload_count % cis_lll->tx.bn))); + } + } + } + + if (cis_lll->rx.bn) { + while (lazy--) { + uint64_t payload_count; + uint8_t u; + + payload_count = cis_lll->rx.payload_count + cis_lll->rx.bn_curr - 1U; + u = cis_lll->nse - ((cis_lll->nse / cis_lll->rx.bn) * + (cis_lll->rx.bn - 1U - + (payload_count % cis_lll->rx.bn))); + while (((((cis_lll->rx.payload_count / cis_lll->rx.bn) + cis_lll->rx.ft) < + (cis_lll->event_count + 1U)) || + ((((cis_lll->rx.payload_count / cis_lll->rx.bn) + cis_lll->rx.ft) == + (cis_lll->event_count + 1U)) && (u <= (cis_lll->nse + 1U)))) && + ((cis_lll->rx.payload_count / cis_lll->rx.bn) < + cis_lll->event_count)) { + /* sn and nesn are 1-bit, only Least Significant bit is needed */ + cis_lll->nesn++; + cis_lll->rx.bn_curr++; + if (cis_lll->rx.bn_curr > cis_lll->rx.bn) { + cis_lll->rx.payload_count += cis_lll->rx.bn; + cis_lll->rx.bn_curr = 1U; + } + + payload_count = cis_lll->rx.payload_count + + cis_lll->rx.bn_curr - 1U; + u = cis_lll->nse - ((cis_lll->nse / cis_lll->rx.bn) * + (cis_lll->rx.bn - 1U - + (payload_count % cis_lll->rx.bn))); + } + } + } +} diff --git a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_scan_aux.c b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_scan_aux.c index 50bcb0e6662..c8188d87cb6 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_scan_aux.c +++ b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_scan_aux.c @@ -1510,7 +1510,7 @@ static void isr_rx_connect_rsp(void *param) rx = ftr->extra; rx->hdr.type = NODE_RX_TYPE_RELEASE; - goto isr_rx_do_close; + goto isr_rx_connect_rsp_do_close; } /* Update the max Tx and Rx time; and connection PHY based on the @@ -1548,7 +1548,7 @@ static void isr_rx_connect_rsp(void *param) } #endif /* CONFIG_BT_CTLR_PRIVACY */ -isr_rx_do_close: +isr_rx_connect_rsp_do_close: if (IS_ENABLED(CONFIG_BT_CTLR_PROFILE_ISR)) { lll_prof_cputime_capture(); } diff --git a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_sync_iso.c b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_sync_iso.c index 5089c6ccde7..12e9fd342b9 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_sync_iso.c +++ b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_sync_iso.c @@ -489,7 +489,6 @@ static void isr_rx(void *param) uint8_t access_addr[4]; uint16_t data_chan_id; uint8_t data_chan_use; - uint8_t payload_index; uint8_t crc_init[3]; uint8_t rssi_ready; uint32_t start_us; @@ -556,6 +555,8 @@ static void isr_rx(void *param) if (crc_ok) { struct lll_sync_iso_stream *sync_stream; uint16_t stream_handle; + uint8_t payload_offset; + uint8_t payload_index; struct pdu_bis *pdu; /* Check if Control Subevent being received */ @@ -593,21 +594,28 @@ static void isr_rx(void *param) /* TODO: check same CSSN is used in every subevent */ } - /* calculate the payload index in the sliding window */ - payload_index = lll->payload_tail + (lll->bn_curr - 1U) + - (lll->ptc_curr * lll->pto); + /* Check payload buffer overflow */ + payload_offset = (lll->bn_curr - 1U) + + (lll->ptc_curr * lll->pto); + if (payload_offset > lll->payload_count_max) { + goto isr_rx_done; + } + + /* Calculate the payload index in the sliding window */ + payload_index = lll->payload_tail + payload_offset; if (payload_index >= lll->payload_count_max) { payload_index -= lll->payload_count_max; } + /* Get reference to stream context */ stream_handle = lll->stream_handle[lll->stream_curr]; sync_stream = ull_sync_iso_lll_stream_get(stream_handle); - /* store the received PDU */ + /* Store the received PDU if selected stream and not already + * received (say in previous event as pre-transmitted PDU. + */ if ((lll->bis_curr == sync_stream->bis_index) && pdu->len && - !lll->payload[bis_idx][payload_index] && - ((payload_index >= lll->payload_tail) || - (payload_index < lll->payload_head))) { + !lll->payload[bis_idx][payload_index]) { uint16_t handle; if (lll->enc) { @@ -644,10 +652,23 @@ static void isr_rx(void *param) /* Find the next (bn_curr)th subevent to receive PDU */ while (lll->bn_curr < lll->bn) { + uint8_t payload_offset; + uint8_t payload_index; + + /* Next burst number to check for reception required */ lll->bn_curr++; + /* Check payload buffer overflow */ + payload_offset = (lll->bn_curr - 1U); + if (payload_offset > lll->payload_count_max) { + /* (bn_curr)th Rx PDU skip subevent */ + skipped++; + + continue; + } + /* Find the index of the (bn_curr)th Rx PDU buffer */ - payload_index = lll->payload_tail + (lll->bn_curr - 1U); + payload_index = lll->payload_tail + payload_offset; if (payload_index >= lll->payload_count_max) { payload_index -= lll->payload_count_max; } @@ -667,6 +688,11 @@ static void isr_rx(void *param) /* Find the next repetition (irc_curr)th subevent to receive PDU */ if (lll->irc_curr < lll->irc) { if (!new_burst) { + uint8_t payload_index; + + /* Increment to next repetition count and be at first + * burst count for it. + */ lll->bn_curr = 1U; lll->irc_curr++; @@ -715,6 +741,10 @@ static void isr_rx(void *param) if (lll->ptc_curr < lll->ptc) { lll->ptc_curr++; + /* TODO: optimize to skip pre-transmission subevent in case + * of insufficient buffers in sliding window. + */ + /* Receive the (ptc_curr)th Rx PDU of bis_curr */ bis = lll->bis_curr; @@ -733,6 +763,7 @@ static void isr_rx(void *param) stream_handle = lll->stream_handle[lll->stream_curr]; sync_stream = ull_sync_iso_lll_stream_get(stream_handle); if (sync_stream->bis_index <= lll->num_bis) { + uint8_t payload_index; uint8_t bis_idx_new; lll->bis_curr = sync_stream->bis_index; @@ -1058,8 +1089,7 @@ static void isr_rx_done(void *param) bn = lll->bn; while (bn--) { if (lll->payload[bis_idx][payload_tail]) { - node_rx = - lll->payload[bis_idx][payload_tail]; + node_rx = lll->payload[bis_idx][payload_tail]; lll->payload[bis_idx][payload_tail] = NULL; iso_rx_put(node_rx->hdr.link, node_rx); diff --git a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_vendor.h b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_vendor.h index 5ee063e4d53..db5f8e3f1d8 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_vendor.h +++ b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_vendor.h @@ -21,28 +21,28 @@ /* Active connection in peripheral role with extended scanning on 1M and Coded * PHY, scheduling and receiving auxiliary PDUs. */ -#define EVENT_OVERHEAD_START_US 458 +#define EVENT_OVERHEAD_START_US 733 /* 24 RTC ticks */ #else /* !CONFIG_BT_CTLR_PHY_CODED */ /* Active connection in peripheral role with extended scanning on 1M only, * scheduling and receiving auxiliary PDUs. */ -#define EVENT_OVERHEAD_START_US 428 +#define EVENT_OVERHEAD_START_US 428 /* 14 RTC ticks */ #endif /* !CONFIG_BT_CTLR_PHY_CODED */ #else /* !CONFIG_BT_OBSERVER */ /* Active connection in peripheral role with legacy scanning on 1M. */ -#define EVENT_OVERHEAD_START_US 275 +#define EVENT_OVERHEAD_START_US 275 /* 9 RTC ticks */ #endif /* !CONFIG_BT_OBSERVER */ #else /* !CONFIG_BT_CTLR_ADV_EXT */ /* Active connection in peripheral role with additional advertising state. */ -#define EVENT_OVERHEAD_START_US 275 +#define EVENT_OVERHEAD_START_US 275 /* 9 RTC ticks */ #endif /* !CONFIG_BT_CTLR_ADV_EXT */ /* Worst-case time margin needed after event end-time in the air * (done/preempt race margin + power-down/chain delay) */ -#define EVENT_OVERHEAD_END_US 100 +#define EVENT_OVERHEAD_END_US 40 /* Sleep Clock Accuracy */ #define EVENT_JITTER_US 16 diff --git a/subsys/bluetooth/controller/ll_sw/openisa/hal/RV32M1/radio/radio.c b/subsys/bluetooth/controller/ll_sw/openisa/hal/RV32M1/radio/radio.c index 6107bfc9c68..ecdf17bbe43 100644 --- a/subsys/bluetooth/controller/ll_sw/openisa/hal/RV32M1/radio/radio.c +++ b/subsys/bluetooth/controller/ll_sw/openisa/hal/RV32M1/radio/radio.c @@ -684,7 +684,7 @@ void radio_whiten_iv_set(uint32_t iv) GENFSK->WHITEN_SZ_THR |= GENFSK_WHITEN_SZ_THR_WHITEN_SZ_THR(0); } -void radio_aa_set(uint8_t *aa) +void radio_aa_set(const uint8_t *aa) { /* Configure Access Address detection using NETWORK ADDRESS 0 */ GENFSK->NTW_ADR_0 = *((uint32_t *)aa); diff --git a/subsys/bluetooth/controller/ll_sw/openisa/hal/RV32M1/radio/radio.h b/subsys/bluetooth/controller/ll_sw/openisa/hal/RV32M1/radio/radio.h index 1168804bbd5..cc3fda99964 100644 --- a/subsys/bluetooth/controller/ll_sw/openisa/hal/RV32M1/radio/radio.h +++ b/subsys/bluetooth/controller/ll_sw/openisa/hal/RV32M1/radio/radio.h @@ -20,7 +20,7 @@ void radio_tx_power_set(uint32_t power); void radio_tx_power_max_set(void); void radio_freq_chan_set(uint32_t chan); void radio_whiten_iv_set(uint32_t iv); -void radio_aa_set(uint8_t *aa); +void radio_aa_set(const uint8_t *aa); void radio_pkt_configure(uint8_t bits_len, uint8_t max_len, uint8_t flags); void radio_pkt_rx_set(void *rx_packet); void radio_pkt_tx_set(void *tx_packet); diff --git a/subsys/bluetooth/controller/ll_sw/openisa/hal/RV32M1/ticker.h b/subsys/bluetooth/controller/ll_sw/openisa/hal/RV32M1/ticker.h index 642ca1e608f..ab9b14207c1 100644 --- a/subsys/bluetooth/controller/ll_sw/openisa/hal/RV32M1/ticker.h +++ b/subsys/bluetooth/controller/ll_sw/openisa/hal/RV32M1/ticker.h @@ -5,43 +5,72 @@ * SPDX-License-Identifier: Apache-2.0 */ -#define HAL_TICKER_CNTR_CLK_FREQ_HZ 32768U +#define HAL_TICKER_CNTR_CLK_FREQ_HZ 32768U +#define HAL_TICKER_CNTR_CLK_UNIT_FSEC 30517578125UL +#define HAL_TICKER_FSEC_PER_USEC 1000000000UL +#define HAL_TICKER_PSEC_PER_USEC 1000000UL +#define HAL_TICKER_FSEC_PER_PSEC 1000UL +/* Macro defining the minimum counter compare offset */ #define HAL_TICKER_CNTR_CMP_OFFSET_MIN 2 +/* Macro defining the max. counter update latency in ticks */ #define HAL_TICKER_CNTR_SET_LATENCY 1 + +/* Macro defines the h/w supported most significant bit */ +#define HAL_TICKER_CNTR_MSBIT 31 + +/* Macro defining the HW supported counter bits */ +#define HAL_TICKER_CNTR_MASK 0xFFFFFFFF + /* * When the LPTMR is enabled, the first increment will take an additional * one or two prescaler clock cycles due to synchronization logic. */ +/* Macro to translate microseconds to tick units. + * NOTE: This returns the floor value. + */ #define HAL_TICKER_US_TO_TICKS(x) \ ( \ - ((uint32_t)(((uint64_t) (x) * 1000000000UL) / 30517578125UL)) \ - & HAL_TICKER_CNTR_MASK \ + ((uint32_t)(((uint64_t) (x) * HAL_TICKER_FSEC_PER_USEC) / \ + HAL_TICKER_CNTR_CLK_UNIT_FSEC)) & \ + HAL_TICKER_CNTR_MASK \ ) -#define HAL_TICKER_REMAINDER(x) \ +/* Macro to translate microseconds to tick units. + * NOTE: This returns the ceil value. + */ +#define HAL_TICKER_US_TO_TICKS_CEIL(x) \ ( \ - ( \ - ((uint64_t) (x) * 1000000000UL) \ - - ((uint64_t)HAL_TICKER_US_TO_TICKS(x) * 30517578125UL) \ - ) \ - / 1000UL \ + DIV_ROUND_UP(((uint64_t) (x) * HAL_TICKER_FSEC_PER_USEC), \ + HAL_TICKER_CNTR_CLK_UNIT_FSEC) & \ + HAL_TICKER_CNTR_MASK \ ) +/* Macro to translate tick units to microseconds. */ #define HAL_TICKER_TICKS_TO_US(x) \ - ((uint32_t)(((uint64_t)(x) * 30517578125UL) / 1000000000UL)) - -#define HAL_TICKER_CNTR_MSBIT 31 + ( \ + ((uint32_t)(((uint64_t)(x) * HAL_TICKER_CNTR_CLK_UNIT_FSEC) / \ + HAL_TICKER_FSEC_PER_USEC)) \ + ) -#define HAL_TICKER_CNTR_MASK 0xFFFFFFFF +/* Macro returning remainder in picoseconds (to fit in 32-bits) */ +#define HAL_TICKER_REMAINDER(x) \ + ( \ + ( \ + ((uint64_t) (x) * HAL_TICKER_FSEC_PER_USEC) \ + - ((uint64_t)HAL_TICKER_US_TO_TICKS(x) * \ + HAL_TICKER_CNTR_CLK_UNIT_FSEC) \ + ) \ + / HAL_TICKER_FSEC_PER_PSEC \ + ) /* Macro defining the remainder resolution/range * ~ 1000000 * HAL_TICKER_TICKS_TO_US(1) */ #define HAL_TICKER_REMAINDER_RANGE \ - HAL_TICKER_TICKS_TO_US(1000000) + HAL_TICKER_TICKS_TO_US(HAL_TICKER_PSEC_PER_USEC) /* Macro defining the margin for positioning re-scheduled nodes */ #define HAL_TICKER_RESCHEDULE_MARGIN \ diff --git a/subsys/bluetooth/controller/ll_sw/pdu.h b/subsys/bluetooth/controller/ll_sw/pdu.h index ceeb9ca12e7..93e50e1ebcc 100644 --- a/subsys/bluetooth/controller/ll_sw/pdu.h +++ b/subsys/bluetooth/controller/ll_sw/pdu.h @@ -1018,8 +1018,8 @@ struct pdu_iso_sdu_sh { uint8_t len; /* Note, timeoffset only available in first segment of sdu */ - uint32_t payload:8; uint32_t timeoffset:24; + uint32_t payload:8; #endif /* CONFIG_LITTLE_ENDIAN */ } __packed; diff --git a/subsys/bluetooth/controller/ll_sw/ull.c b/subsys/bluetooth/controller/ll_sw/ull.c index 2f325f1a86b..3130978df4e 100644 --- a/subsys/bluetooth/controller/ll_sw/ull.c +++ b/subsys/bluetooth/controller/ll_sw/ull.c @@ -44,6 +44,7 @@ #include "lll_sync_iso.h" #include "lll_iso_tx.h" #include "lll_conn.h" +#include "lll_conn_iso.h" #include "lll_df.h" #include "ull_adv_types.h" @@ -60,6 +61,7 @@ #endif /* CONFIG_BT_CTLR_USER_EXT */ #include "isoal.h" +#include "ll_feat_internal.h" #include "ull_internal.h" #include "ull_iso_internal.h" #include "ull_adv_internal.h" @@ -69,7 +71,6 @@ #include "ull_central_internal.h" #include "ull_iso_types.h" #include "ull_conn_internal.h" -#include "lll_conn_iso.h" #include "ull_conn_iso_types.h" #include "ull_central_iso_internal.h" #include "ull_llcp.h" @@ -277,22 +278,21 @@ #define TICKER_USER_ULL_LOW_OPS (1 + TICKER_USER_ULL_LOW_VENDOR_OPS + 1) -/* NOTE: When ULL_LOW priority is configured to lower than ULL_HIGH, then extra - * ULL_HIGH operations queue elements are required to buffer the - * requested ticker operations. +/* NOTE: Extended Advertising needs one extra ticker operation being enqueued + * for scheduling the auxiliary PDU reception while there can already + * be three other operations being enqueued. + * + * This value also covers the case were initiator with 1M and Coded PHY + * scan window is stopping the two scan tickers, stopping one scan stop + * ticker and starting one new ticker for establishing an ACL connection. */ -#if defined(CONFIG_BT_CENTRAL) && defined(CONFIG_BT_CTLR_ADV_EXT) && \ - defined(CONFIG_BT_CTLR_PHY_CODED) +#if defined(CONFIG_BT_CTLR_ADV_EXT) #define TICKER_USER_ULL_HIGH_OPS (4 + TICKER_USER_ULL_HIGH_VENDOR_OPS + \ TICKER_USER_ULL_HIGH_FLASH_OPS + 1) -#else /* !CONFIG_BT_CENTRAL || !CONFIG_BT_CTLR_ADV_EXT || - * !CONFIG_BT_CTLR_PHY_CODED - */ +#else /* !CONFIG_BT_CTLR_ADV_EXT */ #define TICKER_USER_ULL_HIGH_OPS (3 + TICKER_USER_ULL_HIGH_VENDOR_OPS + \ TICKER_USER_ULL_HIGH_FLASH_OPS + 1) -#endif /* !CONFIG_BT_CENTRAL || !CONFIG_BT_CTLR_ADV_EXT || - * !CONFIG_BT_CTLR_PHY_CODED - */ +#endif /* !CONFIG_BT_CTLR_ADV_EXT */ #define TICKER_USER_LLL_OPS (3 + TICKER_USER_LLL_VENDOR_OPS + 1) @@ -899,6 +899,10 @@ void ll_reset(void) LL_ASSERT(!err); #endif +#if defined(CONFIG_BT_CTLR_SET_HOST_FEATURE) + ll_feat_reset(); +#endif /* CONFIG_BT_CTLR_SET_HOST_FEATURE */ + /* clear static random address */ (void)ll_addr_set(1U, NULL); } @@ -1228,10 +1232,8 @@ void ll_rx_dequeue(void) /* FIXME: use the correct adv and scan set to get * enabled status bitmask */ - bm = (IS_ENABLED(CONFIG_BT_OBSERVER) && - (ull_scan_is_enabled(0) << 1)) | - (IS_ENABLED(CONFIG_BT_BROADCASTER) && - ull_adv_is_enabled(0)); + bm = (IS_ENABLED(CONFIG_BT_OBSERVER)?(ull_scan_is_enabled(0) << 1):0) | + (IS_ENABLED(CONFIG_BT_BROADCASTER)?ull_adv_is_enabled(0):0); if (!bm) { ull_filter_adv_scan_state_cb(0); @@ -2063,8 +2065,6 @@ void *ull_prepare_dequeue_iter(uint8_t *idx) void ull_prepare_dequeue(uint8_t caller_id) { - void *param_normal_head = NULL; - void *param_normal_next = NULL; void *param_resume_head = NULL; void *param_resume_next = NULL; struct lll_event *next; @@ -2105,41 +2105,31 @@ void ull_prepare_dequeue(uint8_t caller_id) /* The prepare element was not a resume event, it would * use the radio or was enqueued back into prepare * pipeline with a preempt timeout being set. - * - * Remember the first encountered and the next element - * in the prepare pipeline so that we do not infinitely - * loop through the resume events in prepare pipeline. */ if (!is_resume) { - if (!param_normal_head) { - param_normal_head = param; - } else if (!param_normal_next) { - param_normal_next = param; - } - } else { - if (!param_resume_head) { - param_resume_head = param; - } else if (!param_resume_next) { - param_resume_next = param; - } + break; + } + + /* Remember the first encountered resume and the next + * resume element in the prepare pipeline so that we do + * not infinitely loop through the resume events in + * prepare pipeline. + */ + if (!param_resume_head) { + param_resume_head = param; + } else if (!param_resume_next) { + param_resume_next = param; } /* Stop traversing the prepare pipeline when we reach - * back to the first or next event where we + * back to the first or next resume event where we * initially started processing the prepare pipeline. */ - if (!next->is_aborted && - ((!next->is_resume && - ((next->prepare_param.param == - param_normal_head) || - (next->prepare_param.param == - param_normal_next))) || - (next->is_resume && - !param_normal_next && - ((next->prepare_param.param == - param_resume_head) || - (next->prepare_param.param == - param_resume_next))))) { + if (next->is_resume && + ((next->prepare_param.param == + param_resume_head) || + (next->prepare_param.param == + param_resume_next))) { break; } } @@ -2582,18 +2572,10 @@ static uint8_t tx_cmplt_get(uint16_t *handle, uint8_t *first, uint8_t last) /* We must count each SDU HCI fragment */ tx_node = tx->node; if (IS_NODE_TX_PTR(tx_node)) { - if (IS_ADV_ISO_HANDLE(tx->handle)) { - /* FIXME: ADV_ISO shall be updated to - * use ISOAL for TX. Until then, assume - * 1 node equals 1 fragment. - */ - sdu_fragments = 1U; - } else { - /* We count each SDU fragment completed - * by this PDU. - */ - sdu_fragments = tx_node->sdu_fragments; - } + /* We count each SDU fragment completed + * by this PDU. + */ + sdu_fragments = tx_node->sdu_fragments; /* Replace node reference with fragments * count diff --git a/subsys/bluetooth/controller/ll_sw/ull_adv.c b/subsys/bluetooth/controller/ll_sw/ull_adv.c index 3797acdc541..b5595e5e9c2 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_adv.c +++ b/subsys/bluetooth/controller/ll_sw/ull_adv.c @@ -690,7 +690,7 @@ uint8_t ll_adv_params_set(uint16_t interval, uint8_t adv_type, lll_adv_data_reset(&adv->lll.scan_rsp); err = lll_adv_aux_data_init(&adv->lll.scan_rsp); if (err) { - return err; + return BT_HCI_ERR_MEM_CAPACITY_EXCEEDED; } pdu = lll_adv_scan_rsp_peek(&adv->lll); @@ -710,7 +710,7 @@ uint8_t ll_adv_params_set(uint16_t interval, uint8_t adv_type, lll_adv_data_reset(&adv->lll.scan_rsp); err = lll_adv_data_init(&adv->lll.scan_rsp); if (err) { - return err; + return BT_HCI_ERR_MEM_CAPACITY_EXCEEDED; } pdu = lll_adv_scan_rsp_peek(&adv->lll); @@ -926,7 +926,7 @@ uint8_t ll_adv_enable(uint8_t enable) err = lll_adv_data_init(&adv->lll.scan_rsp); if (err) { - return err; + return BT_HCI_ERR_MEM_CAPACITY_EXCEEDED; } pdu_scan = lll_adv_scan_rsp_peek(lll); @@ -1251,7 +1251,7 @@ uint8_t ll_adv_enable(uint8_t enable) HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_XTAL_US); adv->ull.ticks_preempt_to_start = HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_PREEMPT_MIN_US); - adv->ull.ticks_slot = HAL_TICKER_US_TO_TICKS(time_us); + adv->ull.ticks_slot = HAL_TICKER_US_TO_TICKS_CEIL(time_us); ticks_slot_offset = MAX(adv->ull.ticks_active_to_start, adv->ull.ticks_prepare_to_start); @@ -1873,7 +1873,7 @@ uint8_t ull_scan_rsp_set(struct ll_adv_set *adv, uint8_t len, err = lll_adv_data_init(&adv->lll.scan_rsp); if (err) { - return err; + return BT_HCI_ERR_MEM_CAPACITY_EXCEEDED; } prev = lll_adv_scan_rsp_peek(&adv->lll); @@ -2145,7 +2145,7 @@ uint8_t ull_adv_time_update(struct ll_adv_set *adv, struct pdu_adv *pdu, chan_map = lll->chan_map; chan_cnt = util_ones_count_get(&chan_map, sizeof(chan_map)); time_us = adv_time_get(pdu, pdu_scan, chan_cnt, phy, phy_flags); - time_ticks = HAL_TICKER_US_TO_TICKS(time_us); + time_ticks = HAL_TICKER_US_TO_TICKS_CEIL(time_us); #if !defined(CONFIG_BT_CTLR_JIT_SCHEDULING) uint32_t volatile ret_cb; diff --git a/subsys/bluetooth/controller/ll_sw/ull_adv_aux.c b/subsys/bluetooth/controller/ll_sw/ull_adv_aux.c index 907a69b05c7..2480c286e4b 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_adv_aux.c +++ b/subsys/bluetooth/controller/ll_sw/ull_adv_aux.c @@ -117,6 +117,7 @@ uint8_t ll_adv_aux_ad_data_set(uint8_t handle, uint8_t op, uint8_t frag_pref, uint8_t ad_len_overflow; uint8_t ad_len_chain; struct pdu_adv *pdu; + uint8_t ad_len = 0U; #endif /* CONFIG_BT_CTLR_ADV_AUX_PDU_LINK */ /* Get the advertising set instance */ @@ -301,7 +302,7 @@ uint8_t ll_adv_aux_ad_data_set(uint8_t handle, uint8_t op, uint8_t frag_pref, struct pdu_adv *pdu_chain_prev; struct pdu_adv *pdu_chain; uint16_t ad_len_total; - uint8_t ad_len_prev; + uint8_t ad_len_prev = 0U; /* Traverse to next set clear hdr data parameter */ val_ptr += sizeof(data); @@ -317,28 +318,22 @@ uint8_t ll_adv_aux_ad_data_set(uint8_t handle, uint8_t op, uint8_t frag_pref, ad_len_total = 0U; pdu_chain_prev = pdu_prev; pdu_chain = pdu; + /* make a copy of the previous chain, until we reach the end */ do { - /* Prepare for aux ptr field reference to be returned, hence - * second parameter will be for AD data field. - */ - *val_ptr = 0U; - (void)memset((void *)&val_ptr[ULL_ADV_HDR_DATA_DATA_PTR_OFFSET], - 0U, ULL_ADV_HDR_DATA_DATA_PTR_SIZE); + val_ptr = hdr_data; + *val_ptr++ = 0U; + (void)memset((void *)val_ptr, 0U, + ULL_ADV_HDR_DATA_DATA_PTR_SIZE); pdu_prev = pdu_chain_prev; pdu = pdu_chain; - /* Add Aux Ptr field if not already present */ err = ull_adv_aux_pdu_set_clear(adv, pdu_prev, pdu, - (ULL_ADV_PDU_HDR_FIELD_AD_DATA | - ULL_ADV_PDU_HDR_FIELD_AUX_PTR), - 0, hdr_data); - LL_ASSERT(!err || (err == BT_HCI_ERR_PACKET_TOO_LONG)); + ULL_ADV_PDU_HDR_FIELD_AD_DATA, + 0U, hdr_data); + ad_len_prev = hdr_data[ULL_ADV_HDR_DATA_LEN_OFFSET]; - /* Get PDUs previous AD data length */ - ad_len_prev = - hdr_data[ULL_ADV_HDR_DATA_AUX_PTR_PTR_OFFSET + - ULL_ADV_HDR_DATA_AUX_PTR_PTR_SIZE]; + LL_ASSERT(!err || (err == BT_HCI_ERR_PACKET_TOO_LONG)); /* Check of max supported AD data len */ ad_len_total += ad_len_prev; @@ -365,31 +360,10 @@ uint8_t ll_adv_aux_ad_data_set(uint8_t handle, uint8_t op, uint8_t frag_pref, (!pdu_chain_prev && !pdu_chain)); } while (pdu_chain_prev); - if (err == BT_HCI_ERR_PACKET_TOO_LONG) { - ad_len_overflow = - hdr_data[ULL_ADV_HDR_DATA_AUX_PTR_PTR_OFFSET + - ULL_ADV_HDR_DATA_AUX_PTR_PTR_SIZE + - ULL_ADV_HDR_DATA_DATA_PTR_OFFSET + - ULL_ADV_HDR_DATA_DATA_PTR_SIZE]; - - /* Prepare for aux ptr field reference to be returned, - * hence second parameter will be for AD data field. - * Fill it with reduced AD data length. - */ - *val_ptr = ad_len_prev - ad_len_overflow; - - /* AD data len in chain PDU */ - ad_len_chain = len; - - /* Proceed to add chain PDU */ - err = 0U; - } else { - /* No AD data overflow */ - ad_len_overflow = 0U; - - /* No AD data in chain PDU */ - ad_len_chain = 0U; - } + /* No AD data overflow */ + ad_len_overflow = 0U; + /* No AD data in chain PDU */ + ad_len_chain = 0U; } #else /* !CONFIG_BT_CTLR_ADV_AUX_PDU_LINK */ } else { @@ -411,8 +385,85 @@ uint8_t ll_adv_aux_ad_data_set(uint8_t handle, uint8_t op, uint8_t frag_pref, #if defined(CONFIG_BT_CTLR_ADV_AUX_PDU_LINK) if ((op == BT_HCI_LE_EXT_ADV_OP_INTERM_FRAG) || - (op == BT_HCI_LE_EXT_ADV_OP_LAST_FRAG) || - ad_len_overflow) { + (op == BT_HCI_LE_EXT_ADV_OP_LAST_FRAG)) { + /* in the previous step we duplicated the chain + * the next step is to append new data in the last existing pdu in the chain, + */ + + uint8_t chain_err = 0U; + + val_ptr = hdr_data; + *val_ptr++ = len; + (void)memcpy(val_ptr, &data, sizeof(data)); + + /* Append data to the last PDU */ + chain_err = ull_adv_aux_pdu_set_clear(adv, pdu_prev, pdu, + ULL_ADV_PDU_HDR_FIELD_AD_DATA_APPEND, + 0U, hdr_data); + + LL_ASSERT((!chain_err) || (chain_err == BT_HCI_ERR_PACKET_TOO_LONG)); + + /* FIXME: the code has become quite complex, an alternative and simpler + * implementation would be to first fill an array with all data that + * must be send, and create the chained PDUs from this array + */ + if (chain_err == BT_HCI_ERR_PACKET_TOO_LONG) { + /* We could not fit all the data, append as much as possible + * ad_len_overflow is how much overflows with the AUX ptr + */ + const uint16_t chain_add_fields = ULL_ADV_PDU_HDR_FIELD_AD_DATA_APPEND | + ULL_ADV_PDU_HDR_FIELD_AUX_PTR; + + val_ptr = hdr_data; + *val_ptr++ = len; + (void)memcpy(val_ptr, &data, sizeof(data)); + val_ptr += sizeof(data); + *val_ptr++ = len; + (void)memcpy(val_ptr, &data, sizeof(data)); + chain_err = ull_adv_aux_pdu_set_clear(adv, pdu_prev, pdu, + chain_add_fields, + 0U, hdr_data); + ad_len_chain = hdr_data[ULL_ADV_HDR_DATA_AUX_PTR_PTR_OFFSET + + ULL_ADV_HDR_DATA_AUX_PTR_PTR_SIZE + + ULL_ADV_HDR_DATA_DATA_PTR_OFFSET + + ULL_ADV_HDR_DATA_DATA_PTR_SIZE]; + + /* len is the total amount of datawe want to add + * ad_len_chain is the amount of data that does + * not fit in the current PDU + * the difference of the two is the amount that + * we can fit in the current PDU + */ + ad_len = len - ad_len_chain; + + val_ptr = hdr_data; + *val_ptr++ = ad_len; + (void)memcpy(val_ptr, &data, sizeof(data)); + val_ptr += sizeof(data); + *val_ptr++ = ad_len; + (void)memcpy(val_ptr, &data, sizeof(data)); + + /* we now know how much data we can add to the + * last PDU without getting an overflow + */ + chain_err = ull_adv_aux_pdu_set_clear(adv, pdu_prev, pdu, + chain_add_fields, + 0U, hdr_data); + LL_ASSERT(chain_err == 0U); + /* + * in the next PDU we still need to add ad_len_chain bytes of data + * but we do not have overflow, since we already added + * the exact amount that would fit. We explicitly set overflow to 0. + * FIXME: ad_len_overflow already should be 0, to be verified. We wait + * fixing this until rewriting this whole function + */ + ad_len_overflow = 0U; + } else { + ad_len_overflow = 0U; + } + } + + if (ad_len_chain || ad_len_overflow) { struct pdu_adv_com_ext_adv *com_hdr_chain; struct pdu_adv_com_ext_adv *com_hdr; struct pdu_adv_ext_hdr *hdr_chain; @@ -425,6 +476,7 @@ uint8_t ll_adv_aux_ad_data_set(uint8_t handle, uint8_t op, uint8_t frag_pref, uint16_t sec_len; uint8_t *dptr; + len = ad_len_chain; /* Get reference to flags in superior PDU */ com_hdr = &pdu->adv_ext_ind; if (com_hdr->ext_hdr_len) { @@ -450,6 +502,7 @@ uint8_t ll_adv_aux_ad_data_set(uint8_t handle, uint8_t op, uint8_t frag_pref, hdr_chain = (void *)&com_hdr_chain->ext_hdr_adv_data[0]; dptr_chain = (void *)hdr_chain; + LL_ASSERT(dptr_chain); /* Flags */ *dptr_chain = 0U; @@ -498,6 +551,7 @@ uint8_t ll_adv_aux_ad_data_set(uint8_t handle, uint8_t op, uint8_t frag_pref, if (ad_len_overflow) { uint8_t *ad_overflow; + val_ptr = hdr_data; /* Copy overflowed AD data from previous PDU into this * new chain PDU */ @@ -505,6 +559,7 @@ uint8_t ll_adv_aux_ad_data_set(uint8_t handle, uint8_t op, uint8_t frag_pref, &val_ptr[ULL_ADV_HDR_DATA_DATA_PTR_OFFSET], sizeof(ad_overflow)); ad_overflow += *val_ptr; + (void)memcpy(dptr_chain, ad_overflow, ad_len_overflow); dptr_chain += ad_len_overflow; @@ -529,8 +584,6 @@ uint8_t ll_adv_aux_ad_data_set(uint8_t handle, uint8_t op, uint8_t frag_pref, return err; } - /* AD data len in chain PDU besides the overflow */ - len = ad_len_chain; } /* Check AdvData overflow */ @@ -555,7 +608,13 @@ uint8_t ll_adv_aux_ad_data_set(uint8_t handle, uint8_t op, uint8_t frag_pref, pdu_chain->len = sec_len + ad_len_overflow + len; /* Fill AD Data in chain PDU */ - (void)memcpy(dptr_chain, data, len); + if (ad_len_overflow != 0U) { + (void)memcpy(dptr_chain, data, ad_len_overflow); + } + + if (ad_len_chain != 0U) { + (void)memcpy(dptr_chain, &data[ad_len + ad_len_overflow], ad_len_chain); + } /* Get reference to aux ptr in superior PDU */ (void)memcpy(&aux_ptr, @@ -1804,7 +1863,7 @@ uint8_t ull_adv_aux_hdr_set_clear(struct ll_adv_set *adv, /* TODO: need aux_chain_ind support */ if ((sec_len + ad_len + ad_fragment_len) > PDU_AC_PAYLOAD_SIZE_MAX) { /* return excess length */ - *(uint8_t *)hdr_data = sec_len + ad_len - + *(uint8_t *)hdr_data = sec_len + ad_len + ad_fragment_len - PDU_AC_PAYLOAD_SIZE_MAX; if (pri_pdu == pri_pdu_prev) { @@ -2272,7 +2331,7 @@ uint8_t ull_adv_aux_pdu_set_clear(struct ll_adv_set *adv, /* Check AdvData overflow */ if ((len + ad_len + ad_fragment_len) > PDU_AC_PAYLOAD_SIZE_MAX) { /* return excess length */ - *(uint8_t *)hdr_data = len + ad_len - + *(uint8_t *)hdr_data = len + ad_len + ad_fragment_len - PDU_AC_PAYLOAD_SIZE_MAX; /* Will use packet too long error to determine fragmenting @@ -2454,7 +2513,7 @@ uint32_t ull_adv_aux_evt_init(struct ll_adv_aux_set *aux, HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_XTAL_US); aux->ull.ticks_preempt_to_start = HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_PREEMPT_MIN_US); - aux->ull.ticks_slot = HAL_TICKER_US_TO_TICKS(time_us); + aux->ull.ticks_slot = HAL_TICKER_US_TO_TICKS_CEIL(time_us); if (IS_ENABLED(CONFIG_BT_CTLR_LOW_LAT)) { ticks_slot_overhead = MAX(aux->ull.ticks_active_to_start, @@ -2471,7 +2530,7 @@ uint32_t ull_adv_aux_evt_init(struct ll_adv_aux_set *aux, #if defined(CONFIG_BT_CTLR_ADV_RESERVE_MAX) time_us = ull_adv_aux_time_get(aux, PDU_AC_PAYLOAD_SIZE_MAX, PDU_AC_PAYLOAD_SIZE_MAX); - ticks_slot = HAL_TICKER_US_TO_TICKS(time_us); + ticks_slot = HAL_TICKER_US_TO_TICKS_CEIL(time_us); #else ticks_slot = aux->ull.ticks_slot; #endif @@ -2997,7 +3056,7 @@ static uint8_t aux_time_update(struct ll_adv_aux_set *aux, struct pdu_adv *pdu, uint32_t time_us; time_us = aux_time_min_get(aux); - time_ticks = HAL_TICKER_US_TO_TICKS(time_us); + time_ticks = HAL_TICKER_US_TO_TICKS_CEIL(time_us); #if !defined(CONFIG_BT_CTLR_JIT_SCHEDULING) uint32_t volatile ret_cb; diff --git a/subsys/bluetooth/controller/ll_sw/ull_adv_internal.h b/subsys/bluetooth/controller/ll_sw/ull_adv_internal.h index 00bf3c126df..a2a0db7391f 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_adv_internal.h +++ b/subsys/bluetooth/controller/ll_sw/ull_adv_internal.h @@ -205,7 +205,7 @@ ull_adv_aux_hdr_len_fill(struct pdu_adv_com_ext_adv *com_hdr, uint8_t len) void ull_adv_sync_started_stopped(struct ll_adv_aux_set *aux); /* notify adv_sync_set that an iso instance has been created for it */ -void ull_adv_iso_created(struct ll_adv_sync_set *sync); +void ull_adv_sync_iso_created(struct ll_adv_sync_set *sync); #endif /* CONFIG_BT_CTLR_ADV_EXT */ @@ -322,6 +322,9 @@ struct lll_adv_iso_stream *ull_adv_iso_stream_get(uint16_t handle); /* helper function to release stream instances */ void ull_adv_iso_stream_release(struct ll_adv_iso_set *adv_iso); +/* helper function to return time reservation for Broadcast ISO event */ +uint32_t ull_adv_iso_max_time_get(const struct ll_adv_iso_set *adv_iso); + #if defined(CONFIG_BT_CTLR_DF_ADV_CTE_TX) /* helper function to release unused DF configuration memory */ void ull_df_adv_cfg_release(struct lll_df_adv_cfg *df_adv_cfg); diff --git a/subsys/bluetooth/controller/ll_sw/ull_adv_iso.c b/subsys/bluetooth/controller/ll_sw/ull_adv_iso.c index 765e911b208..6191f811311 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_adv_iso.c +++ b/subsys/bluetooth/controller/ll_sw/ull_adv_iso.c @@ -57,6 +57,7 @@ static struct stream *adv_iso_stream_acquire(void); static uint16_t adv_iso_stream_handle_get(struct lll_adv_iso_stream *stream); static uint8_t ptc_calc(const struct lll_adv_iso *lll, uint32_t event_spacing, uint32_t event_spacing_max); +static uint32_t adv_iso_time_get(const struct ll_adv_iso_set *adv_iso, bool max); static uint32_t adv_iso_start(struct ll_adv_iso_set *adv_iso, uint32_t iso_interval_us); static uint8_t adv_iso_chm_update(uint8_t big_handle); @@ -97,13 +98,19 @@ uint8_t ll_big_create(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis, struct ll_adv_iso_set *adv_iso; struct pdu_adv *pdu_prev, *pdu; struct pdu_big_info *big_info; + uint32_t ticks_slot_overhead; + struct ll_adv_sync_set *sync; + struct ll_adv_aux_set *aux; uint32_t event_spacing_max; uint8_t pdu_big_info_size; uint32_t iso_interval_us; uint32_t latency_packing; + uint32_t ticks_slot_sync; + uint32_t ticks_slot_aux; memq_link_t *link_cmplt; memq_link_t *link_term; struct ll_adv_set *adv; + uint32_t slot_overhead; uint32_t event_spacing; uint16_t ctrl_spacing; uint8_t sdu_per_event; @@ -275,14 +282,62 @@ uint8_t ll_big_create(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis, lll_adv_iso->num_bis; event_spacing = latency_packing + ctrl_spacing + EVENT_OVERHEAD_START_US + EVENT_OVERHEAD_END_US; - /* FIXME: calculate overheads due to extended and periodic advertising. + + /* Check if aux context allocated before we are creating ISO */ + if (adv->lll.aux) { + aux = HDR_LLL2ULL(adv->lll.aux); + } else { + aux = NULL; + } + + /* Calculate overheads due to extended advertising. */ + if (aux && aux->is_started) { + ticks_slot_aux = aux->ull.ticks_slot; + if (IS_ENABLED(CONFIG_BT_CTLR_LOW_LAT)) { + ticks_slot_overhead = MAX(aux->ull.ticks_active_to_start, + aux->ull.ticks_prepare_to_start); + } else { + ticks_slot_overhead = 0U; + } + ticks_slot_aux += ticks_slot_overhead; + } else { + ticks_slot_aux = 0U; + } + + /* Calculate overheads due to periodic advertising. */ + sync = HDR_LLL2ULL(lll_adv_sync); + if (sync->is_started) { + ticks_slot_sync = sync->ull.ticks_slot; + if (IS_ENABLED(CONFIG_BT_CTLR_LOW_LAT)) { + ticks_slot_overhead = MAX(sync->ull.ticks_active_to_start, + sync->ull.ticks_prepare_to_start); + } else { + ticks_slot_overhead = 0U; + } + ticks_slot_sync += ticks_slot_overhead; + } else { + ticks_slot_sync = 0U; + } + + /* Calculate total overheads due to extended and periodic advertising */ + if (CONFIG_BT_CTLR_ADV_AUX_SYNC_OFFSET > 0U) { + ticks_slot_overhead = MAX(ticks_slot_aux, ticks_slot_sync); + } else { + ticks_slot_overhead = ticks_slot_aux + ticks_slot_sync; + } + + /* Calculate max available ISO event spacing */ + slot_overhead = HAL_TICKER_TICKS_TO_US(ticks_slot_overhead); + if (slot_overhead < iso_interval_us) { + event_spacing_max = iso_interval_us - slot_overhead; + } else { + event_spacing_max = 0U; + } + + /* Check if ISO interval too small to fit the calculated BIG event + * timing required for the supplied BIG create parameters. */ - event_spacing_max = iso_interval_us - 2000U; if (event_spacing > event_spacing_max) { - /* ISO interval too small to fit the calculated BIG event - * timing required for the supplied BIG create parameters. - */ - /* Release allocated link buffers */ ll_rx_link_release(link_cmplt); ll_rx_link_release(link_term); @@ -487,7 +542,7 @@ uint8_t ll_big_create(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis, #if defined(CONFIG_BT_TICKER_EXT_EXPIRE_INFO) /* Notify the sync instance */ - ull_adv_iso_created(HDR_LLL2ULL(lll_adv_sync)); + ull_adv_sync_iso_created(HDR_LLL2ULL(lll_adv_sync)); #endif /* CONFIG_BT_TICKER_EXT_EXPIRE_INFO */ /* Commit the BIGInfo in the ACAD field of Periodic Advertising */ @@ -891,6 +946,11 @@ void ull_adv_iso_stream_release(struct ll_adv_iso_set *adv_iso) lll->adv = NULL; } +uint32_t ull_adv_iso_max_time_get(const struct ll_adv_iso_set *adv_iso) +{ + return adv_iso_time_get(adv_iso, true); +} + static int init_reset(void) { /* Add initializations common to power up initialization and HCI reset @@ -944,16 +1004,53 @@ static uint8_t ptc_calc(const struct lll_adv_iso *lll, uint32_t event_spacing, return 0U; } +static uint32_t adv_iso_time_get(const struct ll_adv_iso_set *adv_iso, bool max) +{ + const struct lll_adv_iso *lll_iso; + uint32_t ctrl_spacing; + uint32_t pdu_spacing; + uint32_t time_us; + + lll_iso = &adv_iso->lll; + + pdu_spacing = PDU_BIS_US(lll_iso->max_pdu, lll_iso->enc, lll_iso->phy, + lll_iso->phy_flags) + + EVENT_MSS_US; + ctrl_spacing = PDU_BIS_US(sizeof(struct pdu_big_ctrl), lll_iso->enc, + lll_iso->phy, lll_iso->phy_flags); + + /* 1. Maximum PDU transmission time in 1M/2M/S8 PHY is 17040 us, or + * represented in 15-bits. + * 2. NSE in the range 1 to 31 is represented in 5-bits + * 3. num_bis in the range 1 to 31 is represented in 5-bits + * + * Hence, worst case event time can be represented in 25-bits plus + * one each bit for added ctrl_spacing and radio event overheads. I.e. + * 27-bits required and sufficiently covered by using 32-bit data type + * for time_us. + */ + + if (IS_ENABLED(CONFIG_BT_CTLR_ADV_ISO_RESERVE_MAX) || max) { + time_us = (pdu_spacing * lll_iso->nse * lll_iso->num_bis) + + ctrl_spacing; + } else { + time_us = pdu_spacing * ((lll_iso->nse * lll_iso->num_bis) - + lll_iso->ptc); + } + + /* Add implementation defined radio event overheads */ + time_us += EVENT_OVERHEAD_START_US + EVENT_OVERHEAD_END_US; + + return time_us; +} + static uint32_t adv_iso_start(struct ll_adv_iso_set *adv_iso, uint32_t iso_interval_us) { uint32_t ticks_slot_overhead; - struct lll_adv_iso *lll_iso; uint32_t ticks_slot_offset; - uint32_t volatile ret_cb; + volatile uint32_t ret_cb; uint32_t ticks_anchor; - uint32_t ctrl_spacing; - uint32_t pdu_spacing; uint32_t ticks_slot; uint32_t slot_us; uint32_t ret; @@ -961,23 +1058,14 @@ static uint32_t adv_iso_start(struct ll_adv_iso_set *adv_iso, ull_hdr_init(&adv_iso->ull); - lll_iso = &adv_iso->lll; - - pdu_spacing = PDU_BIS_US(lll_iso->max_pdu, lll_iso->enc, lll_iso->phy, - lll_iso->phy_flags) + - EVENT_MSS_US; - ctrl_spacing = PDU_BIS_US(sizeof(struct pdu_big_ctrl), lll_iso->enc, - lll_iso->phy, lll_iso->phy_flags); - slot_us = (pdu_spacing * lll_iso->nse * lll_iso->num_bis) + - ctrl_spacing; - slot_us += EVENT_OVERHEAD_START_US + EVENT_OVERHEAD_END_US; + slot_us = adv_iso_time_get(adv_iso, false); adv_iso->ull.ticks_active_to_start = 0U; adv_iso->ull.ticks_prepare_to_start = HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_XTAL_US); adv_iso->ull.ticks_preempt_to_start = HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_PREEMPT_MIN_US); - adv_iso->ull.ticks_slot = HAL_TICKER_US_TO_TICKS(slot_us); + adv_iso->ull.ticks_slot = HAL_TICKER_US_TO_TICKS_CEIL(slot_us); ticks_slot_offset = MAX(adv_iso->ull.ticks_active_to_start, adv_iso->ull.ticks_prepare_to_start); @@ -1005,7 +1093,7 @@ static uint32_t adv_iso_start(struct ll_adv_iso_set *adv_iso, ret_cb = TICKER_STATUS_BUSY; ret = ticker_start(TICKER_INSTANCE_ID_CTLR, TICKER_USER_ID_THREAD, - (TICKER_ID_ADV_ISO_BASE + lll_iso->handle), + (TICKER_ID_ADV_ISO_BASE + adv_iso->lll.handle), ticks_anchor, 0U, HAL_TICKER_US_TO_TICKS(iso_interval_us), HAL_TICKER_REMAINDER(iso_interval_us), diff --git a/subsys/bluetooth/controller/ll_sw/ull_adv_sync.c b/subsys/bluetooth/controller/ll_sw/ull_adv_sync.c index 178116a54ec..f1f521ca235 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_adv_sync.c +++ b/subsys/bluetooth/controller/ll_sw/ull_adv_sync.c @@ -222,7 +222,7 @@ uint8_t ll_adv_sync_param_set(uint8_t handle, uint16_t interval, uint16_t flags) } #if defined(CONFIG_BT_CTLR_ADV_ISO) && defined(CONFIG_BT_TICKER_EXT_EXPIRE_INFO) -void ull_adv_iso_created(struct ll_adv_sync_set *sync) +void ull_adv_sync_iso_created(struct ll_adv_sync_set *sync) { if (sync->lll.iso && sync->is_started) { uint8_t iso_handle = sync->lll.iso->handle; @@ -1136,7 +1136,7 @@ uint32_t ull_adv_sync_evt_init(struct ll_adv_set *adv, HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_XTAL_US); sync->ull.ticks_preempt_to_start = HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_PREEMPT_MIN_US); - sync->ull.ticks_slot = HAL_TICKER_US_TO_TICKS(time_us); + sync->ull.ticks_slot = HAL_TICKER_US_TO_TICKS_CEIL(time_us); ticks_slot_offset = MAX(sync->ull.ticks_active_to_start, sync->ull.ticks_prepare_to_start); diff --git a/subsys/bluetooth/controller/ll_sw/ull_central.c b/subsys/bluetooth/controller/ll_sw/ull_central.c index 5effe2ae676..7ea3c600ea6 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_central.c +++ b/subsys/bluetooth/controller/ll_sw/ull_central.c @@ -97,6 +97,7 @@ uint8_t ll_create_connection(uint16_t scan_interval, uint16_t scan_window, uint16_t max_tx_time; uint16_t max_rx_time; memq_link_t *link; + uint32_t slot_us; uint8_t hop; int err; @@ -290,8 +291,6 @@ uint8_t ll_create_connection(uint16_t scan_interval, uint16_t scan_window, /* Setup the PRT reload */ ull_cp_prt_reload_set(conn, conn_interval_us); - conn->central.terminate_ack = 0U; - conn->llcp_terminate.reason_final = 0U; /* NOTE: use allocated link for generating dedicated * terminate ind rx node @@ -360,13 +359,13 @@ uint8_t ll_create_connection(uint16_t scan_interval, uint16_t scan_window, #endif /* CONFIG_BT_CTLR_ADV_EXT */ #endif /* CONFIG_BT_CTLR_DATA_LENGTH */ - conn->ull.ticks_slot = - HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_START_US + - EVENT_OVERHEAD_END_US + - ready_delay_us + - max_tx_time + - EVENT_IFS_US + - max_rx_time); + /* Calculate event time reservation */ + slot_us = max_tx_time + max_rx_time; + slot_us += EVENT_IFS_US + (EVENT_CLOCK_JITTER_US << 1); + slot_us += ready_delay_us; + slot_us += EVENT_OVERHEAD_START_US + EVENT_OVERHEAD_END_US; + + conn->ull.ticks_slot = HAL_TICKER_US_TO_TICKS_CEIL(slot_us); #if defined(CONFIG_BT_CTLR_PRIVACY) ull_filter_scan_update(filter_policy); diff --git a/subsys/bluetooth/controller/ll_sw/ull_central_iso.c b/subsys/bluetooth/controller/ll_sw/ull_central_iso.c index 0c7a8e5ef35..474b8d0dbe9 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_central_iso.c +++ b/subsys/bluetooth/controller/ll_sw/ull_central_iso.c @@ -136,7 +136,9 @@ uint8_t ll_cis_parameters_set(uint8_t cis_id, ll_iso_setup.stream[cis_idx].c_max_sdu = c_sdu; ll_iso_setup.stream[cis_idx].p_max_sdu = p_sdu; ll_iso_setup.stream[cis_idx].lll.tx.phy = c_phy; + ll_iso_setup.stream[cis_idx].lll.tx.phy_flags = PHY_FLAGS_S8; ll_iso_setup.stream[cis_idx].lll.rx.phy = p_phy; + ll_iso_setup.stream[cis_idx].lll.rx.phy_flags = PHY_FLAGS_S8; ll_iso_setup.stream[cis_idx].central.c_rtn = c_rtn; ll_iso_setup.stream[cis_idx].central.p_rtn = p_rtn; ll_iso_setup.cis_idx++; @@ -234,6 +236,15 @@ uint8_t ll_cig_parameters_commit(uint8_t cig_id, uint16_t *handles) cig->iso_interval = BT_HCI_ISO_INTERVAL_MIN; } +#if defined(CONFIG_BT_CTLR_CONN_ISO_AVOID_SEGMENTATION) + /* Check if this is a HAP usecase which requires higher link bandwidth to ensure + * segmentation is not invoked in ISO-AL. + */ + if (cig->central.framing && cig->c_sdu_interval == 10000U) { + cig->iso_interval = 6; /* 7500 us */ + } +#endif + if (!cig->central.framing && (cig->c_sdu_interval % ISO_INT_UNIT_US)) { /* Framing not requested but requirement for unframed is not met. Force * CIG into framed mode. @@ -243,6 +254,7 @@ uint8_t ll_cig_parameters_commit(uint8_t cig_id, uint16_t *handles) } iso_interval_us = cig->iso_interval * ISO_INT_UNIT_US; + cig->lll.iso_interval_us = iso_interval_us; lll_hdr_init(&cig->lll, cig); max_se_length = 0U; @@ -550,7 +562,7 @@ uint8_t ll_cig_parameters_commit(uint8_t cig_id, uint16_t *handles) HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_XTAL_US); cig->ull.ticks_preempt_to_start = HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_PREEMPT_MIN_US); - cig->ull.ticks_slot = HAL_TICKER_US_TO_TICKS(slot_us); + cig->ull.ticks_slot = HAL_TICKER_US_TO_TICKS_CEIL(slot_us); #endif /* !CONFIG_BT_CTLR_JIT_SCHEDULING */ /* Reset params cache */ @@ -626,7 +638,9 @@ uint8_t ll_cis_parameters_test_set(uint8_t cis_id, uint8_t nse, ll_iso_setup.stream[cis_idx].lll.tx.max_pdu = c_bn ? c_pdu : 0U; ll_iso_setup.stream[cis_idx].lll.rx.max_pdu = p_bn ? p_pdu : 0U; ll_iso_setup.stream[cis_idx].lll.tx.phy = c_phy; + ll_iso_setup.stream[cis_idx].lll.tx.phy_flags = PHY_FLAGS_S8; ll_iso_setup.stream[cis_idx].lll.rx.phy = p_phy; + ll_iso_setup.stream[cis_idx].lll.rx.phy_flags = PHY_FLAGS_S8; ll_iso_setup.stream[cis_idx].lll.tx.bn = c_bn; ll_iso_setup.stream[cis_idx].lll.rx.bn = p_bn; ll_iso_setup.cis_idx++; @@ -681,15 +695,6 @@ void ll_cis_create(uint16_t cis_handle, uint16_t acl_handle) /* Initialize stream states */ cis->established = 0; cis->teardown = 0; - cis->lll.event_count = LLL_CONN_ISO_EVENT_COUNT_MAX; - cis->lll.sn = 0; - cis->lll.nesn = 0; - cis->lll.cie = 0; - cis->lll.flush = LLL_CIS_FLUSH_NONE; - cis->lll.active = 0; - cis->lll.datapath_ready_rx = 0; - cis->lll.tx.bn_curr = 1U; - cis->lll.rx.bn_curr = 1U; (void)memset(&cis->hdr, 0U, sizeof(cis->hdr)); @@ -853,19 +858,35 @@ uint8_t ull_central_iso_setup(uint16_t cis_handle, cis->offset = cis_offset; #else /* !CONFIG_BT_CTLR_JIT_SCHEDULING */ - if (IS_ENABLED(CONFIG_BT_CTLR_CENTRAL_SPACING) && (CONFIG_BT_CTLR_CENTRAL_SPACING > 0)) { + + if (false) { + +#if defined(CONFIG_BT_CTLR_CENTRAL_SPACING) + } else if (CONFIG_BT_CTLR_CENTRAL_SPACING > 0) { uint32_t cis_offset; - cis_offset = MAX((HAL_TICKER_TICKS_TO_US(conn->ull.ticks_slot) + - (EVENT_TICKER_RES_MARGIN_US << 1U) + cig->sync_delay - - cis->sync_delay), *cis_offset_min); + cis_offset = HAL_TICKER_TICKS_TO_US(conn->ull.ticks_slot) + + (EVENT_TICKER_RES_MARGIN_US << 1U); + + cis_offset += cig->sync_delay - cis->sync_delay; + + if (cis_offset < *cis_offset_min) { + cis_offset = *cis_offset_min; + } + cis->offset = cis_offset; +#endif /* CONFIG_BT_CTLR_CENTRAL_SPACING */ + } else { cis->offset = *cis_offset_min; } #endif /* !CONFIG_BT_CTLR_JIT_SCHEDULING */ cis->central.instant = instant; +#if defined(CONFIG_BT_CTLR_ISOAL_PSN_IGNORE) + cis->pkt_seq_num = 0U; +#endif /* CONFIG_BT_CTLR_ISOAL_PSN_IGNORE */ + cis->lll.event_count = LLL_CONN_ISO_EVENT_COUNT_MAX; cis->lll.next_subevent = 0U; cis->lll.sn = 0U; cis->lll.nesn = 0U; @@ -931,8 +952,9 @@ int ull_central_iso_cis_offset_get(uint16_t cis_handle, #endif /* CONFIG_BT_CTLR_CENTRAL_SPACING != 0 */ *cis_offset_min = HAL_TICKER_TICKS_TO_US(conn->ull.ticks_slot) + - (EVENT_TICKER_RES_MARGIN_US << 1U) + - cig->sync_delay - cis->sync_delay; + (EVENT_TICKER_RES_MARGIN_US << 1U); + + *cis_offset_min += cig->sync_delay - cis->sync_delay; return 0; } @@ -973,7 +995,6 @@ static void mfy_cig_offset_get(void *param) offset_min_us += cig->sync_delay - cis->sync_delay; conn = ll_conn_get(cis->lll.acl_handle); - conn_interval_us = (uint32_t)conn->lll.interval * CONN_INT_UNIT_US; while (offset_min_us >= (conn_interval_us + PDU_CIS_OFFSET_MIN_US)) { offset_min_us -= conn_interval_us; @@ -1131,29 +1152,51 @@ static void set_bn_max_pdu(bool framed, uint32_t iso_interval, uint8_t *max_pdu) { if (framed) { - uint32_t ceil_f_x_max_sdu; - uint16_t max_pdu_bn1; - uint32_t max_drift; + uint32_t max_drift_us; uint32_t ceil_f; - /* Framed (From ES-18002): + /* BT Core 5.4 Vol 6, Part G, Section 2.2: * Max_PDU >= ((ceil(F) x 5 + ceil(F x Max_SDU)) / BN) + 2 * F = (1 + MaxDrift) x ISO_Interval / SDU_Interval * SegmentationHeader + TimeOffset = 5 bytes * Continuation header = 2 bytes * MaxDrift (Max. allowed SDU delivery timing drift) = 100 ppm */ - max_drift = DIV_ROUND_UP(SDU_MAX_DRIFT_PPM * sdu_interval, 1000000U); - ceil_f = DIV_ROUND_UP(iso_interval + max_drift, sdu_interval); - ceil_f_x_max_sdu = DIV_ROUND_UP(max_sdu * (iso_interval + max_drift), - sdu_interval); - - /* Strategy: Keep lowest possible BN. - * TODO: Implement other strategies, possibly as policies. + max_drift_us = DIV_ROUND_UP(SDU_MAX_DRIFT_PPM * sdu_interval, USEC_PER_SEC); + ceil_f = DIV_ROUND_UP((USEC_PER_SEC + max_drift_us) * (uint64_t)iso_interval, + USEC_PER_SEC * (uint64_t)sdu_interval); + if (false) { +#if defined(CONFIG_BT_CTLR_CONN_ISO_AVOID_SEGMENTATION) + /* To avoid segmentation according to HAP, if the ISO_Interval is less than + * the SDU_Interval, we assume BN=1 and calculate the Max_PDU as: + * Max_PDU = celi(F / BN) x (5 / Max_SDU) + * + * This is in accordance with the "Core enhancement for ISOAL CR". + * + * This ensures that the drift can be contained in the difference between + * SDU_Interval and link bandwidth. For BN=1, ceil(F) == ceil(F/BN). */ - max_pdu_bn1 = ceil_f * 5 + ceil_f_x_max_sdu; - *bn = DIV_ROUND_UP(max_pdu_bn1, LL_CIS_OCTETS_TX_MAX); - *max_pdu = DIV_ROUND_UP(max_pdu_bn1, *bn) + 2; + } else if (iso_interval < sdu_interval) { + *bn = 1; + *max_pdu = ceil_f * (PDU_ISO_SEG_HDR_SIZE + PDU_ISO_SEG_TIMEOFFSET_SIZE + + max_sdu); +#endif + } else { + uint32_t ceil_f_x_max_sdu; + uint16_t max_pdu_bn1; + + ceil_f_x_max_sdu = DIV_ROUND_UP(max_sdu * ((USEC_PER_SEC + max_drift_us) * + (uint64_t)iso_interval), + USEC_PER_SEC * (uint64_t)sdu_interval); + + /* Strategy: Keep lowest possible BN. + * TODO: Implement other strategies, possibly as policies. + */ + max_pdu_bn1 = ceil_f * (PDU_ISO_SEG_HDR_SIZE + + PDU_ISO_SEG_TIMEOFFSET_SIZE) + ceil_f_x_max_sdu; + *bn = DIV_ROUND_UP(max_pdu_bn1, LL_CIS_OCTETS_TX_MAX); + *max_pdu = DIV_ROUND_UP(max_pdu_bn1, *bn) + PDU_ISO_SEG_HDR_SIZE; + } } else { /* For unframed, ISO_Interval must be N x SDU_Interval */ LL_ASSERT(iso_interval % sdu_interval == 0); diff --git a/subsys/bluetooth/controller/ll_sw/ull_conn.c b/subsys/bluetooth/controller/ll_sw/ull_conn.c index 0b54a5dee79..1640c00e87e 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_conn.c +++ b/subsys/bluetooth/controller/ll_sw/ull_conn.c @@ -1044,12 +1044,6 @@ void ull_conn_done(struct node_rx_event_done *done) lll->latency_event = lll->latency; } #endif /* CONFIG_BT_PERIPHERAL */ - -#if defined(CONFIG_BT_CENTRAL) - } else if (reason_final) { - conn->central.terminate_ack = 1; -#endif /* CONFIG_BT_CENTRAL */ - } /* Reset connection failed to establish countdown */ @@ -1233,7 +1227,7 @@ void ull_conn_done(struct node_rx_event_done *done) #if defined(CONFIG_BT_CTLR_SLOT_RESERVATION_UPDATE) #if defined(CONFIG_BT_CTLR_DATA_LENGTH) || defined(CONFIG_BT_CTLR_PHY) if (lll->evt_len_upd) { - uint32_t ready_delay, rx_time, tx_time, ticks_slot; + uint32_t ready_delay, rx_time, tx_time, ticks_slot, slot_us; lll->evt_len_upd = 0; #if defined(CONFIG_BT_CTLR_PHY) @@ -1257,12 +1251,18 @@ void ull_conn_done(struct node_rx_event_done *done) tx_time = PDU_DC_MAX_US(lll->dle.eff.max_tx_octets, 0); rx_time = PDU_DC_MAX_US(lll->dle.eff.max_rx_octets, 0); #endif /* CONFIG_BT_CTLR_PHY */ - ticks_slot = HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_START_US + - ready_delay + - EVENT_IFS_US + - rx_time + - tx_time + - 4); + + /* Calculate event time reservation */ + slot_us = tx_time + rx_time; + slot_us += EVENT_IFS_US + (EVENT_CLOCK_JITTER_US << 1); + slot_us += ready_delay; + + if (IS_ENABLED(CONFIG_BT_CTLR_EVENT_OVERHEAD_RESERVE_MAX) || + !conn->lll.role) { + slot_us += EVENT_OVERHEAD_START_US + EVENT_OVERHEAD_END_US; + } + + ticks_slot = HAL_TICKER_US_TO_TICKS_CEIL(slot_us); if (ticks_slot > conn->ull.ticks_slot) { ticks_slot_plus = ticks_slot - conn->ull.ticks_slot; } else { diff --git a/subsys/bluetooth/controller/ll_sw/ull_conn_iso.c b/subsys/bluetooth/controller/ll_sw/ull_conn_iso.c index 8b79100d942..2ec6a73b4a4 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_conn_iso.c +++ b/subsys/bluetooth/controller/ll_sw/ull_conn_iso.c @@ -851,6 +851,10 @@ void ull_conn_iso_start(struct ll_conn *conn, uint16_t cis_handle, */ if (cig->state == CIG_STATE_ACTIVE) { #if !defined(CONFIG_BT_CTLR_JIT_SCHEDULING) + /* Initialize CIS event lazy at CIS create */ + cis->lll.lazy_active = 0U; + + /* Deferred fill CIS event lazy value at CIS create */ cis_lazy_fill(cis); #else /* CONFIG_BT_CTLR_JIT_SCHEDULING */ /* Set CIS active in already active CIG */ @@ -968,7 +972,9 @@ void ull_conn_iso_start(struct ll_conn *conn, uint16_t cis_handle, /* Below is time reservation for sequential packing */ slot_us = cis->lll.sub_interval * cis->lll.nse; - slot_us += EVENT_OVERHEAD_START_US + EVENT_OVERHEAD_END_US; + if (IS_ENABLED(CONFIG_BT_CTLR_EVENT_OVERHEAD_RESERVE_MAX)) { + slot_us += EVENT_OVERHEAD_START_US + EVENT_OVERHEAD_END_US; + } /* FIXME: How to use ready_delay_us in the time reservation? * i.e. when CISes use different PHYs? Is that even @@ -983,7 +989,7 @@ void ull_conn_iso_start(struct ll_conn *conn, uint16_t cis_handle, HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_XTAL_US); cig->ull.ticks_preempt_to_start = HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_PREEMPT_MIN_US); - cig->ull.ticks_slot = HAL_TICKER_US_TO_TICKS(slot_us); + cig->ull.ticks_slot = HAL_TICKER_US_TO_TICKS_CEIL(slot_us); } ticks_slot_offset = MAX(cig->ull.ticks_active_to_start, @@ -996,6 +1002,9 @@ void ull_conn_iso_start(struct ll_conn *conn, uint16_t cis_handle, } ticks_slot = cig->ull.ticks_slot + ticks_slot_overhead; + + /* Initialize CIS event lazy at CIS create */ + cis->lll.lazy_active = 0U; #endif /* !CONFIG_BT_CTLR_JIT_SCHEDULING */ /* Start CIS peripheral CIG ticker */ @@ -1014,11 +1023,6 @@ void ull_conn_iso_start(struct ll_conn *conn, uint16_t cis_handle, /* Set CIG and the first CIS state as active */ cig->state = CIG_STATE_ACTIVE; cis->lll.active = 1U; - -#if !defined(CONFIG_BT_CTLR_JIT_SCHEDULING) - /* CIS event lazy at CIS create */ - cis->lll.lazy_active = 0U; -#endif /* !CONFIG_BT_CTLR_JIT_SCHEDULING */ } #if !defined(CONFIG_BT_CTLR_JIT_SCHEDULING) @@ -1040,8 +1044,8 @@ static void mfy_cis_lazy_fill(void *param) uint32_t ticks_to_expire; uint32_t ticks_current; uint32_t remainder; + uint16_t lazy = 0U; uint8_t ticker_id; - uint16_t lazy; uint8_t retry; uint8_t id; diff --git a/subsys/bluetooth/controller/ll_sw/ull_conn_iso_types.h b/subsys/bluetooth/controller/ll_sw/ull_conn_iso_types.h index e8ca29da7af..39cee21d0b1 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_conn_iso_types.h +++ b/subsys/bluetooth/controller/ll_sw/ull_conn_iso_types.h @@ -48,6 +48,10 @@ struct ll_conn_iso_stream { */ uint8_t terminate_reason; uint8_t cis_id; + +#if defined(CONFIG_BT_CTLR_ISOAL_PSN_IGNORE) + uint64_t pkt_seq_num:39; +#endif /* CONFIG_BT_CTLR_ISOAL_PSN_IGNORE */ }; struct ll_conn_iso_group { diff --git a/subsys/bluetooth/controller/ll_sw/ull_conn_types.h b/subsys/bluetooth/controller/ll_sw/ull_conn_types.h index 12e16488f38..8fabab03378 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_conn_types.h +++ b/subsys/bluetooth/controller/ll_sw/ull_conn_types.h @@ -202,7 +202,6 @@ struct ll_conn { #if defined(CONFIG_BT_CTLR_CONN_META) uint8_t is_must_expire:1; #endif /* CONFIG_BT_CTLR_CONN_META */ - uint8_t terminate_ack:1; } central; #endif /* CONFIG_BT_CENTRAL */ }; diff --git a/subsys/bluetooth/controller/ll_sw/ull_iso.c b/subsys/bluetooth/controller/ll_sw/ull_iso.c index 1520bd76963..b21a9d20721 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_iso.c +++ b/subsys/bluetooth/controller/ll_sw/ull_iso.c @@ -491,11 +491,11 @@ uint8_t ll_setup_iso_path(uint16_t handle, uint8_t path_dir, uint8_t path_id, pdu_release, &source_handle); if (!err) { - if (cis) { + if (IS_ENABLED(CONFIG_BT_CTLR_CONN_ISO) && cis != NULL) { cis->hdr.datapath_in = dp; } - if (adv_stream) { + if (IS_ENABLED(CONFIG_BT_CTLR_ADV_ISO) && adv_stream != NULL) { adv_stream->dp = dp; } @@ -1058,7 +1058,8 @@ void ll_iso_transmit_test_send_sdu(uint16_t handle, uint32_t ticks_at_expire) /* Send all SDU fragments */ do { - sdu.time_stamp = HAL_TICKER_TICKS_TO_US(ticks_at_expire); + sdu.cntr_time_stamp = HAL_TICKER_TICKS_TO_US(ticks_at_expire); + sdu.time_stamp = sdu.cntr_time_stamp; sdu.size = MIN(remaining_tx, ISO_TEST_TX_BUFFER_SIZE); memset(tx_buffer, 0, sdu.size); diff --git a/subsys/bluetooth/controller/ll_sw/ull_iso_types.h b/subsys/bluetooth/controller/ll_sw/ull_iso_types.h index 18d9a0f7bfe..457ad1d2458 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_iso_types.h +++ b/subsys/bluetooth/controller/ll_sw/ull_iso_types.h @@ -9,11 +9,18 @@ #define LL_BIS_ADV_HANDLE_BASE BT_CTLR_ADV_ISO_STREAM_HANDLE_BASE #define LL_BIS_ADV_IDX_FROM_HANDLE(conn_handle) \ ((conn_handle) - (LL_BIS_ADV_HANDLE_BASE)) +/* Conditional compile to prevent coverity issue CWE570, comparison of unsigned int to 0 */ +#if (LL_BIS_ADV_HANDLE_BASE > 0) #define IS_ADV_ISO_HANDLE(conn_handle) \ (((conn_handle) >= (LL_BIS_ADV_HANDLE_BASE)) && \ ((conn_handle) <= ((LL_BIS_ADV_HANDLE_BASE) + \ (BT_CTLR_ADV_ISO_STREAM_MAX) - 1U))) #else +#define IS_ADV_ISO_HANDLE(conn_handle) \ + ((conn_handle) <= ((LL_BIS_ADV_HANDLE_BASE) + \ + (BT_CTLR_ADV_ISO_STREAM_MAX) - 1U)) +#endif /* LL_BIS_ADV_HANDLE_BASE */ +#else #define LL_BIS_ADV_IDX_FROM_HANDLE(conn_handle) 0U #define IS_ADV_ISO_HANDLE(conn_handle) 0U #endif /* CONFIG_BT_CTLR_ADV_ISO */ @@ -23,11 +30,18 @@ #define LL_BIS_SYNC_HANDLE_BASE BT_CTLR_SYNC_ISO_STREAM_HANDLE_BASE #define LL_BIS_SYNC_IDX_FROM_HANDLE(conn_handle) \ ((conn_handle) - (LL_BIS_SYNC_HANDLE_BASE)) +/* Conditional compile to prevent coverity issue CWE570, comparison of unsigned int to 0 */ +#if (LL_BIS_SYNC_HANDLE_BASE > 0) #define IS_SYNC_ISO_HANDLE(conn_handle) \ (((conn_handle) >= (LL_BIS_SYNC_HANDLE_BASE)) && \ ((conn_handle) <= ((LL_BIS_SYNC_HANDLE_BASE) + \ (BT_CTLR_SYNC_ISO_STREAM_MAX) - 1U))) #else +#define IS_SYNC_ISO_HANDLE(conn_handle) \ + ((conn_handle) <= ((LL_BIS_SYNC_HANDLE_BASE) + \ + (BT_CTLR_SYNC_ISO_STREAM_MAX) - 1U)) +#endif /* LL_BIS_SYNC_HANDLE_BASE */ +#else #define LL_BIS_SYNC_IDX_FROM_HANDLE(conn_handle) 0U #define IS_SYNC_ISO_HANDLE(conn_handle) 0U #endif /* CONFIG_BT_CTLR_SYNC_ISO */ diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_cc.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_cc.c index 387cc884dea..59c23e7a0f6 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_cc.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_cc.c @@ -643,6 +643,7 @@ static void lp_cc_execute_fsm(struct ll_conn *conn, struct proc_ctx *ctx, uint8_ /* LLCP Local Procedure FSM states */ enum { LP_CC_STATE_IDLE, + LP_CC_STATE_WAIT_NTF_AVAIL, LP_CC_STATE_WAIT_OFFSET_CALC, LP_CC_STATE_WAIT_OFFSET_CALC_TX_REQ, LP_CC_STATE_WAIT_TX_CIS_REQ, @@ -839,7 +840,7 @@ static void lp_cc_st_idle(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t ev } else { /* Peer doesn't support CIS Peripheral so report unsupported */ ctx->data.cis_create.error = BT_HCI_ERR_UNSUPP_REMOTE_FEATURE; - lp_cc_complete(conn, ctx, evt, param); + ctx->state = LP_CC_STATE_WAIT_NTF_AVAIL; } break; default: @@ -854,6 +855,26 @@ static void lp_cc_st_idle(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t ev } } +static void lp_cc_state_wait_ntf_avail(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, + void *param) +{ + switch (evt) { + case LP_CC_EVT_RUN: + if (llcp_ntf_alloc_is_available()) { + ctx->node_ref.rx = llcp_ntf_alloc(); + /* Mark node as RETAIN to trigger put/sched */ + ctx->node_ref.rx->hdr.type = NODE_RX_TYPE_RETAIN; + + /* Now we're good to complete procedure*/ + lp_cc_complete(conn, ctx, evt, param); + } + break; + default: + /* Ignore other evts */ + break; + } +} + static void cc_prepare_cis_ind(struct ll_conn *conn, struct proc_ctx *ctx) { uint8_t err; @@ -900,14 +921,14 @@ static void lp_cc_st_wait_rx_cis_rsp(struct ll_conn *conn, struct proc_ctx *ctx, break; case LP_CC_EVT_UNKNOWN: /* Unsupported in peer, so disable locally for this connection */ - feature_unmask_features(conn, LL_FEAT_BIT_CIS_PERIPHERAL); + feature_unmask_peer_features(conn, LL_FEAT_BIT_CIS_PERIPHERAL); ctx->data.cis_create.error = BT_HCI_ERR_UNSUPP_REMOTE_FEATURE; lp_cc_complete(conn, ctx, evt, param); break; case LP_CC_EVT_REJECT: if (pdu->llctrl.reject_ext_ind.error_code == BT_HCI_ERR_UNSUPP_REMOTE_FEATURE) { /* Unsupported in peer, so disable locally for this connection */ - feature_unmask_features(conn, LL_FEAT_BIT_CIS_PERIPHERAL); + feature_unmask_peer_features(conn, LL_FEAT_BIT_CIS_PERIPHERAL); } ctx->data.cis_create.error = pdu->llctrl.reject_ext_ind.error_code; lp_cc_complete(conn, ctx, evt, param); @@ -1040,6 +1061,9 @@ static void lp_cc_execute_fsm(struct ll_conn *conn, struct proc_ctx *ctx, uint8_ case LP_CC_STATE_IDLE: lp_cc_st_idle(conn, ctx, evt, param); break; + case LP_CC_STATE_WAIT_NTF_AVAIL: + lp_cc_state_wait_ntf_avail(conn, ctx, evt, param); + break; case LP_CC_STATE_WAIT_OFFSET_CALC_TX_REQ: lp_cc_st_wait_offset_calc_tx_req(conn, ctx, evt, param); break; diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_features.h b/subsys/bluetooth/controller/ll_sw/ull_llcp_features.h index ac60206bc70..75d92d5524e 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_features.h +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_features.h @@ -9,6 +9,11 @@ static inline void feature_unmask_features(struct ll_conn *conn, uint64_t ll_fea conn->llcp.fex.features_used &= ~ll_feat_mask; } +static inline void feature_unmask_peer_features(struct ll_conn *conn, uint64_t ll_feat_mask) +{ + conn->llcp.fex.features_peer &= ~ll_feat_mask; +} + static inline bool feature_le_encryption(struct ll_conn *conn) { #if defined(CONFIG_BT_CTLR_LE_ENC) diff --git a/subsys/bluetooth/controller/ll_sw/ull_peripheral.c b/subsys/bluetooth/controller/ll_sw/ull_peripheral.c index f1716c06573..5a34f787ec7 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_peripheral.c +++ b/subsys/bluetooth/controller/ll_sw/ull_peripheral.c @@ -89,6 +89,7 @@ void ull_periph_setup(struct node_rx_hdr *rx, struct node_rx_ftr *ftr, uint16_t max_rx_time; uint16_t win_offset; memq_link_t *link; + uint32_t slot_us; uint8_t chan_sel; void *node; @@ -360,10 +361,19 @@ void ull_periph_setup(struct node_rx_hdr *rx, struct node_rx_ftr *ftr, #endif /* !CONFIG_BT_CTLR_DATA_LENGTH */ #if defined(CONFIG_BT_CTLR_PHY) - ready_delay_us = lll_radio_rx_ready_delay_get(lll->phy_rx, 1); -#else - ready_delay_us = lll_radio_rx_ready_delay_get(0, 0); -#endif + ready_delay_us = lll_radio_rx_ready_delay_get(lll->phy_rx, PHY_FLAGS_S8); +#else /* CONFIG_BT_CTLR_PHY */ + ready_delay_us = lll_radio_rx_ready_delay_get(0U, 0U); +#endif /* CONFIG_BT_CTLR_PHY */ + + /* Calculate event time reservation */ + slot_us = max_rx_time + max_tx_time; + slot_us += EVENT_IFS_US + (EVENT_CLOCK_JITTER_US << 1); + slot_us += ready_delay_us; + + if (IS_ENABLED(CONFIG_BT_CTLR_EVENT_OVERHEAD_RESERVE_MAX)) { + slot_us += EVENT_OVERHEAD_START_US + EVENT_OVERHEAD_END_US; + } /* TODO: active_to_start feature port */ conn->ull.ticks_active_to_start = 0U; @@ -371,13 +381,7 @@ void ull_periph_setup(struct node_rx_hdr *rx, struct node_rx_ftr *ftr, HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_XTAL_US); conn->ull.ticks_preempt_to_start = HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_PREEMPT_MIN_US); - conn->ull.ticks_slot = - HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_START_US + - EVENT_OVERHEAD_END_US + - ready_delay_us + - max_rx_time + - EVENT_IFS_US + - max_tx_time); + conn->ull.ticks_slot = HAL_TICKER_US_TO_TICKS_CEIL(slot_us); ticks_slot_offset = MAX(conn->ull.ticks_active_to_start, conn->ull.ticks_prepare_to_start); diff --git a/subsys/bluetooth/controller/ll_sw/ull_peripheral_iso.c b/subsys/bluetooth/controller/ll_sw/ull_peripheral_iso.c index 5bbac2528d0..843bf877935 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_peripheral_iso.c +++ b/subsys/bluetooth/controller/ll_sw/ull_peripheral_iso.c @@ -110,6 +110,10 @@ uint8_t ll_cis_accept(uint16_t handle) } else { cis_offset_min = HAL_TICKER_TICKS_TO_US(conn->ull.ticks_slot) + (EVENT_TICKER_RES_MARGIN_US << 1U); + + if (!IS_ENABLED(CONFIG_BT_CTLR_EVENT_OVERHEAD_RESERVE_MAX)) { + cis_offset_min += EVENT_OVERHEAD_START_US + EVENT_OVERHEAD_END_US; + } } /* Accept request */ @@ -187,6 +191,7 @@ uint8_t ull_peripheral_iso_acquire(struct ll_conn *acl, cig->iso_interval = sys_le16_to_cpu(req->iso_interval); iso_interval_us = cig->iso_interval * CONN_INT_UNIT_US; + cig->lll.iso_interval_us = iso_interval_us; cig->cig_id = req->cig_id; cig->lll.handle = LLL_HANDLE_INVALID; @@ -250,24 +255,23 @@ uint8_t ull_peripheral_iso_acquire(struct ll_conn *acl, cis->p_max_sdu = (uint16_t)(req->p_max_sdu[1] & 0x0F) << 8 | req->p_max_sdu[0]; - cis->lll.handle = 0xFFFF; + cis->lll.active = 0U; + cis->lll.handle = LLL_HANDLE_INVALID; cis->lll.acl_handle = acl->lll.handle; cis->lll.sub_interval = sys_get_le24(req->sub_interval); cis->lll.nse = req->nse; cis->lll.rx.phy = req->c_phy; + cis->lll.rx.phy_flags = PHY_FLAGS_S8; cis->lll.rx.bn = req->c_bn; cis->lll.rx.ft = req->c_ft; cis->lll.rx.max_pdu = sys_le16_to_cpu(req->c_max_pdu); - cis->lll.rx.payload_count = 0; - cis->lll.rx.bn_curr = 1U; cis->lll.tx.phy = req->p_phy; + cis->lll.tx.phy_flags = PHY_FLAGS_S8; cis->lll.tx.bn = req->p_bn; cis->lll.tx.ft = req->p_ft; cis->lll.tx.max_pdu = sys_le16_to_cpu(req->p_max_pdu); - cis->lll.tx.payload_count = 0; - cis->lll.tx.bn_curr = 1U; if (!cis->lll.link_tx_free) { cis->lll.link_tx_free = &cis->lll.link_tx; @@ -322,6 +326,9 @@ uint8_t ull_peripheral_iso_setup(struct pdu_data_llctrl_cis_ind *ind, cis->sync_delay = sys_get_le24(ind->cis_sync_delay); cis->offset = cis_offset; memcpy(cis->lll.access_addr, ind->aa, sizeof(ind->aa)); +#if defined(CONFIG_BT_CTLR_ISOAL_PSN_IGNORE) + cis->pkt_seq_num = 0U; +#endif /* CONFIG_BT_CTLR_ISOAL_PSN_IGNORE */ cis->lll.event_count = LLL_CONN_ISO_EVENT_COUNT_MAX; cis->lll.next_subevent = 0U; cis->lll.sn = 0U; @@ -329,7 +336,6 @@ uint8_t ull_peripheral_iso_setup(struct pdu_data_llctrl_cis_ind *ind, cis->lll.cie = 0U; cis->lll.npi = 0U; cis->lll.flush = LLL_CIS_FLUSH_NONE; - cis->lll.active = 0U; cis->lll.datapath_ready_rx = 0U; cis->lll.tx.payload_count = 0U; cis->lll.rx.payload_count = 0U; diff --git a/subsys/bluetooth/controller/ll_sw/ull_scan.c b/subsys/bluetooth/controller/ll_sw/ull_scan.c index 1ee99565a13..21d6cc9c401 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_scan.c +++ b/subsys/bluetooth/controller/ll_sw/ull_scan.c @@ -500,6 +500,10 @@ uint8_t ull_scan_enable(struct ll_scan_set *scan) * enabled. */ } + +#if defined(CONFIG_BT_TICKER_EXT) + ll_scan_ticker_ext[handle].ticks_slot_window = 0U; +#endif /* CONFIG_BT_TICKER_EXT */ } /* 1M scan window starts without any offset */ @@ -559,6 +563,10 @@ uint8_t ull_scan_enable(struct ll_scan_set *scan) } else { ticks_offset = 0U; } + +#if defined(CONFIG_BT_TICKER_EXT) + ll_scan_ticker_ext[handle].ticks_slot_window = 0U; +#endif /* CONFIG_BT_TICKER_EXT */ } else { ticks_offset = 0U; } diff --git a/subsys/bluetooth/controller/ll_sw/ull_scan_aux.c b/subsys/bluetooth/controller/ll_sw/ull_scan_aux.c index 26fc6d4806b..ecdfdd60d07 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_scan_aux.c +++ b/subsys/bluetooth/controller/ll_sw/ull_scan_aux.c @@ -666,12 +666,10 @@ void ull_scan_aux_setup(memq_link_t *link, struct node_rx_hdr *rx) HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_XTAL_US); aux->ull.ticks_preempt_to_start = HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_PREEMPT_MIN_US); - aux->ull.ticks_slot = - HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_START_US + - ready_delay_us + - PDU_AC_MAX_US(PDU_AC_EXT_PAYLOAD_RX_SIZE, - lll_aux->phy) + - EVENT_OVERHEAD_END_US); + aux->ull.ticks_slot = HAL_TICKER_US_TO_TICKS_CEIL( + EVENT_OVERHEAD_START_US + ready_delay_us + + PDU_AC_MAX_US(PDU_AC_EXT_PAYLOAD_RX_SIZE, lll_aux->phy) + + EVENT_OVERHEAD_END_US); ticks_slot_offset = MAX(aux->ull.ticks_active_to_start, aux->ull.ticks_prepare_to_start); @@ -684,6 +682,13 @@ void ull_scan_aux_setup(memq_link_t *link, struct node_rx_hdr *rx) ticks_aux_offset = HAL_TICKER_US_TO_TICKS(aux_offset_us); +#if (CONFIG_BT_CTLR_ULL_HIGH_PRIO == CONFIG_BT_CTLR_ULL_LOW_PRIO) + /* disable ticker job, in order to chain yield and start to reduce + * CPU use by reducing successive calls to ticker_job(). + */ + mayfly_enable(TICKER_USER_ID_ULL_HIGH, TICKER_USER_ID_ULL_LOW, 0); +#endif + /* Yield the primary scan window or auxiliary or periodic sync event * in ticker. */ @@ -716,6 +721,13 @@ void ull_scan_aux_setup(memq_link_t *link, struct node_rx_hdr *rx) ((ticker_status == TICKER_STATUS_FAILURE) && IS_ENABLED(CONFIG_BT_TICKER_LOW_LAT))); +#if (CONFIG_BT_CTLR_ULL_HIGH_PRIO == CONFIG_BT_CTLR_ULL_LOW_PRIO) + /* enable ticker job, queued ticker operation will be handled + * thereafter. + */ + mayfly_enable(TICKER_USER_ID_ULL_HIGH, TICKER_USER_ID_ULL_LOW, 1); +#endif + return; ull_scan_aux_rx_flush: diff --git a/subsys/bluetooth/controller/ll_sw/ull_sched.c b/subsys/bluetooth/controller/ll_sw/ull_sched.c index a445d986248..4eb7687b97d 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_sched.c +++ b/subsys/bluetooth/controller/ll_sw/ull_sched.c @@ -321,7 +321,7 @@ static int group_free_slot_get(uint8_t user_id, uint32_t ticks_slot_abs, if (false) { -#if defined(CONFIG_BT_BROADCASTER) +#if defined(CONFIG_BT_BROADCASTER) && CONFIG_BT_CTLR_ADV_AUX_SET > 0 } else if (IN_RANGE(ticker_id, TICKER_ID_ADV_AUX_BASE, TICKER_ID_ADV_AUX_LAST)) { const struct ll_adv_aux_set *aux; @@ -391,7 +391,7 @@ static int group_free_slot_get(uint8_t user_id, uint32_t ticks_slot_abs, #endif /* CONFIG_BT_CTLR_ADV_ISO */ #endif /* CONFIG_BT_CTLR_ADV_PERIODIC */ -#endif /* CONFIG_BT_BROADCASTER */ +#endif /* CONFIG_BT_BROADCASTER && CONFIG_BT_CTLR_ADV_AUX_SET > 0 */ #if defined(CONFIG_BT_CONN) } else if (IN_RANGE(ticker_id, TICKER_ID_CONN_BASE, @@ -656,7 +656,7 @@ static struct ull_hdr *ull_hdr_get_cb(uint8_t ticker_id, uint32_t *ticks_slot) time_us = ull_adv_aux_time_get(aux, PDU_AC_PAYLOAD_SIZE_MAX, PDU_AC_PAYLOAD_SIZE_MAX); - *ticks_slot = HAL_TICKER_US_TO_TICKS(time_us); + *ticks_slot = HAL_TICKER_US_TO_TICKS_CEIL(time_us); } else { *ticks_slot = aux->ull.ticks_slot; @@ -686,7 +686,7 @@ static struct ull_hdr *ull_hdr_get_cb(uint8_t ticker_id, uint32_t *ticks_slot) uint32_t time_us; time_us = ull_adv_sync_time_get(sync, PDU_AC_PAYLOAD_SIZE_MAX); - *ticks_slot = HAL_TICKER_US_TO_TICKS(time_us); + *ticks_slot = HAL_TICKER_US_TO_TICKS_CEIL(time_us); } else { *ticks_slot = sync->ull.ticks_slot; } @@ -701,7 +701,10 @@ static struct ull_hdr *ull_hdr_get_cb(uint8_t ticker_id, uint32_t *ticks_slot) adv_iso = ull_adv_iso_get(ticker_id - TICKER_ID_ADV_ISO_BASE); if (adv_iso) { - *ticks_slot = adv_iso->ull.ticks_slot; + uint32_t time_us; + + time_us = ull_adv_iso_max_time_get(adv_iso); + *ticks_slot = HAL_TICKER_US_TO_TICKS_CEIL(time_us); return &adv_iso->ull; } @@ -745,9 +748,11 @@ static struct ull_hdr *ull_hdr_get_cb(uint8_t ticker_id, uint32_t *ticks_slot) #endif /* !CONFIG_BT_CTLR_PHY_CODED */ time_us = EVENT_OVERHEAD_START_US + + EVENT_OVERHEAD_END_US + ready_delay_us + max_rx_time + - EVENT_IFS_US + max_tx_time; - *ticks_slot = HAL_TICKER_US_TO_TICKS(time_us); + EVENT_IFS_US + max_tx_time + + (EVENT_CLOCK_JITTER_US << 1); + *ticks_slot = HAL_TICKER_US_TO_TICKS_CEIL(time_us); } else { *ticks_slot = conn->ull.ticks_slot; } diff --git a/subsys/bluetooth/controller/ll_sw/ull_sync.c b/subsys/bluetooth/controller/ll_sw/ull_sync.c index 6c674885810..265475196fd 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_sync.c +++ b/subsys/bluetooth/controller/ll_sw/ull_sync.c @@ -826,12 +826,10 @@ void ull_sync_setup(struct ll_scan_set *scan, struct ll_scan_aux_set *aux, HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_XTAL_US); sync->ull.ticks_preempt_to_start = HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_PREEMPT_MIN_US); - sync->ull.ticks_slot = - HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_START_US + - ready_delay_us + - PDU_AC_MAX_US(PDU_AC_EXT_PAYLOAD_RX_SIZE, - lll->phy) + - EVENT_OVERHEAD_END_US); + sync->ull.ticks_slot = HAL_TICKER_US_TO_TICKS_CEIL( + EVENT_OVERHEAD_START_US + ready_delay_us + + PDU_AC_MAX_US(PDU_AC_EXT_PAYLOAD_RX_SIZE, lll->phy) + + EVENT_OVERHEAD_END_US); ticks_slot_offset = MAX(sync->ull.ticks_active_to_start, sync->ull.ticks_prepare_to_start); diff --git a/subsys/bluetooth/controller/ll_sw/ull_sync_iso.c b/subsys/bluetooth/controller/ll_sw/ull_sync_iso.c index fbacb56f8b4..513ee3cdbed 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_sync_iso.c +++ b/subsys/bluetooth/controller/ll_sw/ull_sync_iso.c @@ -454,10 +454,9 @@ void ull_sync_iso_setup(struct ll_sync_iso_set *sync_iso, /* Initialize payload pointers */ lll->payload_count_max = PDU_BIG_PAYLOAD_COUNT_MAX; - lll->payload_head = 0U; lll->payload_tail = 0U; for (int i = 0; i < CONFIG_BT_CTLR_SYNC_ISO_STREAM_MAX; i++) { - for (int j = 0; j < PDU_BIG_PAYLOAD_COUNT_MAX; j++) { + for (int j = 0; j < lll->payload_count_max; j++) { lll->payload[i][j] = NULL; } } @@ -509,11 +508,11 @@ void ull_sync_iso_setup(struct ll_sync_iso_set *sync_iso, HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_XTAL_US); sync_iso->ull.ticks_preempt_to_start = HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_PREEMPT_MIN_US); - sync_iso->ull.ticks_slot = HAL_TICKER_US_TO_TICKS( - EVENT_OVERHEAD_START_US + ready_delay_us + - PDU_BIS_MAX_US(PDU_AC_EXT_PAYLOAD_SIZE_MAX, lll->enc, - lll->phy) + - EVENT_OVERHEAD_END_US); + sync_iso->ull.ticks_slot = HAL_TICKER_US_TO_TICKS_CEIL( + EVENT_OVERHEAD_START_US + ready_delay_us + + PDU_BIS_MAX_US(PDU_AC_EXT_PAYLOAD_SIZE_MAX, lll->enc, + lll->phy) + + EVENT_OVERHEAD_END_US); ticks_slot_offset = MAX(sync_iso->ull.ticks_active_to_start, sync_iso->ull.ticks_prepare_to_start); diff --git a/subsys/bluetooth/controller/ticker/ticker.c b/subsys/bluetooth/controller/ticker/ticker.c index 679467d148c..83bd6437d59 100644 --- a/subsys/bluetooth/controller/ticker/ticker.c +++ b/subsys/bluetooth/controller/ticker/ticker.c @@ -55,9 +55,11 @@ struct ticker_node { uint8_t force:1; /* If non-zero, node timeout should * be forced at next expiration */ +#if defined(CONFIG_BT_TICKER_PREFER_START_BEFORE_STOP) uint8_t start_pending:1; /* If non-zero, start is pending for * bottom half of ticker_job. */ +#endif /* CONFIG_BT_TICKER_PREFER_START_BEFORE_STOP */ uint32_t ticks_periodic; /* If non-zero, interval * between expirations */ @@ -1907,12 +1909,15 @@ static inline uint8_t ticker_job_list_manage(struct ticker_instance *instance, /* if op is start, then skip update and stop ops */ if (user_op->op < TICKER_USER_OP_TYPE_UPDATE) { +#if defined(CONFIG_BT_TICKER_PREFER_START_BEFORE_STOP) if (user_op->op == TICKER_USER_OP_TYPE_START) { /* Set start pending to validate a * successive, inline stop operation. */ ticker->start_pending = 1U; } +#endif /* CONFIG_BT_TICKER_PREFER_START_BEFORE_STOP */ + continue; } @@ -1923,7 +1928,10 @@ static inline uint8_t ticker_job_list_manage(struct ticker_instance *instance, * set status and continue. */ if ((user_op->op > TICKER_USER_OP_TYPE_STOP_ABS) || - (((state == 0U) && !ticker->start_pending) && + ((state == 0U) && +#if defined(CONFIG_BT_TICKER_PREFER_START_BEFORE_STOP) + !ticker->start_pending && +#endif /* CONFIG_BT_TICKER_PREFER_START_BEFORE_STOP */ (user_op->op != TICKER_USER_OP_TYPE_YIELD_ABS)) || ((user_op->op == TICKER_USER_OP_TYPE_UPDATE) && (user_op->params.update.ticks_drift_plus == 0U) && @@ -2318,43 +2326,50 @@ static uint8_t ticker_job_reschedule_in_window(struct ticker_instance *instance, uint32_t ticks_elapsed) { struct ticker_node *nodes; - uint8_t rescheduling = 1U; - uint8_t rescheduled = 0U; + uint8_t rescheduling; + uint8_t rescheduled; nodes = &instance->nodes[0]; /* Do until all pending re-schedules handled */ + rescheduling = 1U; + rescheduled = 0U; while (rescheduling) { - uint32_t ticks_to_expire_offset = 0U; - uint32_t ticks_start_offset = 0U; - uint32_t window_start_ticks = 0U; - uint32_t ticks_slot_window = 0U; - uint32_t ticks_to_expire = 0U; + struct ticker_node *ticker_resched; + uint32_t ticks_to_expire_offset; + uint8_t ticker_id_resched_prev; struct ticker_ext *ext_data; - struct ticker_node *ticker; - uint8_t ticker_id_head; + uint32_t ticks_start_offset; + uint32_t window_start_ticks; + uint32_t ticks_slot_window; + uint8_t ticker_id_resched; + uint32_t ticks_to_expire; uint8_t ticker_id_prev; - uint8_t ticker_id_iter; + uint8_t ticker_id_next; uint32_t ticks_slot; rescheduling = 0U; /* Find first pending re-schedule */ - ticker_id_head = instance->ticker_id_head; - while (ticker_id_head != TICKER_NULL) { - ticker = &nodes[ticker_id_head]; - if (TICKER_RESCHEDULE_PENDING(ticker)) { + ticker_id_resched_prev = TICKER_NULL; + ticker_id_resched = instance->ticker_id_head; + while (ticker_id_resched != TICKER_NULL) { + ticker_resched = &nodes[ticker_id_resched]; + if (TICKER_RESCHEDULE_PENDING(ticker_resched)) { /* Pending reschedule found */ break; } - ticker_id_head = ticker->next; + + ticker_id_resched_prev = ticker_id_resched; + ticker_id_resched = ticker_resched->next; } - if (ticker_id_head == TICKER_NULL) { + if (ticker_id_resched == TICKER_NULL) { /* Done */ break; } /* Check for intersection with already active node */ + window_start_ticks = 0U; if (instance->ticks_slot_previous > ticks_elapsed) { /* Active node intersects - window starts after end of * active slot @@ -2363,7 +2378,7 @@ static uint8_t ticker_job_reschedule_in_window(struct ticker_instance *instance, ticks_elapsed; } - ticker_id_iter = nodes[ticker_id_head].next; + ticker_id_next = ticker_resched->next; /* If drift was applied to this node, this must be * taken into consideration. Reduce the window with @@ -2374,43 +2389,50 @@ static uint8_t ticker_job_reschedule_in_window(struct ticker_instance *instance, * ticker would have the best possible window to re-schedule in * and not be restricted to ticks_slot_window - ticks_drift. */ - ext_data = ticker->ext_data; + ext_data = ticker_resched->ext_data; if (ext_data->ticks_drift < ext_data->ticks_slot_window) { ticks_slot_window = ext_data->ticks_slot_window - ext_data->ticks_drift; } else { /* Window has been exhausted - we can't reschedule */ - ticker_id_iter = TICKER_NULL; + ticker_id_next = TICKER_NULL; + + /* Assignment will be unused when TICKER_NULL */ + ticks_slot_window = 0U; } /* Use ticker's reserved time ticks_slot, else for unreserved * tickers use the reschedule margin as ticks_slot. */ - if (ticker->ticks_slot) { - ticks_slot = ticker->ticks_slot; + if (ticker_resched->ticks_slot) { + ticks_slot = ticker_resched->ticks_slot; } else { - LL_ASSERT(TICKER_HAS_SLOT_WINDOW(ticker)); + LL_ASSERT(TICKER_HAS_SLOT_WINDOW(ticker_resched)); ticks_slot = HAL_TICKER_RESCHEDULE_MARGIN; } /* Try to find available slot for re-scheduling */ - while ((ticker_id_iter != TICKER_NULL) && + ticks_to_expire_offset = 0U; + ticks_start_offset = 0U; + ticks_to_expire = 0U; + while ((ticker_id_next != TICKER_NULL) && ((ticks_start_offset + ticks_slot) <= ticks_slot_window)) { - uint32_t window_end_ticks = 0U; - struct ticker_node *node; + struct ticker_node *ticker_next; + uint32_t window_end_ticks; - node = &nodes[ticker_id_iter]; - ticks_to_expire_offset += node->ticks_to_expire; + ticker_next = &nodes[ticker_id_next]; + ticks_to_expire_offset += ticker_next->ticks_to_expire; /* Skip other pending re-schedule nodes and * tickers with no reservation or not periodic */ - if (TICKER_RESCHEDULE_PENDING(node) || - !node->ticks_slot || - !node->ticks_periodic) { - ticker_id_iter = node->next; + if (TICKER_RESCHEDULE_PENDING(ticker_next) || + !ticker_next->ticks_slot || + !ticker_next->ticks_periodic) { + ticker_id_next = ticker_next->next; + continue; } @@ -2436,7 +2458,7 @@ static uint8_t ticker_job_reschedule_in_window(struct ticker_instance *instance, */ if (window_end_ticks > (ticks_start_offset + ticks_slot)) { - if (!ticker->ticks_slot) { + if (!ticker_resched->ticks_slot) { /* Place at start of window */ ticks_to_expire = window_start_ticks; } else { @@ -2469,10 +2491,10 @@ static uint8_t ticker_job_reschedule_in_window(struct ticker_instance *instance, */ ticks_start_offset += ticks_to_expire_offset; window_start_ticks = ticks_start_offset + - node->ticks_slot; + ticker_next->ticks_slot; ticks_to_expire_offset = 0U; - if (!ticker->ticks_slot) { + if (!ticker_resched->ticks_slot) { /* Try at the end of the next node */ ticks_to_expire = window_start_ticks; } else { @@ -2485,56 +2507,63 @@ static uint8_t ticker_job_reschedule_in_window(struct ticker_instance *instance, ticks_slot; } - ticker_id_iter = node->next; + ticker_id_next = ticker_next->next; } ext_data->ticks_drift += ticks_to_expire - - ticker->ticks_to_expire; - ticker->ticks_to_expire = ticks_to_expire; - ticker_id_iter = nodes[ticker_id_head].next; - ticker_id_prev = TICKER_NULL; + ticker_resched->ticks_to_expire; + ticker_resched->ticks_to_expire = ticks_to_expire; /* Place the ticker node sorted by expiration time and adjust * delta times */ - while (ticker_id_iter != TICKER_NULL) { - struct ticker_node *node; + ticker_id_next = ticker_resched->next; + ticker_id_prev = TICKER_NULL; + while (ticker_id_next != TICKER_NULL) { + struct ticker_node *ticker_next; - node = &nodes[ticker_id_iter]; - if (ticker->ticks_to_expire > node->ticks_to_expire) { + ticker_next = &nodes[ticker_id_next]; + if (ticker_resched->ticks_to_expire > + ticker_next->ticks_to_expire) { /* Node is after this - adjust delta */ - ticker->ticks_to_expire -= - node->ticks_to_expire; + ticker_resched->ticks_to_expire -= + ticker_next->ticks_to_expire; } else { /* Node is before this one */ - node->ticks_to_expire -= - ticker->ticks_to_expire; + ticker_next->ticks_to_expire -= + ticker_resched->ticks_to_expire; break; } - ticker_id_prev = ticker_id_iter; - ticker_id_iter = node->next; + ticker_id_prev = ticker_id_next; + ticker_id_next = ticker_next->next; } + /* If the node moved in the list, insert it */ if (ticker_id_prev != TICKER_NULL) { - /* Node did not become the first - update head and - * insert node after 'previous' - */ - instance->ticker_id_head = nodes[ticker_id_head].next; + /* Remove node from its current position in list */ + if (ticker_id_resched_prev != TICKER_NULL) { + /* Node was not at the head of the list */ + nodes[ticker_id_resched_prev].next = + ticker_resched->next; + } else { + /* Node was at the head, move head forward */ + instance->ticker_id_head = ticker_resched->next; + } /* Link inserted node */ - nodes[ticker_id_head].next = nodes[ticker_id_prev].next; - nodes[ticker_id_prev].next = ticker_id_head; + ticker_resched->next = nodes[ticker_id_prev].next; + nodes[ticker_id_prev].next = ticker_id_resched; } /* Remove latency added in ticker_worker */ - ticker->lazy_current--; + ticker_resched->lazy_current--; /* Prevent repeated re-scheduling */ ext_data->reschedule_state = TICKER_RESCHEDULE_STATE_DONE; #if defined(CONFIG_BT_TICKER_EXT_EXPIRE_INFO) - ticker_mark_expire_info_outdated(instance, ticker_id_head); + ticker_mark_expire_info_outdated(instance, ticker_id_resched); #endif /* CONFIG_BT_TICKER_EXT_EXPIRE_INFO */ /* Check for other pending re-schedules and set exit flag */ @@ -2731,7 +2760,9 @@ static inline void ticker_job_list_insert(struct ticker_instance *instance, continue; } +#if defined(CONFIG_BT_TICKER_PREFER_START_BEFORE_STOP) ticker->start_pending = 0U; +#endif /* CONFIG_BT_TICKER_PREFER_START_BEFORE_STOP */ if (((ticker->req - ticker->ack) & 0xff) != 0U) { @@ -3149,6 +3180,7 @@ void ticker_job(void *param) instance->job_guard = 0U; /* trigger worker if deferred */ + cpu_dmb(); if (instance->worker_trigger || compare_trigger) { instance->sched_cb(TICKER_CALL_ID_JOB, TICKER_CALL_ID_WORKER, 1, instance); diff --git a/subsys/bluetooth/controller/util/mayfly.c b/subsys/bluetooth/controller/util/mayfly.c index e8de3acfd80..d45569c6a35 100644 --- a/subsys/bluetooth/controller/util/mayfly.c +++ b/subsys/bluetooth/controller/util/mayfly.c @@ -6,8 +6,13 @@ */ #include + +#include #include #include + +#include "hal/cpu.h" + #include "memq.h" #include "mayfly.h" @@ -154,10 +159,12 @@ static void dequeue(uint8_t callee_id, uint8_t caller_id, memq_link_t *link, m->_link = link; /* reset mayfly state to idle */ + cpu_dmb(); ack = m->_ack; m->_ack = req; /* re-insert, if re-pended by interrupt */ + cpu_dmb(); if (((m->_req - ack) & 0x03) == 1U) { #if defined(MAYFLY_UT) printk("%s: RACE\n", __func__); diff --git a/subsys/bluetooth/host/Kconfig b/subsys/bluetooth/host/Kconfig index 19754705200..2e740686b69 100644 --- a/subsys/bluetooth/host/Kconfig +++ b/subsys/bluetooth/host/Kconfig @@ -23,6 +23,13 @@ config BT_LONG_WQ_PRIO int "Long workqueue priority. Should be pre-emptible." default 10 range 0 NUM_PREEMPT_PRIORITIES + +config BT_LONG_WQ_INIT_PRIO + int "Long workqueue init priority" + default 50 + help + Init priority level to setup the long workqueue. + endif # BT_LONG_WQ config BT_HCI_HOST @@ -156,12 +163,13 @@ rsource "../mesh/Kconfig" rsource "../audio/Kconfig" config BT_HOST_CRYPTO - # Hidden option that compiles in AES encryption support using TinyCrypt - # library if this is not provided by the controller implementation. - bool + bool "Use crypto functionality implemented in the Bluetooth host" default y if !BT_CTLR_CRYPTO select TINYCRYPT select TINYCRYPT_AES + help + The option adds the AES encryption support using TinyCrypt + library if this is not provided by the controller implementation. config BT_HOST_CRYPTO_PRNG bool "Use Tinycrypt library for random number generation" diff --git a/subsys/bluetooth/host/Kconfig.l2cap b/subsys/bluetooth/host/Kconfig.l2cap index acb948c425a..f8d602acd57 100644 --- a/subsys/bluetooth/host/Kconfig.l2cap +++ b/subsys/bluetooth/host/Kconfig.l2cap @@ -66,7 +66,7 @@ config BT_L2CAP_DYNAMIC_CHANNEL allowing the creation of dynamic L2CAP Channels. config BT_L2CAP_ECRED - bool "L2CAP Enhanced Credit Based Flow Control support" + bool "L2CAP Enhanced Credit Based Flow Control support [EXPERIMENTAL]" depends on BT_L2CAP_DYNAMIC_CHANNEL help This option enables support for LE Connection oriented Channels with diff --git a/subsys/bluetooth/host/att.c b/subsys/bluetooth/host/att.c index d2c41eaa928..f5ae3768cec 100644 --- a/subsys/bluetooth/host/att.c +++ b/subsys/bluetooth/host/att.c @@ -670,11 +670,13 @@ static struct net_buf *bt_att_chan_create_pdu(struct bt_att_chan *chan, uint8_t switch (att_op_get_type(op)) { case ATT_RESPONSE: - case ATT_CONFIRMATION: - /* Use a timeout only when responding/confirming */ + /* Use a timeout only when responding */ timeout = BT_ATT_TIMEOUT; re_use = true; break; + case ATT_CONFIRMATION: + timeout = BT_ATT_TIMEOUT; + break; default: timeout = K_FOREVER; } @@ -701,7 +703,7 @@ static struct net_buf *bt_att_chan_create_pdu(struct bt_att_chan *chan, uint8_t * This is better than an assert as an assert would * allow a peer to DoS us. */ - LOG_ERR("already processing a transaction on chan %p", chan); + LOG_ERR("already processing a REQ/RSP on chan %p", chan); return NULL; } diff --git a/subsys/bluetooth/host/conn.c b/subsys/bluetooth/host/conn.c index 5e69c8006c9..0e355c36390 100644 --- a/subsys/bluetooth/host/conn.c +++ b/subsys/bluetooth/host/conn.c @@ -655,21 +655,6 @@ static int do_send_frag(struct bt_conn *conn, struct net_buf *buf, uint8_t flags return err; } -static size_t iso_hdr_len(struct net_buf *buf, struct bt_conn *conn) -{ -#if defined(CONFIG_BT_ISO) - if (conn->type == BT_CONN_TYPE_ISO) { - if (tx_data(buf)->iso_has_ts) { - return BT_HCI_ISO_TS_DATA_HDR_SIZE; - } else { - return BT_HCI_ISO_DATA_HDR_SIZE; - } - } -#endif - - return 0; -} - static int send_frag(struct bt_conn *conn, struct net_buf *buf, struct net_buf *frag, uint8_t flags) @@ -682,9 +667,7 @@ static int send_frag(struct bt_conn *conn, /* Add the data to the buffer */ if (frag) { - size_t iso_hdr = flags == FRAG_START ? iso_hdr_len(buf, conn) : 0; - uint16_t frag_len = MIN(conn_mtu(conn) + iso_hdr, - net_buf_tailroom(frag)); + uint16_t frag_len = MIN(conn_mtu(conn), net_buf_tailroom(frag)); net_buf_add_mem(frag, buf->data, frag_len); net_buf_pull(buf, frag_len); @@ -728,15 +711,11 @@ static struct net_buf *create_frag(struct bt_conn *conn, struct net_buf *buf) /* Fragments never have a TX completion callback */ tx_data(frag)->tx = NULL; tx_data(frag)->is_cont = false; + tx_data(frag)->iso_has_ts = tx_data(buf)->iso_has_ts; return frag; } -static bool fits_single_ctlr_buf(struct net_buf *buf, struct bt_conn *conn) -{ - return buf->len - iso_hdr_len(buf, conn) <= conn_mtu(conn); -} - static int send_buf(struct bt_conn *conn, struct net_buf *buf) { struct net_buf *frag; @@ -746,7 +725,7 @@ static int send_buf(struct bt_conn *conn, struct net_buf *buf) LOG_DBG("conn %p buf %p len %u", conn, buf, buf->len); /* Send directly if the packet fits the ACL MTU */ - if (fits_single_ctlr_buf(buf, conn) && !tx_data(buf)->is_cont) { + if (buf->len <= conn_mtu(conn) && !tx_data(buf)->is_cont) { LOG_DBG("send single"); return send_frag(conn, buf, NULL, FRAG_SINGLE); } @@ -2635,16 +2614,116 @@ static int bt_conn_get_tx_power_level(struct bt_conn *conn, uint8_t type, return 0; } +#if defined(CONFIG_BT_TRANSMIT_POWER_CONTROL) +void notify_tx_power_report(struct bt_conn *conn, + struct bt_conn_le_tx_power_report report) +{ + for (struct bt_conn_cb *cb = callback_list; cb; cb = cb->_next) { + if (cb->tx_power_report) { + cb->tx_power_report(conn, &report); + } + } + + STRUCT_SECTION_FOREACH(bt_conn_cb, cb) + { + if (cb->tx_power_report) { + cb->tx_power_report(conn, &report); + } + } +} + +int bt_conn_le_enhanced_get_tx_power_level(struct bt_conn *conn, + struct bt_conn_le_tx_power *tx_power) +{ + int err; + struct bt_hci_rp_le_read_tx_power_level *rp; + struct net_buf *rsp; + struct bt_hci_cp_le_read_tx_power_level *cp; + struct net_buf *buf; + + if (!tx_power->phy) { + return -EINVAL; + } + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_ENH_READ_TX_POWER_LEVEL, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->handle = sys_cpu_to_le16(conn->handle); + cp->phy = tx_power->phy; + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_ENH_READ_TX_POWER_LEVEL, buf, &rsp); + if (err) { + return err; + } + + rp = (void *) rsp->data; + tx_power->phy = rp->phy; + tx_power->current_level = rp->current_tx_power_level; + tx_power->max_level = rp->max_tx_power_level; + net_buf_unref(rsp); + + return 0; +} + +int bt_conn_le_get_remote_tx_power_level(struct bt_conn *conn, + enum bt_conn_le_tx_power_phy phy) +{ + struct bt_hci_cp_le_read_tx_power_level *cp; + struct net_buf *buf; + + if (!phy) { + return -EINVAL; + } + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_READ_REMOTE_TX_POWER_LEVEL, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->handle = sys_cpu_to_le16(conn->handle); + cp->phy = phy; + + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_REMOTE_TX_POWER_LEVEL, buf, NULL); +} + +int bt_conn_le_set_tx_power_report_enable(struct bt_conn *conn, + bool local_enable, + bool remote_enable) +{ + struct bt_hci_cp_le_set_tx_power_report_enable *cp; + struct net_buf *buf; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_TX_POWER_REPORT_ENABLE, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->handle = sys_cpu_to_le16(conn->handle); + cp->local_enable = local_enable ? BT_HCI_LE_TX_POWER_REPORT_ENABLE : + BT_HCI_LE_TX_POWER_REPORT_DISABLE; + cp->remote_enable = remote_enable ? BT_HCI_LE_TX_POWER_REPORT_ENABLE : + BT_HCI_LE_TX_POWER_REPORT_DISABLE; + + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_TX_POWER_REPORT_ENABLE, buf, NULL); +} +#endif /* CONFIG_BT_TRANSMIT_POWER_CONTROL */ + int bt_conn_le_get_tx_power_level(struct bt_conn *conn, struct bt_conn_le_tx_power *tx_power_level) { int err; if (tx_power_level->phy != 0) { - /* Extend the implementation when LE Enhanced Read Transmit - * Power Level HCI command is available for use. - */ - return -ENOTSUP; + if (IS_ENABLED(CONFIG_BT_TRANSMIT_POWER_CONTROL)) { + return bt_conn_le_enhanced_get_tx_power_level(conn, tx_power_level); + } else { + return -ENOTSUP; + } } err = bt_conn_get_tx_power_level(conn, BT_TX_POWER_LEVEL_CURRENT, diff --git a/subsys/bluetooth/host/conn_internal.h b/subsys/bluetooth/host/conn_internal.h index bddbf11ae7d..a2d8e28bd4d 100644 --- a/subsys/bluetooth/host/conn_internal.h +++ b/subsys/bluetooth/host/conn_internal.h @@ -353,6 +353,9 @@ void notify_le_phy_updated(struct bt_conn *conn); bool le_param_req(struct bt_conn *conn, struct bt_le_conn_param *param); +void notify_tx_power_report(struct bt_conn *conn, + struct bt_conn_le_tx_power_report report); + #if defined(CONFIG_BT_SMP) /* If role specific LTK is present */ bool bt_conn_ltk_present(const struct bt_conn *conn); diff --git a/subsys/bluetooth/host/gatt.c b/subsys/bluetooth/host/gatt.c index 39f01bc45b4..93dfe555580 100644 --- a/subsys/bluetooth/host/gatt.c +++ b/subsys/bluetooth/host/gatt.c @@ -1318,7 +1318,6 @@ static void clear_ccc_cfg(struct bt_gatt_ccc_cfg *cfg) bt_addr_le_copy(&cfg->peer, BT_ADDR_LE_ANY); cfg->id = 0U; cfg->value = 0U; - cfg->link_encrypted = false; } static void gatt_store_ccc_cf(uint8_t id, const bt_addr_le_t *peer_addr); @@ -2050,34 +2049,6 @@ struct bt_gatt_attr *bt_gatt_attr_next(const struct bt_gatt_attr *attr) return next; } -static bool bt_gatt_ccc_cfg_is_matching_conn(const struct bt_conn *conn, - const struct bt_gatt_ccc_cfg *cfg) -{ - bool conn_encrypted = bt_conn_get_security(conn) >= BT_SECURITY_L2; - - if (cfg->link_encrypted && !conn_encrypted) { - return false; - } - - return bt_conn_is_peer_addr_le(conn, cfg->id, &cfg->peer); -} - -static struct bt_conn *bt_gatt_ccc_cfg_conn_lookup(const struct bt_gatt_ccc_cfg *cfg) -{ - struct bt_conn *conn; - - conn = bt_conn_lookup_addr_le(cfg->id, &cfg->peer); - if (conn) { - if (bt_gatt_ccc_cfg_is_matching_conn(conn, cfg)) { - return conn; - } - - bt_conn_unref(conn); - } - - return NULL; -} - static struct bt_gatt_ccc_cfg *find_ccc_cfg(const struct bt_conn *conn, struct _bt_gatt_ccc *ccc) { @@ -2085,7 +2056,8 @@ static struct bt_gatt_ccc_cfg *find_ccc_cfg(const struct bt_conn *conn, struct bt_gatt_ccc_cfg *cfg = &ccc->cfg[i]; if (conn) { - if (bt_gatt_ccc_cfg_is_matching_conn(conn, cfg)) { + if (bt_conn_is_peer_addr_le(conn, cfg->id, + &cfg->peer)) { return cfg; } } else if (bt_addr_le_eq(&cfg->peer, BT_ADDR_LE_ANY)) { @@ -2179,7 +2151,6 @@ ssize_t bt_gatt_attr_write_ccc(struct bt_conn *conn, bt_addr_le_copy(&cfg->peer, &conn->le.dst); cfg->id = conn->id; - cfg->link_encrypted = (bt_conn_get_security(conn) >= BT_SECURITY_L2); } /* Confirm write if cfg is managed by application */ @@ -3259,7 +3230,8 @@ static uint8_t update_ccc(const struct bt_gatt_attr *attr, uint16_t handle, struct bt_gatt_ccc_cfg *cfg = &ccc->cfg[i]; /* Ignore configuration for different peer or not active */ - if (!cfg->value || !bt_gatt_ccc_cfg_is_matching_conn(conn, cfg)) { + if (!cfg->value || + !bt_conn_is_peer_addr_le(conn, cfg->id, &cfg->peer)) { continue; } @@ -3333,11 +3305,11 @@ static uint8_t disconnected_cb(const struct bt_gatt_attr *attr, uint16_t handle, continue; } - if (!bt_gatt_ccc_cfg_is_matching_conn(conn, cfg)) { + if (!bt_conn_is_peer_addr_le(conn, cfg->id, &cfg->peer)) { struct bt_conn *tmp; /* Skip if there is another peer connected */ - tmp = bt_gatt_ccc_cfg_conn_lookup(cfg); + tmp = bt_conn_lookup_addr_le(cfg->id, &cfg->peer); if (tmp) { if (tmp->state == BT_CONN_CONNECTED) { value_used = true; @@ -3427,7 +3399,7 @@ bool bt_gatt_is_subscribed(struct bt_conn *conn, for (size_t i = 0; i < BT_GATT_CCC_MAX; i++) { const struct bt_gatt_ccc_cfg *cfg = &ccc->cfg[i]; - if (bt_gatt_ccc_cfg_is_matching_conn(conn, cfg) && + if (bt_conn_is_peer_addr_le(conn, cfg->id, &cfg->peer) && (ccc_type & ccc->cfg[i].value)) { return true; } @@ -5578,7 +5550,6 @@ static uint8_t ccc_load(const struct bt_gatt_attr *attr, uint16_t handle, } bt_addr_le_copy(&cfg->peer, load->addr_with_id.addr); cfg->id = load->addr_with_id.id; - cfg->link_encrypted = true; } cfg->value = load->entry->value; diff --git a/subsys/bluetooth/host/hci_core.c b/subsys/bluetooth/host/hci_core.c index b3a7e05891a..7cf699aa8cc 100644 --- a/subsys/bluetooth/host/hci_core.c +++ b/subsys/bluetooth/host/hci_core.c @@ -2392,6 +2392,33 @@ int bt_hci_register_vnd_evt_cb(bt_hci_vnd_evt_cb_t cb) } #endif /* CONFIG_BT_HCI_VS_EVT_USER */ +#if defined(CONFIG_BT_TRANSMIT_POWER_CONTROL) +void bt_hci_le_transmit_power_report(struct net_buf *buf) +{ + struct bt_hci_evt_le_transmit_power_report *evt; + struct bt_conn_le_tx_power_report report; + struct bt_conn *conn; + + evt = net_buf_pull_mem(buf, sizeof(*evt)); + conn = bt_conn_lookup_handle(sys_le16_to_cpu(evt->handle), BT_CONN_TYPE_LE); + if (!conn) { + LOG_ERR("Unknown conn handle 0x%04X for transmit power report", + sys_le16_to_cpu(evt->handle)); + return; + } + + report.reason = evt->reason; + report.phy = evt->phy; + report.tx_power_level = evt->tx_power_level; + report.tx_power_level_flag = evt->tx_power_level_flag; + report.delta = evt->delta; + + notify_tx_power_report(conn, report); + + bt_conn_unref(conn); +} +#endif /* CONFIG_BT_TRANSMIT_POWER_CONTROL */ + static const struct event_handler vs_events[] = { #if defined(CONFIG_BT_DF_VS_CL_IQ_REPORT_16_BITS_IQ_SAMPLES) EVENT_HANDLER(BT_HCI_EVT_VS_LE_CONNECTIONLESS_IQ_REPORT, @@ -2537,6 +2564,10 @@ static const struct event_handler meta_events[] = { EVENT_HANDLER(BT_HCI_EVT_LE_CTE_REQUEST_FAILED, bt_hci_le_df_cte_req_failed, sizeof(struct bt_hci_evt_le_cte_req_failed)), #endif /* CONFIG_BT_DF_CONNECTION_CTE_REQ */ +#if defined(CONFIG_BT_TRANSMIT_POWER_CONTROL) + EVENT_HANDLER(BT_HCI_EVT_LE_TRANSMIT_POWER_REPORT, bt_hci_le_transmit_power_report, + sizeof(struct bt_hci_evt_le_transmit_power_report)), +#endif /* CONFIG_BT_TRANSMIT_POWER_CONTROL */ #if defined(CONFIG_BT_PER_ADV_SYNC_RSP) EVENT_HANDLER(BT_HCI_EVT_LE_PER_ADVERTISING_REPORT_V2, bt_hci_le_per_adv_report_v2, sizeof(struct bt_hci_evt_le_per_advertising_report_v2)), @@ -3078,6 +3109,9 @@ static int le_set_event_mask(void) BT_FEAT_LE_PHY_CODED(bt_dev.le.features))) { mask |= BT_EVT_MASK_LE_PHY_UPDATE_COMPLETE; } + if (IS_ENABLED(CONFIG_BT_TRANSMIT_POWER_CONTROL)) { + mask |= BT_EVT_MASK_LE_TRANSMIT_POWER_REPORTING; + } } if (IS_ENABLED(CONFIG_BT_SMP) && @@ -3425,15 +3459,15 @@ static int set_event_mask(void) return bt_hci_cmd_send_sync(BT_HCI_OP_SET_EVENT_MASK, buf, NULL); } -static const char *ver_str(uint8_t ver) +const char *bt_hci_get_ver_str(uint8_t core_version) { const char * const str[] = { "1.0b", "1.1", "1.2", "2.0", "2.1", "3.0", "4.0", "4.1", "4.2", "5.0", "5.1", "5.2", "5.3", "5.4" }; - if (ver < ARRAY_SIZE(str)) { - return str[ver]; + if (core_version < ARRAY_SIZE(str)) { + return str[core_version]; } return "unknown"; @@ -3474,9 +3508,9 @@ static void bt_dev_show_info(void) } LOG_INF("HCI: version %s (0x%02x) revision 0x%04x, manufacturer 0x%04x", - ver_str(bt_dev.hci_version), bt_dev.hci_version, bt_dev.hci_revision, + bt_hci_get_ver_str(bt_dev.hci_version), bt_dev.hci_version, bt_dev.hci_revision, bt_dev.manufacturer); - LOG_INF("LMP: version %s (0x%02x) subver 0x%04x", ver_str(bt_dev.lmp_version), + LOG_INF("LMP: version %s (0x%02x) subver 0x%04x", bt_hci_get_ver_str(bt_dev.lmp_version), bt_dev.lmp_version, bt_dev.lmp_subversion); } @@ -4110,7 +4144,7 @@ int bt_set_name(const char *name) return 0; } - strncpy(bt_dev.name, name, len); + memcpy(bt_dev.name, name, len); bt_dev.name[len] = '\0'; if (IS_ENABLED(CONFIG_BT_SETTINGS)) { diff --git a/subsys/bluetooth/host/long_wq.c b/subsys/bluetooth/host/long_wq.c index 61f86d0c909..2136739c243 100644 --- a/subsys/bluetooth/host/long_wq.c +++ b/subsys/bluetooth/host/long_wq.c @@ -40,4 +40,4 @@ static int long_wq_init(void) return 0; } -SYS_INIT(long_wq_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); +SYS_INIT(long_wq_init, POST_KERNEL, CONFIG_BT_LONG_WQ_INIT_PRIO); diff --git a/subsys/bluetooth/host/smp.c b/subsys/bluetooth/host/smp.c index add06660bf6..9643982c421 100644 --- a/subsys/bluetooth/host/smp.c +++ b/subsys/bluetooth/host/smp.c @@ -4706,6 +4706,7 @@ static void bt_smp_encrypt_change(struct bt_l2cap_chan *chan, */ if (IS_ENABLED(CONFIG_BT_CENTRAL) && IS_ENABLED(CONFIG_BT_PRIVACY) && + conn->role == BT_HCI_ROLE_CENTRAL && !(smp->remote_dist & BT_SMP_DIST_ID_KEY)) { uint8_t smp_err; diff --git a/subsys/bluetooth/host/testing.c b/subsys/bluetooth/host/testing.c index c3121fd230d..3c118c51e0f 100644 --- a/subsys/bluetooth/host/testing.c +++ b/subsys/bluetooth/host/testing.c @@ -102,6 +102,7 @@ void bt_test_mesh_trans_incomp_timer_exp(void) } } +#if defined(CONFIG_BT_MESH_LOW_POWER) int bt_test_mesh_lpn_group_add(uint16_t group) { bt_mesh_lpn_group_add(group); @@ -115,6 +116,7 @@ int bt_test_mesh_lpn_group_remove(uint16_t *groups, size_t groups_count) return 0; } +#endif /* CONFIG_BT_MESH_LOW_POWER */ int bt_test_mesh_rpl_clear(void) { diff --git a/subsys/bluetooth/mesh/Kconfig b/subsys/bluetooth/mesh/Kconfig index 945b45c12cc..63749a69d56 100644 --- a/subsys/bluetooth/mesh/Kconfig +++ b/subsys/bluetooth/mesh/Kconfig @@ -192,6 +192,38 @@ config BT_MESH_UNPROV_BEACON_INT if BT_MESH_PB_ADV +config BT_MESH_PB_ADV_USE_RELAY_SETS + bool "Use relay advertising sets to send provisioning PDUs" + depends on BT_MESH_RELAY_ADV_SETS > 0 + help + Use relay advertising sets to send provisioning PDUs + +config BT_MESH_PB_ADV_TRANS_PDU_RETRANSMIT_COUNT + int "Link Open and Transaction PDU retransmit count" + default 7 if BT_MESH_PB_ADV_USE_RELAY_SETS + default 0 + range 0 7 + help + Controls the number of retransmissions of original Link Open and Transaction PDU, + in addition to the first transmission. + +config BT_MESH_PB_ADV_TRANS_ACK_RETRANSMIT_COUNT + int "Link Ack and Transaction Ack retransmit count" + default 2 + range 0 7 + help + Controls the number of retransmissions of original Link Ack and Transaction Acknowledgment PDU, + in addition to the first transmission. + +config BT_MESH_PB_ADV_LINK_CLOSE_RETRANSMIT_COUNT + int "Link Close retransmit count" + default 7 if BT_MESH_PB_ADV_USE_RELAY_SETS + default 2 + range 0 7 + help + Controls the number of retransmissions of original Link Close, + in addition to the first transmission. + config BT_MESH_PB_ADV_RETRANS_TIMEOUT int "Timeout value of retransmit provisioning PDUs" default 500 @@ -926,7 +958,7 @@ config BT_MESH_LPN_INIT_POLL_TIMEOUT config BT_MESH_LPN_SCAN_LATENCY int "Latency for enabling scanning" range 0 50 - default 15 + default 2 help Latency in milliseconds that it takes to enable scanning. This is in practice how much time in advance before the Receive Window @@ -1235,6 +1267,14 @@ config BT_MESH_DFD_SRV_TARGETS_MAX This value defines the maximum number of Target nodes the Firmware Distribution Server can target simultaneously. +config BT_MESH_DFD_SRV_OOB_UPLOAD + bool "Support for DFU image OOB upload" + help + This enables support for OOB upload of firmware images for + distribution. This makes several callbacks and use of the init + macro BT_MESH_DFD_SRV_INIT_OOB mandatory. See the API documentation + for bt_mesh_dfd_srv_cb for details about the mandatory callbacks. + endif config BT_MESH_RPR_SRV @@ -1367,12 +1407,27 @@ config BT_MESH_PRIV_BEACON_CLI endif # BT_MESH_PRIV_BEACONS +config BT_MESH_COMP_PST_BUF_SIZE + int "Composition Data Page persistence buffer size" + default 100 + help + Stack allocated buffer used to temporarily hold Composition + Data Pages during flash operations. Should reflect the size + of the largest Composition Data Page present in the application. + Note that this buffer should still be large enough to restore previously stored + pages after a performed device firmware update. + config BT_MESH_COMP_PAGE_1 bool "Support for Composition Data Page 1" depends on BT_MESH_MODEL_EXTENSIONS help Enable support for Composition Data Page 1. +config BT_MESH_COMP_PAGE_2 + bool "Support for Composition Data Page 2" + help + Enable support for Composition Data Page 2. + config BT_MESH_MODEL_EXTENSION_LIST_SIZE int "Model extensions list size" depends on BT_MESH_COMP_PAGE_1 @@ -1705,6 +1760,7 @@ config BT_MESH_SETTINGS_WORKQ_PRIO config BT_MESH_SETTINGS_WORKQ_STACK_SIZE int "Stack size of the settings workq" + default 1200 if BT_MESH_RPR_SRV default 880 help Size of the settings workqueue stack. diff --git a/subsys/bluetooth/mesh/access.c b/subsys/bluetooth/mesh/access.c index c022eedbadc..6ca7dbe580b 100644 --- a/subsys/bluetooth/mesh/access.c +++ b/subsys/bluetooth/mesh/access.c @@ -53,6 +53,7 @@ struct comp_foreach_model_arg { }; static const struct bt_mesh_comp *dev_comp; +static const struct bt_mesh_comp2 *dev_comp2; static uint16_t dev_primary_addr; static void (*msg_cb)(uint32_t opcode, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf); @@ -90,17 +91,13 @@ static struct mod_relation mod_rel_list[MOD_REL_LIST_SIZE]; mod_rel_list[(idx)].idx_ext == 0); \ (idx)++) -#define IS_MOD_BASE(mod, idx) \ +#define IS_MOD_BASE(mod, idx, offset) \ (mod_rel_list[(idx)].elem_base == (mod)->elem_idx && \ - mod_rel_list[(idx)].idx_base == (mod)->mod_idx && \ - !(mod_rel_list[(idx)].elem_ext != (mod)->elem_idx && \ - mod_rel_list[(idx)].idx_ext != (mod)->mod_idx)) + mod_rel_list[(idx)].idx_base == (mod)->mod_idx + (offset)) -#define IS_MOD_EXTENSION(mod, idx) \ +#define IS_MOD_EXTENSION(mod, idx, offset) \ (mod_rel_list[(idx)].elem_ext == (mod)->elem_idx && \ - mod_rel_list[(idx)].idx_ext == (mod)->mod_idx && \ - !(mod_rel_list[(idx)].elem_base != (mod)->elem_idx && \ - mod_rel_list[(idx)].idx_base != (mod)->mod_idx)) + mod_rel_list[(idx)].idx_ext == (mod)->mod_idx + (offset)) #define RELATION_TYPE_EXT 0xFF @@ -112,6 +109,9 @@ static const struct { #if IS_ENABLED(CONFIG_BT_MESH_COMP_PAGE_1) { "bt/mesh/cmp/1", 1, }, #endif +#if IS_ENABLED(CONFIG_BT_MESH_COMP_PAGE_2) + { "bt/mesh/cmp/2", 2, }, +#endif }; void bt_mesh_model_foreach(void (*func)(struct bt_mesh_model *mod, @@ -169,6 +169,18 @@ static void data_buf_add_le16_offset(struct net_buf_simple *buf, } } +static void data_buf_add_mem_offset(struct net_buf_simple *buf, uint8_t *data, size_t len, + size_t *offset) +{ + if (*offset >= len) { + *offset -= len; + return; + } + + net_buf_simple_add_mem(buf, data + *offset, len - *offset); + *offset = 0; +} + static void comp_add_model(struct bt_mesh_model *mod, struct bt_mesh_elem *elem, bool vnd, void *user_data) { @@ -183,20 +195,6 @@ static void comp_add_model(struct bt_mesh_model *mod, struct bt_mesh_elem *elem, } #if defined(CONFIG_BT_MESH_LARGE_COMP_DATA_SRV) -static void data_buf_add_mem_offset(struct net_buf_simple *buf, - const void *mem, size_t len, - size_t *offset) -{ - if (*offset >= len) { - *offset -= len; - return; - } else if (*offset > 0) { - net_buf_simple_add_mem(buf, ((uint8_t *)mem), (len - *offset)); - - } else { - net_buf_simple_add_mem(buf, mem, len); - } -} static size_t metadata_model_size(struct bt_mesh_model *mod, struct bt_mesh_elem *elem, bool vnd) @@ -362,23 +360,6 @@ int bt_mesh_metadata_get_page_0(struct net_buf_simple *buf, size_t offset) } #endif -size_t bt_mesh_comp_page_0_size(void) -{ - const struct bt_mesh_comp *comp; - const struct bt_mesh_elem *elem; - size_t size = 10; - int i; - - comp = bt_mesh_comp_get(); - - for (i = 0; i < comp->elem_count; i++) { - elem = &comp->elem[i]; - size += bt_mesh_comp_elem_size(elem); - } - - return size; -} - static int comp_add_elem(struct net_buf_simple *buf, struct bt_mesh_elem *elem, size_t *offset) { @@ -394,7 +375,7 @@ static int comp_add_elem(struct net_buf_simple *buf, struct bt_mesh_elem *elem, return 0; } - if (net_buf_simple_tailroom(buf) < (elem_size + BT_MESH_MIC_SHORT)) { + if (net_buf_simple_tailroom(buf) < ((elem_size - *offset) + BT_MESH_MIC_SHORT)) { if (IS_ENABLED(CONFIG_BT_MESH_LARGE_COMP_DATA_SRV)) { /* Mesh Profile 1.1 Section 4.4.1.2.2: * If the complete list of models does not fit in the Data field, @@ -471,14 +452,14 @@ int bt_mesh_comp_data_get_page_0(struct net_buf_simple *buf, size_t offset) return 0; } -static uint8_t count_mod_ext(struct bt_mesh_model *mod, uint8_t *max_offset) +static uint8_t count_mod_ext(struct bt_mesh_model *mod, uint8_t *max_offset, uint8_t sig_offset) { int i; uint8_t extensions = 0; int8_t offset, offset_record = 0; MOD_REL_LIST_FOR_EACH(i) { - if (IS_MOD_EXTENSION(mod, i) && + if (IS_MOD_EXTENSION(mod, i, sig_offset) && mod_rel_list[i].type == RELATION_TYPE_EXT) { extensions++; offset = mod_rel_list[i].elem_ext - @@ -495,33 +476,34 @@ static uint8_t count_mod_ext(struct bt_mesh_model *mod, uint8_t *max_offset) return extensions; } -static bool is_cor_present(struct bt_mesh_model *mod, uint8_t *cor_id) +static bool is_cor_present(struct bt_mesh_model *mod, uint8_t *cor_id, uint8_t sig_offset) { int i; - MOD_REL_LIST_FOR_EACH(i) { - if ((IS_MOD_BASE(mod, i) || IS_MOD_EXTENSION(mod, i)) && + MOD_REL_LIST_FOR_EACH(i) + { + if ((IS_MOD_BASE(mod, i, sig_offset) || + IS_MOD_EXTENSION(mod, i, sig_offset)) && mod_rel_list[i].type < RELATION_TYPE_EXT) { if (cor_id) { memcpy(cor_id, &mod_rel_list[i].type, sizeof(uint8_t)); } - return true; } } return false; } -static void prep_model_item_header(struct bt_mesh_model *mod, uint8_t *cor_id, - uint8_t *mod_cnt, struct net_buf_simple *buf) +static void prep_model_item_header(struct bt_mesh_model *mod, uint8_t *cor_id, uint8_t *mod_cnt, + struct net_buf_simple *buf, size_t *offset, uint8_t sig_offset) { uint8_t ext_mod_cnt; bool cor_present; uint8_t mod_elem_info = 0; int8_t max_offset; - ext_mod_cnt = count_mod_ext(mod, &max_offset); - cor_present = is_cor_present(mod, cor_id); + ext_mod_cnt = count_mod_ext(mod, &max_offset, sig_offset); + cor_present = is_cor_present(mod, cor_id, sig_offset); mod_elem_info = ext_mod_cnt << 2; if (ext_mod_cnt > 31 || @@ -532,58 +514,59 @@ static void prep_model_item_header(struct bt_mesh_model *mod, uint8_t *cor_id, if (cor_present) { mod_elem_info |= BIT(0); } - net_buf_simple_add_u8(buf, mod_elem_info); + data_buf_add_u8_offset(buf, mod_elem_info, offset); if (cor_present) { - net_buf_simple_add_u8(buf, *cor_id); + data_buf_add_u8_offset(buf, *cor_id, offset); } memset(mod_cnt, ext_mod_cnt, sizeof(uint8_t)); } static void add_items_to_page(struct net_buf_simple *buf, struct bt_mesh_model *mod, - uint8_t ext_mod_cnt) + uint8_t ext_mod_cnt, size_t *offset, uint8_t sig_offset) { - int i, offset; + int i, elem_offset; uint8_t mod_idx; MOD_REL_LIST_FOR_EACH(i) { - if (IS_MOD_EXTENSION(mod, i)) { - offset = mod->elem_idx - mod_rel_list[i].elem_base; + if (IS_MOD_EXTENSION(mod, i, sig_offset) && + mod_rel_list[i].type == RELATION_TYPE_EXT) { + elem_offset = mod->elem_idx - mod_rel_list[i].elem_base; mod_idx = mod_rel_list[i].idx_base; if (ext_mod_cnt < 32 && - offset < 4 && - offset > -5) { + elem_offset < 4 && + elem_offset > -5) { /* short format */ - if (offset < 0) { - offset += 8; + if (elem_offset < 0) { + elem_offset += 8; } - offset |= mod_idx << 3; - net_buf_simple_add_u8(buf, offset); + elem_offset |= mod_idx << 3; + data_buf_add_u8_offset(buf, elem_offset, offset); } else { /* long format */ - if (offset < 0) { - offset += 256; + if (elem_offset < 0) { + elem_offset += 256; } - net_buf_simple_add_u8(buf, offset); - net_buf_simple_add_u8(buf, mod_idx); + data_buf_add_u8_offset(buf, elem_offset, offset); + data_buf_add_u8_offset(buf, mod_idx, offset); } } } } -static size_t mod_items_size(struct bt_mesh_model *mod) +static size_t mod_items_size(struct bt_mesh_model *mod, uint8_t sig_offset) { int i, offset; size_t temp_size = 0; - int ext_mod_cnt = count_mod_ext(mod, NULL); + int ext_mod_cnt = count_mod_ext(mod, NULL, sig_offset); if (!ext_mod_cnt) { return 0; } MOD_REL_LIST_FOR_EACH(i) { - if (IS_MOD_EXTENSION(mod, i)) { + if (IS_MOD_EXTENSION(mod, i, sig_offset)) { offset = mod->elem_idx - mod_rel_list[i].elem_base; temp_size += (ext_mod_cnt < 32 && offset < 4 && offset > -5) ? 1 : 2; } @@ -597,19 +580,19 @@ static size_t page1_elem_size(struct bt_mesh_elem *elem) size_t temp_size = 2; for (int i = 0; i < elem->model_count; i++) { - temp_size += is_cor_present(&elem->models[i], NULL) ? 2 : 1; - temp_size += mod_items_size(&elem->models[i]); + temp_size += is_cor_present(&elem->models[i], NULL, 0) ? 2 : 1; + temp_size += mod_items_size(&elem->models[i], 0); } for (int i = 0; i < elem->vnd_model_count; i++) { - temp_size += is_cor_present(&elem->vnd_models[i], NULL) ? 2 : 1; - temp_size += mod_items_size(&elem->vnd_models[i]); + temp_size += is_cor_present(&elem->vnd_models[i], NULL, elem->model_count) ? 2 : 1; + temp_size += mod_items_size(&elem->vnd_models[i], elem->model_count); } return temp_size; } -int bt_mesh_comp_data_get_page_1(struct net_buf_simple *buf) +static int bt_mesh_comp_data_get_page_1(struct net_buf_simple *buf, size_t offset) { const struct bt_mesh_comp *comp; uint8_t cor_id = 0; @@ -619,8 +602,14 @@ int bt_mesh_comp_data_get_page_1(struct net_buf_simple *buf) comp = bt_mesh_comp_get(); for (i = 0; i < comp->elem_count; i++) { - if (net_buf_simple_tailroom(buf) < - (page1_elem_size(&comp->elem[i]) + BT_MESH_MIC_SHORT)) { + size_t elem_size = page1_elem_size(&comp->elem[i]); + + if (offset >= elem_size) { + offset -= elem_size; + continue; + } + + if (net_buf_simple_tailroom(buf) < ((elem_size - offset) + BT_MESH_MIC_SHORT)) { if (IS_ENABLED(CONFIG_BT_MESH_LARGE_COMP_DATA_SRV)) { /* Mesh Profile 1.1 Section 4.4.1.2.2: * If the complete list of models does not fit in the Data field, @@ -635,28 +624,80 @@ int bt_mesh_comp_data_get_page_1(struct net_buf_simple *buf) return -E2BIG; } - net_buf_simple_add_u8(buf, comp->elem[i].model_count); - net_buf_simple_add_u8(buf, comp->elem[i].vnd_model_count); + data_buf_add_u8_offset(buf, comp->elem[i].model_count, &offset); + data_buf_add_u8_offset(buf, comp->elem[i].vnd_model_count, &offset); for (j = 0; j < comp->elem[i].model_count; j++) { - prep_model_item_header(&comp->elem[i].models[j], - &cor_id, &ext_mod_cnt, buf); + prep_model_item_header(&comp->elem[i].models[j], &cor_id, &ext_mod_cnt, buf, + &offset, 0); if (ext_mod_cnt != 0) { - add_items_to_page(buf, - &comp->elem[i].models[j], - ext_mod_cnt); + add_items_to_page(buf, &comp->elem[i].models[j], ext_mod_cnt, + &offset, + 0); } } for (j = 0; j < comp->elem[i].vnd_model_count; j++) { - prep_model_item_header(&comp->elem[i].vnd_models[j], - &cor_id, &ext_mod_cnt, buf); + prep_model_item_header(&comp->elem[i].vnd_models[j], &cor_id, &ext_mod_cnt, + buf, &offset, + comp->elem[i].model_count); if (ext_mod_cnt != 0) { - add_items_to_page(buf, - &comp->elem[i].vnd_models[j], - ext_mod_cnt); + add_items_to_page(buf, &comp->elem[i].vnd_models[j], ext_mod_cnt, + &offset, + comp->elem[i].model_count); + } + } + } + return 0; +} + +static int bt_mesh_comp_data_get_page_2(struct net_buf_simple *buf, size_t offset) +{ + if (!dev_comp2) { + LOG_ERR("Composition data P2 not registered"); + return -ENODEV; + } + + size_t elem_size; + + for (int i = 0; i < dev_comp2->record_cnt; i++) { + elem_size = + 8 + dev_comp2->record[i].elem_offset_cnt + dev_comp2->record[i].data_len; + if (offset >= elem_size) { + offset -= elem_size; + continue; + } + + if (net_buf_simple_tailroom(buf) < ((elem_size - offset) + BT_MESH_MIC_SHORT)) { + if (IS_ENABLED(CONFIG_BT_MESH_LARGE_COMP_DATA_SRV)) { + /* Mesh Profile 1.1 Section 4.4.1.2.2: + * If the complete list of models does not fit in the Data field, + * the element shall not be reported. + */ + LOG_DBG("Record 0x%04x didn't fit in the Data field", i); + return 0; } + + LOG_ERR("Too large device composition"); + return -E2BIG; + } + + data_buf_add_le16_offset(buf, dev_comp2->record[i].id, &offset); + data_buf_add_u8_offset(buf, dev_comp2->record[i].version.x, &offset); + data_buf_add_u8_offset(buf, dev_comp2->record[i].version.y, &offset); + data_buf_add_u8_offset(buf, dev_comp2->record[i].version.z, &offset); + data_buf_add_u8_offset(buf, dev_comp2->record[i].elem_offset_cnt, &offset); + if (dev_comp2->record[i].elem_offset_cnt) { + data_buf_add_mem_offset(buf, (uint8_t *)dev_comp2->record[i].elem_offset, + dev_comp2->record[i].elem_offset_cnt, &offset); + } + + data_buf_add_le16_offset(buf, dev_comp2->record[i].data_len, &offset); + if (dev_comp2->record[i].data_len) { + data_buf_add_mem_offset(buf, (uint8_t *)dev_comp2->record[i].data, + dev_comp2->record[i].data_len, &offset); } } + return 0; } @@ -994,6 +1035,17 @@ int bt_mesh_comp_register(const struct bt_mesh_comp *comp) return err; } +int bt_mesh_comp2_register(const struct bt_mesh_comp2 *comp2) +{ + if (!IS_ENABLED(CONFIG_BT_MESH_COMP_PAGE_2)) { + return -EINVAL; + } + + dev_comp2 = comp2; + + return 0; +} + void bt_mesh_comp_provision(uint16_t addr) { int i; @@ -1565,6 +1617,22 @@ void bt_mesh_model_extensions_walk(struct bt_mesh_model *model, } #ifdef CONFIG_BT_MESH_MODEL_EXTENSIONS +/* For vendor models, determine the offset within the model relation list + * by counting the number of standard SIG models in the associated element. + */ +static uint8_t get_sig_offset(struct bt_mesh_model *mod) +{ + const struct bt_mesh_elem *elem = bt_mesh_model_elem(mod); + uint8_t i; + + for (i = 0U; i < elem->vnd_model_count; i++) { + if (&elem->vnd_models[i] == mod) { + return elem->model_count; + } + } + return 0; +} + static int mod_rel_register(struct bt_mesh_model *base, struct bt_mesh_model *ext, uint8_t type) @@ -1572,9 +1640,9 @@ static int mod_rel_register(struct bt_mesh_model *base, LOG_DBG(""); struct mod_relation extension = { base->elem_idx, - base->mod_idx, + base->mod_idx + get_sig_offset(base), ext->elem_idx, - ext->mod_idx, + ext->mod_idx + get_sig_offset(ext), type, }; int i; @@ -1610,7 +1678,7 @@ int bt_mesh_model_extend(struct bt_mesh_model *extending_mod, struct bt_mesh_mod /* Check if a's list contains b */ for (it = a; (it != NULL) && (it->next != a); it = it->next) { if (it == b) { - return 0; + goto register_extension; } } @@ -1627,7 +1695,7 @@ int bt_mesh_model_extend(struct bt_mesh_model *extending_mod, struct bt_mesh_mod a->next = b; } - +register_extension: if (IS_ENABLED(CONFIG_BT_MESH_COMP_PAGE_1)) { return mod_rel_register(base_mod, extending_mod, RELATION_TYPE_EXT); } @@ -1645,16 +1713,19 @@ int bt_mesh_model_correspond(struct bt_mesh_model *corresponding_mod, return -ENOTSUP; } + uint8_t base_offset = get_sig_offset(base_mod); + uint8_t corresponding_offset = get_sig_offset(corresponding_mod); + MOD_REL_LIST_FOR_EACH(i) { if (mod_rel_list[i].type < RELATION_TYPE_EXT && mod_rel_list[i].type > cor_id) { cor_id = mod_rel_list[i].type; } - if ((IS_MOD_BASE(base_mod, i) || - IS_MOD_EXTENSION(base_mod, i) || - IS_MOD_BASE(corresponding_mod, i) || - IS_MOD_EXTENSION(corresponding_mod, i)) && + if ((IS_MOD_BASE(base_mod, i, base_offset) || + IS_MOD_EXTENSION(base_mod, i, base_offset) || + IS_MOD_BASE(corresponding_mod, i, corresponding_offset) || + IS_MOD_EXTENSION(corresponding_mod, i, corresponding_offset)) && mod_rel_list[i].type < RELATION_TYPE_EXT) { return mod_rel_register(base_mod, corresponding_mod, mod_rel_list[i].type); } @@ -2151,19 +2222,90 @@ int bt_mesh_comp_data_get_page(struct net_buf_simple *buf, size_t page, size_t o { if (page == 0 || page == 128) { return bt_mesh_comp_data_get_page_0(buf, offset); - } else if (page == 1 || page == 129) { - return bt_mesh_comp_data_get_page_1(buf); + } else if (IS_ENABLED(CONFIG_BT_MESH_COMP_PAGE_1) && (page == 1 || page == 129)) { + return bt_mesh_comp_data_get_page_1(buf, offset); + } else if (IS_ENABLED(CONFIG_BT_MESH_COMP_PAGE_2) && (page == 2 || page == 130)) { + return bt_mesh_comp_data_get_page_2(buf, offset); } return -EINVAL; } +size_t comp_page_0_size(void) +{ + const struct bt_mesh_comp *comp; + const struct bt_mesh_elem *elem; + size_t size = 10; /* Non-variable length params of comp page 0. */ + + comp = bt_mesh_comp_get(); + + for (int i = 0; i < comp->elem_count; i++) { + elem = &comp->elem[i]; + size += bt_mesh_comp_elem_size(elem); + } + + return size; +} + +size_t comp_page_1_size(void) +{ + const struct bt_mesh_comp *comp; + size_t size = 0; + + comp = bt_mesh_comp_get(); + + for (int i = 0; i < comp->elem_count; i++) { + + size += page1_elem_size(&comp->elem[i]); + } + + return size; +} + +size_t comp_page_2_size(void) +{ + size_t size = 0; + + if (!dev_comp2) { + LOG_ERR("Composition data P2 not registered"); + return size; + } + + for (int i = 0; i < dev_comp2->record_cnt; i++) { + size += 8 + dev_comp2->record[i].elem_offset_cnt + dev_comp2->record[i].data_len; + } + return size; +} + +size_t bt_mesh_comp_page_size(uint8_t page) +{ + if (page == 0 || page == 128) { + return comp_page_0_size(); + } else if (IS_ENABLED(CONFIG_BT_MESH_COMP_PAGE_1) && (page == 1 || page == 129)) { + return comp_page_1_size(); + } else if (IS_ENABLED(CONFIG_BT_MESH_COMP_PAGE_2) && (page == 2 || page == 130)) { + return comp_page_2_size(); + } + + return 0; +} + int bt_mesh_comp_store(void) { - NET_BUF_SIMPLE_DEFINE(buf, BT_MESH_TX_SDU_MAX); +#if IS_ENABLED(CONFIG_BT_MESH_V1d1) + NET_BUF_SIMPLE_DEFINE(buf, CONFIG_BT_MESH_COMP_PST_BUF_SIZE); int err; for (int i = 0; i < ARRAY_SIZE(comp_data_pages); i++) { + size_t page_size = bt_mesh_comp_page_size(i); + + if (page_size > CONFIG_BT_MESH_COMP_PST_BUF_SIZE) { + LOG_WRN("CDP%d is larger than the CDP persistence buffer. " + "Please increase the CDP persistence buffer size " + "to the required size (%d bytes)", + i, page_size); + } + net_buf_simple_reset(&buf); err = bt_mesh_comp_data_get_page(&buf, comp_data_pages[i].page, 0); @@ -2180,7 +2322,7 @@ int bt_mesh_comp_store(void) LOG_DBG("Stored CDP%d", comp_data_pages[i].page); } - +#endif return 0; } @@ -2440,3 +2582,30 @@ void bt_mesh_model_data_store_schedule(struct bt_mesh_model *mod) mod->flags |= BT_MESH_MOD_DATA_PENDING; bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_MOD_PENDING); } + +uint8_t bt_mesh_comp_parse_page(struct net_buf_simple *buf) +{ + uint8_t page = net_buf_simple_pull_u8(buf); + + if (page >= 130U && IS_ENABLED(CONFIG_BT_MESH_COMP_PAGE_2) && + (atomic_test_bit(bt_mesh.flags, BT_MESH_COMP_DIRTY) || + IS_ENABLED(CONFIG_BT_MESH_RPR_SRV))) { + page = 130U; + } else if (page >= 129U && IS_ENABLED(CONFIG_BT_MESH_COMP_PAGE_1) && + (atomic_test_bit(bt_mesh.flags, BT_MESH_COMP_DIRTY) || + IS_ENABLED(CONFIG_BT_MESH_RPR_SRV))) { + page = 129U; + } else if (page >= 128U && (atomic_test_bit(bt_mesh.flags, BT_MESH_COMP_DIRTY) || + IS_ENABLED(CONFIG_BT_MESH_RPR_SRV))) { + page = 128U; + } else if (page >= 2U && IS_ENABLED(CONFIG_BT_MESH_COMP_PAGE_2)) { + page = 2U; + } else if (page >= 1U && IS_ENABLED(CONFIG_BT_MESH_COMP_PAGE_1)) { + page = 1U; + } else if (page != 0U) { + LOG_DBG("Composition page %u not available", page); + page = 0U; + } + + return page; +} diff --git a/subsys/bluetooth/mesh/access.h b/subsys/bluetooth/mesh/access.h index a7d3ffe5865..f58c6f1d449 100644 --- a/subsys/bluetooth/mesh/access.h +++ b/subsys/bluetooth/mesh/access.h @@ -23,11 +23,10 @@ enum { void bt_mesh_elem_register(struct bt_mesh_elem *elem, uint8_t count); uint8_t bt_mesh_elem_count(void); -size_t bt_mesh_comp_page_0_size(void); +size_t bt_mesh_comp_page_size(uint8_t page); int bt_mesh_comp_data_get_page_0(struct net_buf_simple *buf, size_t offset); size_t bt_mesh_metadata_page_0_size(void); int bt_mesh_metadata_get_page_0(struct net_buf_simple *buf, size_t offset); -int bt_mesh_comp_data_get_page_1(struct net_buf_simple *buf); /* Find local element based on unicast address */ struct bt_mesh_elem *bt_mesh_elem_find(uint16_t addr); @@ -65,6 +64,7 @@ int bt_mesh_model_recv(struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf); int bt_mesh_comp_register(const struct bt_mesh_comp *comp); int bt_mesh_comp_store(void); int bt_mesh_comp_read(struct net_buf_simple *buf, uint8_t page); +uint8_t bt_mesh_comp_parse_page(struct net_buf_simple *buf); int bt_mesh_models_metadata_store(void); int bt_mesh_models_metadata_read(struct net_buf_simple *buf, size_t offset); diff --git a/subsys/bluetooth/mesh/adv.c b/subsys/bluetooth/mesh/adv.c index 2132c14e151..b24523aacf3 100644 --- a/subsys/bluetooth/mesh/adv.c +++ b/subsys/bluetooth/mesh/adv.c @@ -138,7 +138,7 @@ struct net_buf *bt_mesh_adv_create(enum bt_mesh_adv_type type, uint8_t xmit, k_timeout_t timeout) { #if defined(CONFIG_BT_MESH_RELAY) - if (tag & BT_MESH_RELAY_ADV) { + if (tag == BT_MESH_ADV_TAG_RELAY) { return bt_mesh_adv_create_from_pool(&relay_buf_pool, adv_relay_pool, type, tag, xmit, timeout); @@ -146,7 +146,7 @@ struct net_buf *bt_mesh_adv_create(enum bt_mesh_adv_type type, #endif #if defined(CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE) - if (tag & BT_MESH_FRIEND_ADV) { + if (tag == BT_MESH_ADV_TAG_FRIEND) { return bt_mesh_adv_create_from_pool(&friend_buf_pool, adv_friend_pool, type, tag, xmit, timeout); @@ -202,14 +202,15 @@ struct net_buf *bt_mesh_adv_buf_get(k_timeout_t timeout) return process_events(events, ARRAY_SIZE(events)); } -struct net_buf *bt_mesh_adv_buf_get_by_tag(uint8_t tag, k_timeout_t timeout) +struct net_buf *bt_mesh_adv_buf_get_by_tag(enum bt_mesh_adv_tag_bit tags, k_timeout_t timeout) { - if (IS_ENABLED(CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE) && tag & BT_MESH_FRIEND_ADV) { + if (IS_ENABLED(CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE) && + tags & BT_MESH_ADV_TAG_BIT_FRIEND) { return net_buf_get(&bt_mesh_friend_queue, timeout); } #if CONFIG_BT_MESH_RELAY_ADV_SETS - if (tag & BT_MESH_RELAY_ADV) { + if (!(tags & BT_MESH_ADV_TAG_BIT_LOCAL)) { return net_buf_get(&bt_mesh_relay_queue, timeout); } #endif @@ -222,9 +223,9 @@ struct net_buf *bt_mesh_adv_buf_get(k_timeout_t timeout) return net_buf_get(&bt_mesh_adv_queue, timeout); } -struct net_buf *bt_mesh_adv_buf_get_by_tag(uint8_t tag, k_timeout_t timeout) +struct net_buf *bt_mesh_adv_buf_get_by_tag(enum bt_mesh_adv_tag_bit tags, k_timeout_t timeout) { - ARG_UNUSED(tag); + ARG_UNUSED(tags); return bt_mesh_adv_buf_get(timeout); } @@ -260,14 +261,16 @@ void bt_mesh_adv_send(struct net_buf *buf, const struct bt_mesh_send_cb *cb, } if (IS_ENABLED(CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE) && - BT_MESH_ADV(buf)->tag == BT_MESH_FRIEND_ADV) { + BT_MESH_ADV(buf)->tag == BT_MESH_ADV_TAG_FRIEND) { net_buf_put(&bt_mesh_friend_queue, net_buf_ref(buf)); bt_mesh_adv_buf_friend_ready(); return; } #if CONFIG_BT_MESH_RELAY_ADV_SETS - if (BT_MESH_ADV(buf)->tag == BT_MESH_RELAY_ADV) { + if (BT_MESH_ADV(buf)->tag == BT_MESH_ADV_TAG_RELAY || + (IS_ENABLED(CONFIG_BT_MESH_PB_ADV_USE_RELAY_SETS) && + BT_MESH_ADV(buf)->tag == BT_MESH_ADV_TAG_PROV)) { net_buf_put(&bt_mesh_relay_queue, net_buf_ref(buf)); bt_mesh_adv_buf_relay_ready(); return; diff --git a/subsys/bluetooth/mesh/adv.h b/subsys/bluetooth/mesh/adv.h index 3d0acf8a7ac..a80ff7e8d4b 100644 --- a/subsys/bluetooth/mesh/adv.h +++ b/subsys/bluetooth/mesh/adv.h @@ -26,10 +26,19 @@ enum bt_mesh_adv_type { }; enum bt_mesh_adv_tag { - BT_MESH_LOCAL_ADV = BIT(0), - BT_MESH_RELAY_ADV = BIT(1), - BT_MESH_PROXY_ADV = BIT(2), - BT_MESH_FRIEND_ADV = BIT(3), + BT_MESH_ADV_TAG_LOCAL, + BT_MESH_ADV_TAG_RELAY, + BT_MESH_ADV_TAG_PROXY, + BT_MESH_ADV_TAG_FRIEND, + BT_MESH_ADV_TAG_PROV, +}; + +enum bt_mesh_adv_tag_bit { + BT_MESH_ADV_TAG_BIT_LOCAL = BIT(BT_MESH_ADV_TAG_LOCAL), + BT_MESH_ADV_TAG_BIT_RELAY = BIT(BT_MESH_ADV_TAG_RELAY), + BT_MESH_ADV_TAG_BIT_PROXY = BIT(BT_MESH_ADV_TAG_PROXY), + BT_MESH_ADV_TAG_BIT_FRIEND = BIT(BT_MESH_ADV_TAG_FRIEND), + BT_MESH_ADV_TAG_BIT_PROV = BIT(BT_MESH_ADV_TAG_PROV), }; struct bt_mesh_adv { @@ -57,7 +66,7 @@ void bt_mesh_adv_send(struct net_buf *buf, const struct bt_mesh_send_cb *cb, struct net_buf *bt_mesh_adv_buf_get(k_timeout_t timeout); -struct net_buf *bt_mesh_adv_buf_get_by_tag(uint8_t tag, k_timeout_t timeout); +struct net_buf *bt_mesh_adv_buf_get_by_tag(enum bt_mesh_adv_tag_bit tags, k_timeout_t timeout); void bt_mesh_adv_gatt_update(void); @@ -75,6 +84,8 @@ void bt_mesh_adv_buf_local_ready(void); void bt_mesh_adv_buf_relay_ready(void); +void bt_mesh_adv_buf_terminate(const struct net_buf *buf); + void bt_mesh_adv_buf_friend_ready(void); int bt_mesh_adv_gatt_send(void); diff --git a/subsys/bluetooth/mesh/adv_ext.c b/subsys/bluetooth/mesh/adv_ext.c index 88c631a5867..fe9fc4fd403 100644 --- a/subsys/bluetooth/mesh/adv_ext.c +++ b/subsys/bluetooth/mesh/adv_ext.c @@ -13,6 +13,9 @@ #include #include #include +#if defined(CONFIG_BT_LL_SOFTDEVICE) +#include +#endif #include "common/bt_str.h" @@ -57,7 +60,7 @@ enum { }; struct bt_mesh_ext_adv { - uint8_t tag; + const enum bt_mesh_adv_tag_bit tags; ATOMIC_DEFINE(flags, ADV_FLAGS_NUM); struct bt_le_ext_adv *instance; struct net_buf *buf; @@ -69,72 +72,93 @@ struct bt_mesh_ext_adv { static void send_pending_adv(struct k_work *work); static bool schedule_send(struct bt_mesh_ext_adv *adv); -static STRUCT_SECTION_ITERABLE(bt_mesh_ext_adv, adv_main) = { - .tag = ( +static struct bt_mesh_ext_adv advs[] = { + [0] = { + .tags = ( #if !defined(CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE) - BT_MESH_FRIEND_ADV | + BT_MESH_ADV_TAG_BIT_FRIEND | #endif #if !defined(CONFIG_BT_MESH_ADV_EXT_GATT_SEPARATE) - BT_MESH_PROXY_ADV | + BT_MESH_ADV_TAG_BIT_PROXY | #endif /* !CONFIG_BT_MESH_ADV_EXT_GATT_SEPARATE */ #if defined(CONFIG_BT_MESH_ADV_EXT_RELAY_USING_MAIN_ADV_SET) - BT_MESH_RELAY_ADV | + BT_MESH_ADV_TAG_BIT_RELAY | #endif /* CONFIG_BT_MESH_ADV_EXT_RELAY_USING_MAIN_ADV_SET */ - BT_MESH_LOCAL_ADV), - - .work = Z_WORK_DELAYABLE_INITIALIZER(send_pending_adv), -}; - +#if defined(CONFIG_BT_MESH_PB_ADV) + BT_MESH_ADV_TAG_BIT_PROV | +#endif /* CONFIG_BT_MESH_PB_ADV */ + BT_MESH_ADV_TAG_BIT_LOCAL + ), + .work = Z_WORK_DELAYABLE_INITIALIZER(send_pending_adv), + }, #if CONFIG_BT_MESH_RELAY_ADV_SETS -static STRUCT_SECTION_ITERABLE_ARRAY(bt_mesh_ext_adv, adv_relay, CONFIG_BT_MESH_RELAY_ADV_SETS) = { - [0 ... CONFIG_BT_MESH_RELAY_ADV_SETS - 1] = { - .tag = BT_MESH_RELAY_ADV, + [1 ... CONFIG_BT_MESH_RELAY_ADV_SETS] = { + .tags = ( +#if defined(CONFIG_BT_MESH_RELAY) + BT_MESH_ADV_TAG_BIT_RELAY | +#endif /* CONFIG_BT_MESH_RELAY */ +#if defined(CONFIG_BT_MESH_PB_ADV_USE_RELAY_SETS) + BT_MESH_ADV_TAG_BIT_PROV | +#endif /* CONFIG_BT_MESH_PB_ADV_USE_RELAY_SETS */ + 0), .work = Z_WORK_DELAYABLE_INITIALIZER(send_pending_adv), - } -}; + }, #endif /* CONFIG_BT_MESH_RELAY_ADV_SETS */ - #if defined(CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE) -#define ADV_EXT_FRIEND 1 -static STRUCT_SECTION_ITERABLE(bt_mesh_ext_adv, adv_friend) = { - .tag = BT_MESH_FRIEND_ADV, - .work = Z_WORK_DELAYABLE_INITIALIZER(send_pending_adv), -}; -#else /* CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE */ -#define ADV_EXT_FRIEND 0 + { + .tags = BT_MESH_ADV_TAG_BIT_FRIEND, + .work = Z_WORK_DELAYABLE_INITIALIZER(send_pending_adv), + }, #endif /* CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE */ - #if defined(CONFIG_BT_MESH_ADV_EXT_GATT_SEPARATE) -#define ADV_EXT_GATT 1 -static STRUCT_SECTION_ITERABLE(bt_mesh_ext_adv, adv_gatt) = { - .tag = BT_MESH_PROXY_ADV, - .work = Z_WORK_DELAYABLE_INITIALIZER(send_pending_adv), -}; -#else /* CONFIG_BT_MESH_ADV_EXT_GATT_SEPARATE */ -#define ADV_EXT_GATT 0 + { + .tags = BT_MESH_ADV_TAG_BIT_PROXY, + .work = Z_WORK_DELAYABLE_INITIALIZER(send_pending_adv), + }, #endif /* CONFIG_BT_MESH_ADV_EXT_GATT_SEPARATE */ +}; -#define BT_MESH_ADV_COUNT (1 + CONFIG_BT_MESH_RELAY_ADV_SETS + ADV_EXT_FRIEND + ADV_EXT_GATT) - -BUILD_ASSERT(CONFIG_BT_EXT_ADV_MAX_ADV_SET >= BT_MESH_ADV_COUNT, +BUILD_ASSERT(ARRAY_SIZE(advs) <= CONFIG_BT_EXT_ADV_MAX_ADV_SET, "Insufficient adv instances"); static inline struct bt_mesh_ext_adv *relay_adv_get(void) { -#if CONFIG_BT_MESH_RELAY_ADV_SETS - return adv_relay; -#else /* !CONFIG_BT_MESH_RELAY_ADV_SETS */ - return &adv_main; -#endif /* CONFIG_BT_MESH_RELAY_ADV_SETS */ + if (!!(CONFIG_BT_MESH_RELAY_ADV_SETS)) { + return &advs[1]; + } else { + return &advs[0]; + } } static inline struct bt_mesh_ext_adv *gatt_adv_get(void) { -#if defined(CONFIG_BT_MESH_ADV_EXT_GATT_SEPARATE) - return &adv_gatt; -#else /* !CONFIG_BT_MESH_ADV_EXT_GATT_SEPARATE */ - return &adv_main; -#endif /* CONFIG_BT_MESH_ADV_EXT_GATT_SEPARATE */ + if (IS_ENABLED(CONFIG_BT_MESH_ADV_EXT_GATT_SEPARATE)) { + return &advs[ARRAY_SIZE(advs) - 1]; + } else { + return &advs[0]; + } +} + +static int set_adv_randomness(uint8_t handle, int rand_us) +{ +#if defined(CONFIG_BT_LL_SOFTDEVICE) + struct net_buf *buf; + sdc_hci_cmd_vs_set_adv_randomness_t *cmd_params; + + buf = bt_hci_cmd_create(SDC_HCI_OPCODE_CMD_VS_SET_ADV_RANDOMNESS, sizeof(*cmd_params)); + if (!buf) { + LOG_ERR("Could not allocate command buffer"); + return -ENOMEM; + } + + cmd_params = net_buf_add(buf, sizeof(*cmd_params)); + cmd_params->adv_handle = handle; + cmd_params->rand_us = rand_us; + + return bt_hci_cmd_send_sync(SDC_HCI_OPCODE_CMD_VS_SET_ADV_RANDOMNESS, buf, NULL); +#else + return 0; +#endif /* defined(CONFIG_BT_LL_SOFTDEVICE) */ } static int adv_start(struct bt_mesh_ext_adv *adv, @@ -234,20 +258,13 @@ static int buf_send(struct bt_mesh_ext_adv *adv, struct net_buf *buf) return err; } -static const char *adv_tag_to_str(enum bt_mesh_adv_tag tag) -{ - if (tag & BT_MESH_LOCAL_ADV) { - return "local adv"; - } else if (tag & BT_MESH_PROXY_ADV) { - return "proxy adv"; - } else if (tag & BT_MESH_RELAY_ADV) { - return "relay adv"; - } else if (tag & BT_MESH_FRIEND_ADV) { - return "friend adv"; - } else { - return "(unknown tag)"; - } -} +static const char * const adv_tag_to_str[] = { + [BT_MESH_ADV_TAG_LOCAL] = "local adv", + [BT_MESH_ADV_TAG_RELAY] = "relay adv", + [BT_MESH_ADV_TAG_PROXY] = "proxy adv", + [BT_MESH_ADV_TAG_FRIEND] = "friend adv", + [BT_MESH_ADV_TAG_PROV] = "prov adv", +}; static void send_pending_adv(struct k_work *work) { @@ -264,8 +281,9 @@ static void send_pending_adv(struct k_work *work) */ int64_t duration = k_uptime_delta(&adv->timestamp); - LOG_DBG("Advertising stopped after %u ms for (%u) %s", (uint32_t)duration, adv->tag, - adv_tag_to_str(adv->tag)); + LOG_DBG("Advertising stopped after %u ms for %s", (uint32_t)duration, + adv->buf ? adv_tag_to_str[BT_MESH_ADV(adv->buf)->tag] : + adv_tag_to_str[BT_MESH_ADV_TAG_PROXY]); atomic_clear_bit(adv->flags, ADV_FLAG_ACTIVE); atomic_clear_bit(adv->flags, ADV_FLAG_PROXY); @@ -283,7 +301,7 @@ static void send_pending_adv(struct k_work *work) atomic_clear_bit(adv->flags, ADV_FLAG_SCHEDULED); - while ((buf = bt_mesh_adv_buf_get_by_tag(adv->tag, K_NO_WAIT))) { + while ((buf = bt_mesh_adv_buf_get_by_tag(adv->tags, K_NO_WAIT))) { /* busy == 0 means this was canceled */ if (!BT_MESH_ADV(buf)->busy) { net_buf_unref(buf); @@ -301,7 +319,7 @@ static void send_pending_adv(struct k_work *work) } if (!IS_ENABLED(CONFIG_BT_MESH_GATT_SERVER) || - !(adv->tag & BT_MESH_PROXY_ADV)) { + !(adv->tags & BT_MESH_ADV_TAG_BIT_PROXY)) { return; } @@ -344,8 +362,9 @@ static bool schedule_send(struct bt_mesh_ext_adv *adv) atomic_clear_bit(adv->flags, ADV_FLAG_SCHEDULE_PENDING); - if ((IS_ENABLED(CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE) && adv->tag & BT_MESH_FRIEND_ADV) || - (CONFIG_BT_MESH_RELAY_ADV_SETS > 0 && adv->tag == BT_MESH_RELAY_ADV)) { + if ((IS_ENABLED(CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE) && + adv->tags & BT_MESH_ADV_TAG_BIT_FRIEND) || + (CONFIG_BT_MESH_RELAY_ADV_SETS > 0 && adv->tags & BT_MESH_ADV_TAG_BIT_RELAY)) { k_work_reschedule(&adv->work, K_NO_WAIT); } else { /* The controller will send the next advertisement immediately. @@ -366,7 +385,7 @@ void bt_mesh_adv_gatt_update(void) void bt_mesh_adv_buf_local_ready(void) { - (void)schedule_send(&adv_main); + (void)schedule_send(advs); } void bt_mesh_adv_buf_relay_ready(void) @@ -381,15 +400,49 @@ void bt_mesh_adv_buf_relay_ready(void) /* Attempt to use the main adv set for the sending of relay messages. */ if (IS_ENABLED(CONFIG_BT_MESH_ADV_EXT_RELAY_USING_MAIN_ADV_SET)) { - (void)schedule_send(&adv_main); + (void)schedule_send(advs); } } void bt_mesh_adv_buf_friend_ready(void) { -#if defined(CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE) - (void)schedule_send(&adv_friend); -#endif + if (IS_ENABLED(CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE)) { + schedule_send(&advs[1 + CONFIG_BT_MESH_RELAY_ADV_SETS]); + } else { + schedule_send(&advs[0]); + } +} + +void bt_mesh_adv_buf_terminate(const struct net_buf *buf) +{ + int err; + + for (int i = 0; i < ARRAY_SIZE(advs); i++) { + struct bt_mesh_ext_adv *adv = &advs[i]; + + if (adv->buf != buf) { + continue; + } + + if (!atomic_test_bit(adv->flags, ADV_FLAG_ACTIVE)) { + return; + } + + err = bt_le_ext_adv_stop(adv->instance); + if (err) { + LOG_ERR("Failed to stop adv %d", err); + return; + } + + /* Do not call `cb:end`, since this user action */ + BT_MESH_ADV(adv->buf)->cb = NULL; + + atomic_set_bit(adv->flags, ADV_FLAG_SENT); + + k_work_submit(&adv->work.work); + + return; + } } void bt_mesh_adv_init(void) @@ -401,17 +454,18 @@ void bt_mesh_adv_init(void) #if defined(CONFIG_BT_MESH_DEBUG_USE_ID_ADDR) .options = BT_LE_ADV_OPT_USE_IDENTITY, #endif -}; - STRUCT_SECTION_FOREACH(bt_mesh_ext_adv, adv) { - (void)memcpy(&adv->adv_param, &adv_param, sizeof(adv_param)); + }; + + for (int i = 0; i < ARRAY_SIZE(advs); i++) { + (void)memcpy(&advs[i].adv_param, &adv_param, sizeof(adv_param)); } } static struct bt_mesh_ext_adv *adv_instance_find(struct bt_le_ext_adv *instance) { - STRUCT_SECTION_FOREACH(bt_mesh_ext_adv, adv) { - if (adv->instance == instance) { - return adv; + for (int i = 0; i < ARRAY_SIZE(advs); i++) { + if (advs[i].instance == instance) { + return &advs[i]; } } @@ -461,18 +515,26 @@ int bt_mesh_adv_enable(void) #endif /* CONFIG_BT_MESH_GATT_SERVER */ }; - if (adv_main.instance) { + if (advs[0].instance) { /* Already initialized */ return 0; } - - STRUCT_SECTION_FOREACH(bt_mesh_ext_adv, adv) { - err = bt_le_ext_adv_create(&adv->adv_param, &adv_cb, - &adv->instance); + for (int i = 0; i < ARRAY_SIZE(advs); i++) { + err = bt_le_ext_adv_create(&advs[i].adv_param, &adv_cb, + &advs[i].instance); if (err) { return err; } + + if (IS_ENABLED(CONFIG_BT_LL_SOFTDEVICE) && + IS_ENABLED(CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE) && + advs[i].tags == BT_MESH_ADV_TAG_BIT_FRIEND) { + err = set_adv_randomness(advs[i].instance->handle, 0); + if (err) { + LOG_ERR("Failed to set zero randomness: %d", err); + } + } } return 0; @@ -499,5 +561,5 @@ int bt_mesh_adv_gatt_start(const struct bt_le_adv_param *param, int bt_mesh_adv_bt_data_send(uint8_t num_events, uint16_t adv_interval, const struct bt_data *ad, size_t ad_len) { - return bt_data_send(&adv_main, num_events, adv_interval, ad, ad_len); + return bt_data_send(advs, num_events, adv_interval, ad, ad_len); } diff --git a/subsys/bluetooth/mesh/adv_legacy.c b/subsys/bluetooth/mesh/adv_legacy.c index 8136aef9f22..a7d7dd1a320 100644 --- a/subsys/bluetooth/mesh/adv_legacy.c +++ b/subsys/bluetooth/mesh/adv_legacy.c @@ -205,6 +205,11 @@ void bt_mesh_adv_gatt_update(void) bt_mesh_adv_buf_get_cancel(); } +void bt_mesh_adv_buf_terminate(const struct net_buf *buf) +{ + ARG_UNUSED(buf); +} + void bt_mesh_adv_init(void) { k_thread_create(&adv_thread_data, adv_thread_stack, diff --git a/subsys/bluetooth/mesh/beacon.c b/subsys/bluetooth/mesh/beacon.c index 8c0ac3b54e9..afdea5b4c6a 100644 --- a/subsys/bluetooth/mesh/beacon.c +++ b/subsys/bluetooth/mesh/beacon.c @@ -271,7 +271,7 @@ static bool net_beacon_send(struct bt_mesh_subnet *sub, struct bt_mesh_beacon *b return false; } - buf = bt_mesh_adv_create(BT_MESH_ADV_BEACON, BT_MESH_LOCAL_ADV, + buf = bt_mesh_adv_create(BT_MESH_ADV_BEACON, BT_MESH_ADV_TAG_LOCAL, PROV_XMIT, K_NO_WAIT); if (!buf) { LOG_ERR("Unable to allocate beacon buffer"); @@ -335,7 +335,7 @@ static int unprovisioned_beacon_send(void) LOG_DBG(""); - buf = bt_mesh_adv_create(BT_MESH_ADV_BEACON, BT_MESH_LOCAL_ADV, + buf = bt_mesh_adv_create(BT_MESH_ADV_BEACON, BT_MESH_ADV_TAG_LOCAL, UNPROV_XMIT, K_NO_WAIT); if (!buf) { LOG_ERR("Unable to allocate beacon buffer"); @@ -362,7 +362,7 @@ static int unprovisioned_beacon_send(void) if (prov->uri) { size_t len; - buf = bt_mesh_adv_create(BT_MESH_ADV_URI, BT_MESH_LOCAL_ADV, + buf = bt_mesh_adv_create(BT_MESH_ADV_URI, BT_MESH_ADV_TAG_LOCAL, UNPROV_XMIT, K_NO_WAIT); if (!buf) { LOG_ERR("Unable to allocate URI buffer"); diff --git a/subsys/bluetooth/mesh/blob_srv.c b/subsys/bluetooth/mesh/blob_srv.c index 56a89472ea6..1e6cb9bc91a 100644 --- a/subsys/bluetooth/mesh/blob_srv.c +++ b/subsys/bluetooth/mesh/blob_srv.c @@ -251,6 +251,37 @@ static void resume(struct bt_mesh_blob_srv *srv) reset_timer(srv); } +static void end(struct bt_mesh_blob_srv *srv) +{ + phase_set(srv, BT_MESH_BLOB_XFER_PHASE_COMPLETE); + k_work_cancel_delayable(&srv->rx_timeout); + k_work_cancel_delayable(&srv->pull.report); + io_close(srv); + erase_state(srv); + + if (srv->cb && srv->cb->end) { + srv->cb->end(srv, srv->state.xfer.id, true); + } +} + +static bool all_blocks_received(struct bt_mesh_blob_srv *srv) +{ + for (int i = 0; i < ARRAY_SIZE(srv->state.blocks); ++i) { + if (srv->state.blocks[i]) { + return false; + } + } + + return true; +} + +static bool pull_mode_xfer_complete(struct bt_mesh_blob_srv *srv) +{ + return srv->state.xfer.mode == BT_MESH_BLOB_XFER_MODE_PULL && + srv->phase == BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_CHUNK && + all_blocks_received(srv); +} + static void timeout(struct k_work *work) { struct bt_mesh_blob_srv *srv = @@ -260,6 +291,8 @@ static void timeout(struct k_work *work) if (srv->phase == BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START) { cancel(srv); + } else if (pull_mode_xfer_complete(srv)) { + end(srv); } else { suspend(srv); } @@ -388,6 +421,15 @@ static int handle_xfer_get(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ct struct bt_mesh_blob_srv *srv = mod->user_data; LOG_DBG(""); + + if (pull_mode_xfer_complete(srv)) { + /* The client requested transfer. If we are in Pull mode and all blocks were + * received, we should change the Transfer state here to Complete so that the client + * receives the correct state. + */ + end(srv); + } + xfer_status_rsp(srv, ctx, BT_MESH_BLOB_SUCCESS); return 0; @@ -685,7 +727,7 @@ static int handle_chunk(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, struct bt_mesh_blob_chunk chunk; size_t expected_size = 0; uint16_t idx; - int i, err; + int err; idx = net_buf_simple_pull_le16(buf); chunk.size = buf->len; @@ -745,26 +787,26 @@ static int handle_chunk(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, atomic_clear_bit(srv->state.blocks, srv->block.number); - for (i = 0; i < ARRAY_SIZE(srv->state.blocks); ++i) { - if (!srv->state.blocks[i]) { - continue; - } - + if (!all_blocks_received(srv)) { phase_set(srv, BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_BLOCK); store_state(srv); return 0; } - phase_set(srv, BT_MESH_BLOB_XFER_PHASE_COMPLETE); - k_work_cancel_delayable(&srv->rx_timeout); - k_work_cancel_delayable(&srv->pull.report); - io_close(srv); - erase_state(srv); - - if (srv->cb && srv->cb->end) { - srv->cb->end(srv, srv->state.xfer.id, true); + if (srv->state.xfer.mode == BT_MESH_BLOB_XFER_MODE_PULL) { + /* By spec (section 5.2.4), the BLOB Server stops sending BLOB Partial Block Report + * messages "If the current block is the last block, then the server determines that + * the client knows the transfer is complete. For example, a higher-layer model may + * indicate that the client considers the transfer complete." + * + * We don't have any way for higher-layer model to indicate that the transfer is + * complete. Therefore we need to keep sending Partial Block Report messages until + * the client sends BLOB Transfer Get message or the Block Timer expires. + */ + return 0; } + end(srv); return 0; } diff --git a/subsys/bluetooth/mesh/cfg.c b/subsys/bluetooth/mesh/cfg.c index 287023afa68..c7bea0d29b0 100644 --- a/subsys/bluetooth/mesh/cfg.c +++ b/subsys/bluetooth/mesh/cfg.c @@ -16,6 +16,8 @@ #include "friend.h" #include "adv.h" #include "cfg.h" +#include "od_priv_proxy.h" +#include "priv_beacon.h" #define LOG_LEVEL CONFIG_BT_MESH_CFG_LOG_LEVEL #include @@ -30,14 +32,6 @@ struct cfg_val { uint8_t gatt_proxy; uint8_t frnd; uint8_t default_ttl; -#if defined(CONFIG_BT_MESH_PRIV_BEACONS) - uint8_t priv_beacon; - uint8_t priv_beacon_int; - uint8_t priv_gatt_proxy; -#endif -#if defined(CONFIG_BT_MESH_OD_PRIV_PROXY_SRV) - uint8_t on_demand_state; -#endif }; void bt_mesh_beacon_set(bool beacon) @@ -109,9 +103,9 @@ int bt_mesh_priv_beacon_set(enum bt_mesh_feat_state priv_beacon) /* Beacon timer will stop automatically when all beacons are disabled. */ } - if (IS_ENABLED(CONFIG_BT_SETTINGS) && + if (IS_ENABLED(CONFIG_BT_SETTINGS) && IS_ENABLED(CONFIG_BT_MESH_PRIV_BEACON_SRV) && atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { - bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_CFG_PENDING); + bt_mesh_priv_beacon_srv_store_schedule(); } return 0; @@ -161,9 +155,9 @@ int bt_mesh_od_priv_proxy_set(uint8_t on_demand_proxy) bt_mesh.on_demand_state = on_demand_proxy; } - if (IS_ENABLED(CONFIG_BT_SETTINGS) && + if (IS_ENABLED(CONFIG_BT_SETTINGS) && IS_ENABLED(CONFIG_BT_MESH_OD_PRIV_PROXY_SRV) && atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { - bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_CFG_PENDING); + bt_mesh_od_priv_proxy_srv_store_schedule(); } return 0; #endif @@ -244,9 +238,9 @@ int bt_mesh_priv_gatt_proxy_set(enum bt_mesh_feat_state priv_gatt_proxy) bt_mesh_adv_gatt_update(); } - if (IS_ENABLED(CONFIG_BT_SETTINGS) && + if (IS_ENABLED(CONFIG_BT_SETTINGS) && IS_ENABLED(CONFIG_BT_MESH_PRIV_BEACON_SRV) && atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { - bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_CFG_PENDING); + bt_mesh_priv_beacon_srv_store_schedule(); } return 0; @@ -261,7 +255,6 @@ enum bt_mesh_feat_state bt_mesh_priv_gatt_proxy_get(void) return feature_get(BT_MESH_PRIV_GATT_PROXY); } - int bt_mesh_default_ttl_set(uint8_t default_ttl) { if (default_ttl == 1 || default_ttl > BT_MESH_TTL_MAX) { @@ -454,14 +447,6 @@ static int cfg_set(const char *name, size_t len_rd, bt_mesh_gatt_proxy_set(cfg.gatt_proxy); bt_mesh_friend_set(cfg.frnd); bt_mesh_default_ttl_set(cfg.default_ttl); -#if defined(CONFIG_BT_MESH_PRIV_BEACONS) - bt_mesh_priv_beacon_set(cfg.priv_beacon); - bt_mesh_priv_beacon_update_interval_set(cfg.priv_beacon_int); - bt_mesh_priv_gatt_proxy_set(cfg.priv_gatt_proxy); -#endif -#if defined(CONFIG_BT_MESH_OD_PRIV_PROXY_SRV) - bt_mesh_od_priv_proxy_set(cfg.on_demand_state); -#endif LOG_DBG("Restored configuration state"); @@ -476,7 +461,7 @@ static void clear_cfg(void) err = settings_delete("bt/mesh/Cfg"); if (err) { - LOG_ERR("Failed to clear configuration"); + LOG_ERR("Failed to clear configuration (err: %d)", err); } else { LOG_DBG("Cleared configuration"); } @@ -494,15 +479,6 @@ static void store_pending_cfg(void) val.gatt_proxy = bt_mesh_gatt_proxy_get(); val.frnd = bt_mesh_friend_get(); val.default_ttl = bt_mesh_default_ttl_get(); -#if defined(CONFIG_BT_MESH_PRIV_BEACONS) - val.priv_beacon = bt_mesh_priv_beacon_get(); - val.priv_beacon_int = bt_mesh_priv_beacon_update_interval_get(); - val.priv_gatt_proxy = bt_mesh_priv_gatt_proxy_get(); -#endif -#if defined(CONFIG_BT_MESH_OD_PRIV_PROXY_SRV) - val.on_demand_state = bt_mesh_od_priv_proxy_get(); -#endif - err = settings_save_one("bt/mesh/Cfg", &val, sizeof(val)); if (err) { diff --git a/subsys/bluetooth/mesh/cfg_cli.c b/subsys/bluetooth/mesh/cfg_cli.c index a5ad80193f3..c9f4f98f39d 100644 --- a/subsys/bluetooth/mesh/cfg_cli.c +++ b/subsys/bluetooth/mesh/cfg_cli.c @@ -2320,7 +2320,7 @@ struct bt_mesh_mod_id_vnd bt_mesh_comp_p0_elem_mod_vnd(struct bt_mesh_comp_p0_el struct bt_mesh_comp_p1_elem *bt_mesh_comp_p1_elem_pull(struct net_buf_simple *buf, struct bt_mesh_comp_p1_elem *elem) { - if (buf->len < 6) { + if (buf->len < 4) { LOG_DBG("No more elements to pull or missing data"); return NULL; } @@ -2443,3 +2443,38 @@ struct bt_mesh_comp_p1_ext_item *bt_mesh_comp_p1_pull_ext_item( } return ext_item; } + +struct bt_mesh_comp_p2_record *bt_mesh_comp_p2_record_pull(struct net_buf_simple *buf, + struct bt_mesh_comp_p2_record *record) +{ + if (buf->len < 8) { + LOG_DBG("No more elements to pull or missing data"); + return NULL; + } + + uint8_t elem_offset_cnt; + uint16_t data_len; + + record->id = net_buf_simple_pull_le16(buf); + record->version.x = net_buf_simple_pull_u8(buf); + record->version.y = net_buf_simple_pull_u8(buf); + record->version.z = net_buf_simple_pull_u8(buf); + elem_offset_cnt = net_buf_simple_pull_u8(buf); + if (buf->len < elem_offset_cnt + 2) { + LOG_WRN("Invalid composition data offset count"); + return NULL; + } + + net_buf_simple_init_with_data(record->elem_buf, + net_buf_simple_pull_mem(buf, elem_offset_cnt), + elem_offset_cnt); + data_len = net_buf_simple_pull_le16(buf); + if (buf->len < data_len) { + LOG_WRN("Invalid composition data additional data length"); + return NULL; + } + + net_buf_simple_init_with_data(record->data_buf, + net_buf_simple_pull_mem(buf, data_len), data_len); + return record; +} diff --git a/subsys/bluetooth/mesh/cfg_srv.c b/subsys/bluetooth/mesh/cfg_srv.c index 2fba9360206..f65d2cba3c7 100644 --- a/subsys/bluetooth/mesh/cfg_srv.c +++ b/subsys/bluetooth/mesh/cfg_srv.c @@ -59,21 +59,7 @@ static int dev_comp_data_get(struct bt_mesh_model *model, LOG_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, bt_hex(buf->data, buf->len)); - page = net_buf_simple_pull_u8(buf); - - if (page >= 129U && IS_ENABLED(CONFIG_BT_MESH_COMP_PAGE_1) && - (atomic_test_bit(bt_mesh.flags, BT_MESH_COMP_DIRTY) || - IS_ENABLED(CONFIG_BT_MESH_RPR_SRV))) { - page = 129U; - } else if (page >= 128U && (atomic_test_bit(bt_mesh.flags, BT_MESH_COMP_DIRTY) || - IS_ENABLED(CONFIG_BT_MESH_RPR_SRV))) { - page = 128U; - } else if (page >= 1U && IS_ENABLED(CONFIG_BT_MESH_COMP_PAGE_1)) { - page = 1U; - } else if (page != 0U) { - LOG_DBG("Composition page %u not available", page); - page = 0U; - } + page = bt_mesh_comp_parse_page(buf); LOG_DBG("Preparing Composition data page %d", page); bt_mesh_model_msg_init(&sdu, OP_DEV_COMP_DATA_STATUS); diff --git a/subsys/bluetooth/mesh/crypto.c b/subsys/bluetooth/mesh/crypto.c index e42717475c0..b79458f7ea2 100644 --- a/subsys/bluetooth/mesh/crypto.c +++ b/subsys/bluetooth/mesh/crypto.c @@ -419,7 +419,7 @@ int bt_mesh_net_decrypt(const struct bt_mesh_key *key, struct net_buf_simple *bu if (IS_ENABLED(CONFIG_BT_MESH_PROXY) && type == BT_MESH_NONCE_PROXY) { create_proxy_nonce(nonce, buf->data, iv_index); - } else if (IS_ENABLED(CONFIG_BT_MESH_PROXY_SOLICITATION) && + } else if (IS_ENABLED(CONFIG_BT_MESH_SOLICITATION) && type == BT_MESH_NONCE_SOLICITATION) { create_proxy_sol_nonce(nonce, buf->data); } else { diff --git a/subsys/bluetooth/mesh/crypto_psa.c b/subsys/bluetooth/mesh/crypto_psa.c index 450cde679a8..587c367a6bd 100644 --- a/subsys/bluetooth/mesh/crypto_psa.c +++ b/subsys/bluetooth/mesh/crypto_psa.c @@ -7,6 +7,7 @@ #include #include +#include #define LOG_LEVEL CONFIG_BT_MESH_CRYPTO_LOG_LEVEL #include @@ -510,3 +511,12 @@ int bt_mesh_key_compare(const uint8_t raw_key[16], const struct bt_mesh_key *key return memcmp(out, raw_key, 16); } + +__weak int bt_rand(void *buf, size_t len) +{ + CHECKIF(buf == NULL || len == 0) { + return -EINVAL; + } + + return psa_generate_random(buf, len) == PSA_SUCCESS ? 0 : -EIO; +} diff --git a/subsys/bluetooth/mesh/dfd_srv.c b/subsys/bluetooth/mesh/dfd_srv.c index 2898ca39a39..0a9ecba6cec 100644 --- a/subsys/bluetooth/mesh/dfd_srv.c +++ b/subsys/bluetooth/mesh/dfd_srv.c @@ -224,7 +224,19 @@ static int handle_capabilities_get(struct bt_mesh_model *mod, struct bt_mesh_msg size = MIN(size, CONFIG_BT_MESH_DFD_SRV_SLOT_SPACE); net_buf_simple_add_le32(&rsp, CONFIG_BT_MESH_DFD_SRV_SLOT_SPACE - size); - net_buf_simple_add_u8(&rsp, 0U); /* OOB retrieval not supported */ + +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD + struct bt_mesh_dfd_srv *srv = mod->user_data; + + if (srv->oob_schemes.count > 0) { + net_buf_simple_add_u8(&rsp, 1); + net_buf_simple_add_mem(&rsp, srv->oob_schemes.schemes, + srv->oob_schemes.count); + } else +#endif + { + net_buf_simple_add_u8(&rsp, 0); + } bt_mesh_model_send(mod, ctx, &rsp, NULL, NULL); @@ -329,9 +341,10 @@ static int handle_apply(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, return 0; } -static void upload_status_rsp(struct bt_mesh_dfd_srv *srv, - struct bt_mesh_msg_ctx *ctx, - enum bt_mesh_dfd_status status) +static void upload_status_rsp_with_progress(struct bt_mesh_dfd_srv *srv, + struct bt_mesh_msg_ctx *ctx, + enum bt_mesh_dfd_status status, + uint8_t progress) { BT_MESH_MODEL_BUF_DEFINE(rsp, BT_MESH_DFD_OP_UPLOAD_STATUS, DFD_UPLOAD_STATUS_MSG_MAXLEN); @@ -346,14 +359,40 @@ static void upload_status_rsp(struct bt_mesh_dfd_srv *srv, return; } - net_buf_simple_add_u8(&rsp, - bt_mesh_blob_srv_progress(&srv->upload.blob)); - net_buf_simple_add_mem(&rsp, srv->upload.slot->fwid, - srv->upload.slot->fwid_len); +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD + if (srv->upload.is_oob) { + net_buf_simple_add_u8(&rsp, progress | BIT(7)); + net_buf_simple_add_mem(&rsp, srv->upload.oob.current_fwid, + srv->upload.oob.current_fwid_len); + } else +#endif + { + net_buf_simple_add_u8(&rsp, progress); + net_buf_simple_add_mem(&rsp, srv->upload.slot->fwid, + srv->upload.slot->fwid_len); + } bt_mesh_model_send(srv->mod, ctx, &rsp, NULL, NULL); } +static void upload_status_rsp(struct bt_mesh_dfd_srv *srv, + struct bt_mesh_msg_ctx *ctx, + enum bt_mesh_dfd_status status) +{ + uint8_t progress; + +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD + if (srv->upload.is_oob) { + progress = srv->cb->oob_progress_get(srv, srv->upload.slot); + } else +#endif + { + progress = bt_mesh_blob_srv_progress(&srv->upload.blob); + } + + upload_status_rsp_with_progress(srv, ctx, status, progress); +} + static int handle_upload_get(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { @@ -364,16 +403,50 @@ static int handle_upload_get(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx * return 0; } +static inline int set_upload_fwid(struct bt_mesh_dfd_srv *srv, struct bt_mesh_msg_ctx *ctx, + const uint8_t *fwid, size_t fwid_len) +{ + int err = bt_mesh_dfu_slot_fwid_set(srv->upload.slot, fwid, fwid_len); + + switch (err) { + case -EFBIG: /* Fwid too long */ + case -EALREADY: /* Other server is in progress with this fwid */ + bt_mesh_dfu_slot_release(srv->upload.slot); + srv->upload.slot = NULL; + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); + break; + case -EEXIST: /* Img with this fwid already is in list */ + srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS; + bt_mesh_dfu_slot_release(srv->upload.slot); + + err = bt_mesh_dfu_slot_get(fwid, fwid_len, &srv->upload.slot); + if (!err) { + upload_status_rsp_with_progress(srv, ctx, BT_MESH_DFD_SUCCESS, 100); + } else { + srv->upload.slot = NULL; + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); + } + break; + case 0: + srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE; + break; + case -EINVAL: /* Slot in wrong state. */ + default: + break; + } + + return err; +} + static int handle_upload_start(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { struct bt_mesh_dfd_srv *srv = mod->user_data; - const struct bt_mesh_dfu_slot *old_slot = srv->upload.slot; size_t meta_len, fwid_len, size; const uint8_t *meta, *fwid; uint16_t timeout_base; uint64_t blob_id; - int err, idx; + int err; uint8_t ttl; ttl = net_buf_simple_pull_u8(buf); @@ -392,9 +465,7 @@ static int handle_upload_start(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx LOG_DBG("Upload Start: size: %d, fwid: %s, metadata: %s", size, bt_hex(fwid, fwid_len), bt_hex(meta, meta_len)); - if (size > CONFIG_BT_MESH_DFD_SRV_SLOT_MAX_SIZE || - fwid_len > CONFIG_BT_MESH_DFU_FWID_MAXLEN || - meta_len > CONFIG_BT_MESH_DFU_METADATA_MAXLEN) { + if (size > CONFIG_BT_MESH_DFD_SRV_SLOT_MAX_SIZE) { upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INSUFFICIENT_RESOURCES); return 0; @@ -413,7 +484,11 @@ static int handle_upload_start(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx !memcmp(srv->upload.slot->metadata, meta, meta_len) && srv->upload.blob.state.xfer.id == blob_id && srv->upload.blob.state.ttl == ttl && - srv->upload.blob.state.timeout_base == timeout_base) { + srv->upload.blob.state.timeout_base == timeout_base +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD + && !srv->upload.is_oob +#endif + ) { LOG_DBG("Duplicate upload start"); upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); return 0; @@ -424,23 +499,18 @@ static int handle_upload_start(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx return 0; } - idx = bt_mesh_dfu_slot_get(fwid, fwid_len, &srv->upload.slot); - if (idx >= 0 && bt_mesh_dfu_slot_is_valid(srv->upload.slot)) { - LOG_DBG("Already received image"); - srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS; - upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); - return 0; - } - - if (old_slot && !bt_mesh_dfu_slot_is_valid(old_slot)) { - LOG_DBG("Deleting old invalid slot"); - slot_del(srv, old_slot); + /* This will be a no-op if the slot state isn't RESERVED, which is + * what we want. + */ + if (srv->upload.slot) { + bt_mesh_dfu_slot_release(srv->upload.slot); } - /* TODO Store transfer state before slot is added. */ +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD + srv->upload.is_oob = false; +#endif + srv->upload.slot = bt_mesh_dfu_slot_reserve(); - srv->upload.slot = bt_mesh_dfu_slot_add(size, fwid, fwid_len, meta, - meta_len, NULL, 0); if (!srv->upload.slot) { LOG_WRN("No space for slot"); upload_status_rsp(srv, ctx, @@ -448,11 +518,27 @@ static int handle_upload_start(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx return 0; } + err = set_upload_fwid(srv, ctx, fwid, fwid_len); + if (err) { + return err; + } + + err = bt_mesh_dfu_slot_info_set(srv->upload.slot, size, meta, meta_len); + switch (err) { + case -EFBIG: + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); + break; + case 0: + break; + default: + return err; + } + srv->io = NULL; err = srv->cb->recv(srv, srv->upload.slot, &srv->io); if (err || !srv->io) { LOG_ERR("App rejected upload. err: %d io: %p", err, srv->io); - slot_del(srv, srv->upload.slot); + bt_mesh_dfu_slot_release(srv->upload.slot); upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); return 0; } @@ -461,7 +547,7 @@ static int handle_upload_start(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx timeout_base); if (err) { LOG_ERR("BLOB Server rejected upload (err: %d)", err); - slot_del(srv, srv->upload.slot); + bt_mesh_dfu_slot_release(srv->upload.slot); upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); return 0; } @@ -478,10 +564,89 @@ static int handle_upload_start_oob(struct bt_mesh_model *mod, struct bt_mesh_msg struct net_buf_simple *buf) { struct bt_mesh_dfd_srv *srv = mod->user_data; + uint8_t uri_len; + uint8_t *uri; + uint16_t fwid_len; + uint8_t *fwid; - LOG_DBG(""); + uri_len = net_buf_simple_pull_u8(buf); + + if (uri_len > buf->len) { + return -EINVAL; + } + + uri = net_buf_simple_pull_mem(buf, uri_len); + fwid_len = buf->len; + fwid = net_buf_simple_pull_mem(buf, fwid_len); + + LOG_DBG("Upload OOB Start"); + LOG_HEXDUMP_DBG(uri, uri_len, "URI"); + LOG_HEXDUMP_DBG(fwid, fwid_len, "FWID"); + + if (upload_is_busy(srv)) { +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD + if (srv->upload.is_oob && + uri_len == srv->upload.oob.uri_len && + fwid_len == srv->upload.oob.current_fwid_len && + !memcmp(uri, srv->upload.oob.uri, uri_len) && + !memcmp(fwid, srv->upload.oob.current_fwid, fwid_len)) { + /* Same image, return SUCCESS for idempotency */ + upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); + return 0; + } +#endif + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_BUSY_WITH_UPLOAD); + return 0; +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD + } else if (srv->upload.is_oob && srv->upload.is_pending_oob_check) { + /* Ignore the request if we didn't confirm the previous one. */ + return 0; +#endif + } + +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD + if (uri_len > CONFIG_BT_MESH_DFU_URI_MAXLEN || + fwid_len > CONFIG_BT_MESH_DFU_FWID_MAXLEN) { + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); + return 0; + } + struct bt_mesh_dfu_slot *slot = bt_mesh_dfu_slot_reserve(); + + if (slot == NULL) { + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INSUFFICIENT_RESOURCES); + return 0; + } + + /* This will be a no-op if the slot state isn't RESERVED, which is + * what we want. + */ + if (srv->upload.slot) { + bt_mesh_dfu_slot_release(srv->upload.slot); + } + + srv->upload.is_oob = true; + srv->upload.slot = slot; + memcpy(srv->upload.oob.uri, uri, uri_len); + srv->upload.oob.uri_len = uri_len; + memcpy(srv->upload.oob.current_fwid, fwid, fwid_len); + srv->upload.oob.current_fwid_len = fwid_len; + memcpy(&srv->upload.oob.ctx, ctx, sizeof(struct bt_mesh_msg_ctx)); + + int status = srv->cb->start_oob_upload(srv, srv->upload.slot, srv->upload.oob.uri, + srv->upload.oob.uri_len, + srv->upload.oob.current_fwid, + srv->upload.oob.current_fwid_len); + + if (status != BT_MESH_DFD_SUCCESS) { + upload_status_rsp(srv, ctx, status); + bt_mesh_dfu_slot_release(srv->upload.slot); + } else { + srv->upload.is_pending_oob_check = true; + } +#else upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_URI_NOT_SUPPORTED); +#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */ return 0; } @@ -492,7 +657,14 @@ static int handle_upload_cancel(struct bt_mesh_model *mod, struct bt_mesh_msg_ct struct bt_mesh_dfd_srv *srv = mod->user_data; srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_IDLE; - (void)bt_mesh_blob_srv_cancel(&srv->upload.blob); +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD + if (srv->upload.is_oob) { + srv->cb->cancel_oob_upload(srv, srv->upload.slot); + } else +#endif + { + (void)bt_mesh_blob_srv_cancel(&srv->upload.blob); + } upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); return 0; @@ -508,7 +680,7 @@ static void fw_status_rsp(struct bt_mesh_dfd_srv *srv, bt_mesh_model_msg_init(&rsp, BT_MESH_DFD_OP_FW_STATUS); net_buf_simple_add_u8(&rsp, status); - net_buf_simple_add_le16(&rsp, bt_mesh_dfu_slot_foreach(NULL, NULL)); + net_buf_simple_add_le16(&rsp, bt_mesh_dfu_slot_count()); net_buf_simple_add_le16(&rsp, idx); if (fwid) { @@ -522,7 +694,7 @@ static int handle_fw_get(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { struct bt_mesh_dfd_srv *srv = mod->user_data; - const struct bt_mesh_dfu_slot *slot; + struct bt_mesh_dfu_slot *slot; const uint8_t *fwid; size_t fwid_len; int idx; @@ -531,7 +703,7 @@ static int handle_fw_get(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, fwid = net_buf_simple_pull_mem(buf, fwid_len); idx = bt_mesh_dfu_slot_get(fwid, fwid_len, &slot); - if (idx >= 0 && bt_mesh_dfu_slot_is_valid(slot)) { + if (idx >= 0) { fw_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS, idx, fwid, fwid_len); } else { @@ -552,7 +724,7 @@ static int handle_fw_get_by_index(struct bt_mesh_model *mod, struct bt_mesh_msg_ idx = net_buf_simple_pull_le16(buf); slot = bt_mesh_dfu_slot_at(idx); - if (slot && bt_mesh_dfu_slot_is_valid(slot)) { + if (slot) { fw_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS, idx, slot->fwid, slot->fwid_len); } else { @@ -614,7 +786,7 @@ const struct bt_mesh_model_op _bt_mesh_dfd_srv_op[] = { { BT_MESH_DFD_OP_APPLY, BT_MESH_LEN_EXACT(0), handle_apply }, { BT_MESH_DFD_OP_UPLOAD_GET, BT_MESH_LEN_EXACT(0), handle_upload_get }, { BT_MESH_DFD_OP_UPLOAD_START, BT_MESH_LEN_MIN(16), handle_upload_start }, - { BT_MESH_DFD_OP_UPLOAD_START_OOB, BT_MESH_LEN_EXACT(2), handle_upload_start_oob }, + { BT_MESH_DFD_OP_UPLOAD_START_OOB, BT_MESH_LEN_MIN(2), handle_upload_start_oob }, { BT_MESH_DFD_OP_UPLOAD_CANCEL, BT_MESH_LEN_EXACT(0), handle_upload_cancel }, { BT_MESH_DFD_OP_FW_GET, BT_MESH_LEN_MIN(0), handle_fw_get }, { BT_MESH_DFD_OP_FW_GET_BY_INDEX, BT_MESH_LEN_EXACT(2), handle_fw_get_by_index }, @@ -729,8 +901,7 @@ static void upload_end(struct bt_mesh_blob_srv *b, uint64_t id, bool success) LOG_DBG("%u", success); - if (success) { - bt_mesh_dfu_slot_valid_set(srv->upload.slot, true); + if (success && (bt_mesh_dfu_slot_commit(srv->upload.slot) == 0)) { srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS; return; } @@ -850,7 +1021,7 @@ enum bt_mesh_dfd_status bt_mesh_dfd_srv_start(struct bt_mesh_dfd_srv *srv, xfer.mode = params->xfer_mode; xfer.slot = bt_mesh_dfu_slot_at(params->slot_idx); - if (!xfer.slot || !bt_mesh_dfu_slot_is_valid(xfer.slot)) { + if (!xfer.slot) { return BT_MESH_DFD_ERR_FW_NOT_FOUND; } @@ -887,6 +1058,13 @@ enum bt_mesh_dfd_status bt_mesh_dfd_srv_start(struct bt_mesh_dfd_srv *srv, sys_slist_init(&srv->inputs.targets); for (i = 0; i < srv->target_cnt; i++) { + uint16_t addr = srv->targets[i].blob.addr; + + memset(&srv->targets[i].blob, 0, sizeof(struct bt_mesh_blob_target)); + memset(&srv->pull_ctxs[i], 0, sizeof(struct bt_mesh_blob_target_pull)); + srv->targets[i].blob.addr = addr; + srv->targets[i].blob.pull = &srv->pull_ctxs[i]; + sys_slist_append(&srv->inputs.targets, &srv->targets[i].blob.n); } @@ -1013,7 +1191,7 @@ enum bt_mesh_dfd_status bt_mesh_dfd_srv_apply(struct bt_mesh_dfd_srv *srv) enum bt_mesh_dfd_status bt_mesh_dfd_srv_fw_delete(struct bt_mesh_dfd_srv *srv, size_t *fwid_len, const uint8_t **fwid) { - const struct bt_mesh_dfu_slot *slot; + struct bt_mesh_dfu_slot *slot; int idx, err; if (srv->phase != BT_MESH_DFD_PHASE_IDLE) { @@ -1023,7 +1201,7 @@ enum bt_mesh_dfd_status bt_mesh_dfd_srv_fw_delete(struct bt_mesh_dfd_srv *srv, s } idx = bt_mesh_dfu_slot_get(*fwid, *fwid_len, &slot); - if (idx < 0 || !bt_mesh_dfu_slot_is_valid(slot)) { + if (idx < 0) { return BT_MESH_DFD_SUCCESS; } @@ -1049,3 +1227,72 @@ enum bt_mesh_dfd_status bt_mesh_dfd_srv_fw_delete_all(struct bt_mesh_dfd_srv *sr return BT_MESH_DFD_SUCCESS; } + +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD +int bt_mesh_dfd_srv_oob_check_complete(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot, int status, + uint8_t *fwid, size_t fwid_len) +{ + int err; + + if (slot != srv->upload.slot || !srv->upload.is_oob || + srv->upload.phase == BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE || + !srv->upload.is_pending_oob_check) { + /* This should not happen, unless the application calls the function with a + * "wrong" pointer or at a wrong time. + */ + return -EINVAL; + } + + srv->upload.is_pending_oob_check = false; + + if (status != BT_MESH_DFD_SUCCESS) { + bt_mesh_dfu_slot_release(srv->upload.slot); + upload_status_rsp(srv, &srv->upload.oob.ctx, status); + return -ECANCELED; + } + + err = set_upload_fwid(srv, &srv->upload.oob.ctx, fwid, fwid_len); + + if (err) { + return err; + } + + upload_status_rsp(srv, &srv->upload.oob.ctx, BT_MESH_DFD_SUCCESS); + return 0; +} + +int bt_mesh_dfd_srv_oob_store_complete(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot, bool success, + size_t size, const uint8_t *metadata, size_t metadata_len) +{ + int err = 0; + + if (srv->upload.phase != BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE || + srv->upload.slot != slot || !srv->upload.is_oob) { + return -EINVAL; + } + + if (!success) { + goto error; + } + + err = bt_mesh_dfu_slot_info_set(srv->upload.slot, size, metadata, metadata_len); + if (err) { + goto error; + } + + err = bt_mesh_dfu_slot_commit(srv->upload.slot); + if (err) { + goto error; + } + + srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS; + return 0; + +error: + srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ERROR; + bt_mesh_dfu_slot_release(srv->upload.slot); + return err; +} +#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */ diff --git a/subsys/bluetooth/mesh/dfu_cli.c b/subsys/bluetooth/mesh/dfu_cli.c index 3805a04e450..4db7779116b 100644 --- a/subsys/bluetooth/mesh/dfu_cli.c +++ b/subsys/bluetooth/mesh/dfu_cli.c @@ -305,6 +305,23 @@ static void tx_end(int err, void *cb_data) blob_cli_broadcast_tx_complete(&cli->blob); } +static int tx(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf, + const struct bt_mesh_send_cb *cb, struct bt_mesh_dfu_cli *cli) +{ + int err; + + err = bt_mesh_model_send(mod, ctx, buf, cb, cli); + if (err) { + LOG_ERR("Send err: %d", err); + if (cb) { + cb->end(err, cli); + } + return err; + } + + return 0; +} + static int info_get(struct bt_mesh_dfu_cli *cli, struct bt_mesh_msg_ctx *ctx, uint8_t idx, uint8_t max_count, const struct bt_mesh_send_cb *cb) @@ -314,7 +331,7 @@ static int info_get(struct bt_mesh_dfu_cli *cli, struct bt_mesh_msg_ctx *ctx, net_buf_simple_add_u8(&buf, idx); net_buf_simple_add_u8(&buf, max_count); - return bt_mesh_model_send(cli->mod, ctx, &buf, cb, cli); + return tx(cli->mod, ctx, &buf, cb, cli); } static void send_info_get(struct bt_mesh_blob_cli *b, uint16_t dst) @@ -352,7 +369,7 @@ static void send_update_start(struct bt_mesh_blob_cli *b, uint16_t dst) net_buf_simple_add_mem(&buf, cli->xfer.slot->metadata, cli->xfer.slot->metadata_len); - bt_mesh_model_send(cli->mod, &ctx, &buf, &send_cb, cli); + (void)tx(cli->mod, &ctx, &buf, &send_cb, cli); } static void send_update_get(struct bt_mesh_blob_cli *b, uint16_t dst) @@ -363,7 +380,7 @@ static void send_update_get(struct bt_mesh_blob_cli *b, uint16_t dst) BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_GET, 0); bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_GET); - bt_mesh_model_send(cli->mod, &ctx, &buf, &send_cb, cli); + (void)tx(cli->mod, &ctx, &buf, &send_cb, cli); } static void send_update_cancel(struct bt_mesh_blob_cli *b, uint16_t dst) @@ -374,7 +391,7 @@ static void send_update_cancel(struct bt_mesh_blob_cli *b, uint16_t dst) BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_CANCEL, 0); bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_CANCEL); - bt_mesh_model_send(cli->mod, &ctx, &buf, &send_cb, cli); + (void)tx(cli->mod, &ctx, &buf, &send_cb, cli); } static void send_update_apply(struct bt_mesh_blob_cli *b, uint16_t dst) @@ -385,7 +402,7 @@ static void send_update_apply(struct bt_mesh_blob_cli *b, uint16_t dst) BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_APPLY, 0); bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_APPLY); - bt_mesh_model_send(cli->mod, &ctx, &buf, &send_cb, cli); + (void)tx(cli->mod, &ctx, &buf, &send_cb, cli); } /******************************************************************************* diff --git a/subsys/bluetooth/mesh/dfu_slot.c b/subsys/bluetooth/mesh/dfu_slot.c index 16628793457..097d5abcc82 100644 --- a/subsys/bluetooth/mesh/dfu_slot.c +++ b/subsys/bluetooth/mesh/dfu_slot.c @@ -20,23 +20,22 @@ LOG_MODULE_REGISTER(bt_mesh_dfu_slot); #define DFU_SLOT_SETTINGS_PATH "bt/mesh-dfu/slot" -#define HEADER_SIZE offsetof(struct bt_mesh_dfu_slot, fwid) +#define HEADER_SIZE offsetof(struct slot, slot.fwid) #define PROP_HEADER "h" #define PROP_FWID "id" #define PROP_METADATA "m" -#define PROP_URI "u" -#define VALID_SLOTS_TAG "v" -#define SLOT_IN_ARRAY(_slot) PART_OF_ARRAY(slots, CONTAINER_OF(_slot, struct slot, slot)) - -static ATOMIC_DEFINE(valid_slots, CONFIG_BT_MESH_DFU_SLOT_CNT); static sys_slist_t list; + static struct slot { - sys_snode_t n; + uint32_t idx; struct bt_mesh_dfu_slot slot; + sys_snode_t n; } slots[CONFIG_BT_MESH_DFU_SLOT_CNT]; +static uint32_t slot_index; + static char *slot_entry_encode(uint16_t idx, char buf[SLOT_ENTRY_BUFLEN], const char *property) { @@ -46,32 +45,29 @@ static char *slot_entry_encode(uint16_t idx, char buf[SLOT_ENTRY_BUFLEN], return buf; } -static inline bool slot_in_use(const struct bt_mesh_dfu_slot *slot) +static bool slot_eq(const struct bt_mesh_dfu_slot *slot, + const uint8_t *fwid, size_t fwid_len) { - return slot->size > 0U; + return (slot->fwid_len == fwid_len) && + !memcmp(fwid, slot->fwid, fwid_len); } -static inline uint16_t slot_idx(const struct bt_mesh_dfu_slot *slot) +static bool is_slot_committed(struct slot *slot_to_check) { - return CONTAINER_OF(slot, struct slot, slot) - &slots[0]; -} + struct slot *s; -static inline void slot_invalidate(struct slot *slot_to_invalidate) -{ - slot_to_invalidate->slot.size = 0U; - atomic_clear_bit(valid_slots, slot_to_invalidate - &slots[0]); -} + SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { + if (s == slot_to_check) { + return true; + } + } -static bool slot_eq(const struct bt_mesh_dfu_slot *slot, - const uint8_t *fwid, size_t fwid_len) -{ - return (slot->fwid_len == fwid_len) && - !memcmp(fwid, slot->fwid, fwid_len); + return false; } static int slot_store(const struct slot *slot_to_store) { - uint16_t idx = slot_to_store - &slots[0]; + uint16_t idx = ARRAY_INDEX(slots, slot_to_store); char buf[SLOT_ENTRY_BUFLEN]; int err; @@ -90,55 +86,51 @@ static int slot_store(const struct slot *slot_to_store) err = settings_save_one(slot_entry_encode(idx, buf, PROP_METADATA), slot_to_store->slot.metadata, slot_to_store->slot.metadata_len); - if (err) { - return err; - } - return settings_save_one(slot_entry_encode(idx, buf, PROP_URI), - slot_to_store->slot.uri, slot_to_store->slot.uri_len); + return err; } static void slot_erase(struct slot *slot_to_erase) { - uint16_t idx = slot_to_erase - &slots[0]; + uint16_t idx = ARRAY_INDEX(slots, slot_to_erase); char buf[SLOT_ENTRY_BUFLEN]; settings_delete(slot_entry_encode(idx, buf, PROP_HEADER)); settings_delete(slot_entry_encode(idx, buf, PROP_FWID)); settings_delete(slot_entry_encode(idx, buf, PROP_METADATA)); - settings_delete(slot_entry_encode(idx, buf, PROP_URI)); } -static int valid_slots_store(void) +static void slot_index_defrag(void) { - return settings_save_one(DFU_SLOT_SETTINGS_PATH "/" VALID_SLOTS_TAG, - valid_slots, sizeof(valid_slots)); + slot_index = 0; + struct slot *s; + + SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { + s->idx = ++slot_index; + slot_store(s); + } } -const struct bt_mesh_dfu_slot * -bt_mesh_dfu_slot_add(size_t size, const uint8_t *fwid, - size_t fwid_len, const uint8_t *metadata, - size_t metadata_len, const char *uri, size_t uri_len) +int bt_mesh_dfu_slot_count(void) { - struct slot *slot = NULL; - int err, i; + int cnt = 0; + sys_snode_t *n; - if (size == 0 || fwid_len > CONFIG_BT_MESH_DFU_FWID_MAXLEN || - metadata_len > CONFIG_BT_MESH_DFU_METADATA_MAXLEN || - uri_len > CONFIG_BT_MESH_DFU_URI_MAXLEN) { - LOG_WRN("Param too large: (size: %d, fwid: %d, metadata: %d, uri: %d)", - size, fwid_len, metadata_len, uri_len); - return NULL; + SYS_SLIST_FOR_EACH_NODE(&list, n) { + cnt++; } - for (i = 0; i < ARRAY_SIZE(slots); ++i) { - if (!slot_in_use(&slots[i].slot)) { - slot = &slots[i]; - continue; - } + return cnt; +} - if (slot_eq(&slots[i].slot, fwid, fwid_len)) { - return &slots[i].slot; +struct bt_mesh_dfu_slot *bt_mesh_dfu_slot_reserve(void) +{ + struct slot *slot = NULL; + + for (int i = 0; i < ARRAY_SIZE(slots); ++i) { + if (slots[i].idx == 0) { + slot = &slots[i]; + break; } } @@ -147,110 +139,136 @@ bt_mesh_dfu_slot_add(size_t size, const uint8_t *fwid, return NULL; } - slot->slot.fwid_len = fwid_len; - slot->slot.metadata_len = metadata_len; - slot->slot.uri_len = uri_len; - memcpy(slot->slot.fwid, fwid, fwid_len); - memcpy(slot->slot.metadata, metadata, metadata_len); - memcpy(slot->slot.uri, uri, uri_len); - slot->slot.size = size; - - err = slot_store(slot); - if (err) { - slot_invalidate(slot); - LOG_WRN("Store failed (err: %d)", err); - return NULL; + if (slot_index == UINT32_MAX) { + slot_index_defrag(); } - sys_slist_append(&list, &slot->n); + slot->slot.fwid_len = 0; + slot->slot.metadata_len = 0; + slot->slot.size = 0; + slot->idx = ++slot_index; + + LOG_DBG("Reserved slot #%u", slot - &slots[0]); - LOG_DBG("Added slot #%u: %s", slot - &slots[0], - bt_hex(slot->slot.fwid, slot->slot.fwid_len)); return &slot->slot; } -int bt_mesh_dfu_slot_valid_set(const struct bt_mesh_dfu_slot *slot, bool valid) +int bt_mesh_dfu_slot_info_set(struct bt_mesh_dfu_slot *dfu_slot, size_t size, + const uint8_t *metadata, size_t metadata_len) { - uint16_t idx; - bool prev; - int err; + struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot); + + if (metadata_len > CONFIG_BT_MESH_DFU_METADATA_MAXLEN) { + return -EFBIG; + } - if (!SLOT_IN_ARRAY(slot) || !slot_in_use(slot)) { - return -ENOENT; + if (slot->idx == 0 || is_slot_committed(slot)) { + return -EINVAL; } - idx = slot_idx(slot); + slot->slot.size = size; + slot->slot.metadata_len = metadata_len; + memcpy(slot->slot.metadata, metadata, metadata_len); + return 0; +} + +int bt_mesh_dfu_slot_fwid_set(struct bt_mesh_dfu_slot *dfu_slot, + const uint8_t *fwid, size_t fwid_len) +{ + struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot); - LOG_DBG("%u: %u", idx, valid); + if (fwid_len > CONFIG_BT_MESH_DFU_FWID_MAXLEN) { + return -EFBIG; + } - if (valid) { - prev = atomic_test_and_set_bit(valid_slots, idx); - } else { - prev = atomic_test_and_clear_bit(valid_slots, idx); + if (slot->idx == 0 || is_slot_committed(slot)) { + return -EINVAL; } - if (valid == prev) { - return 0; + for (int i = 0; i < ARRAY_SIZE(slots); i++) { + if (slots[i].idx != 0 && + slot_eq(&slots[i].slot, fwid, fwid_len)) { + return is_slot_committed(&slots[i]) ? + -EEXIST : -EALREADY; + } + } + + slot->slot.fwid_len = fwid_len; + memcpy(slot->slot.fwid, fwid, fwid_len); + return 0; +} + +int bt_mesh_dfu_slot_commit(struct bt_mesh_dfu_slot *dfu_slot) +{ + int err; + struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot); + + if (slot->idx == 0 || + slot->slot.fwid_len == 0 || + slot->slot.size == 0 || + is_slot_committed(slot)) { + return -EINVAL; } - err = valid_slots_store(); + err = slot_store(slot); if (err) { - LOG_WRN("Storage failed. err: %d", err); - atomic_set_bit_to(valid_slots, idx, prev); + LOG_WRN("Store failed (err: %d)", err); + return err; } - return err; + sys_slist_append(&list, &slot->n); + + LOG_DBG("Stored slot #%u: %s", ARRAY_INDEX(slots, slot), + bt_hex(slot->slot.fwid, slot->slot.fwid_len)); + return 0; } -bool bt_mesh_dfu_slot_is_valid(const struct bt_mesh_dfu_slot *slot) +void bt_mesh_dfu_slot_release(const struct bt_mesh_dfu_slot *dfu_slot) { - uint16_t idx; + struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot); - if (!SLOT_IN_ARRAY(slot) || !slot_in_use(slot)) { - return false; + if (is_slot_committed(slot)) { + return; } - idx = slot_idx(slot); - return atomic_test_bit(valid_slots, idx); + slot->idx = 0; } -int bt_mesh_dfu_slot_del(const struct bt_mesh_dfu_slot *slot) +int bt_mesh_dfu_slot_del(const struct bt_mesh_dfu_slot *dfu_slot) { - struct slot *s = CONTAINER_OF(slot, struct slot, slot); + struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot); - if (!SLOT_IN_ARRAY(slot) || !slot_in_use(slot)) { - return -ENOENT; + if (!sys_slist_find_and_remove(&list, &slot->n)) { + return -EINVAL; } - LOG_DBG("%u", s - &slots[0]); + int idx = ARRAY_INDEX(slots, slot); - slot_erase(s); - slot_invalidate(s); - sys_slist_find_and_remove(&list, &s->n); + LOG_DBG("%u", idx); + + slot_erase(slot); + slot->idx = 0; return 0; } -int bt_mesh_dfu_slot_del_all(void) +void bt_mesh_dfu_slot_del_all(void) { struct slot *s; - SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { slot_erase(s); - slot_invalidate(s); + s->idx = 0; } sys_slist_init(&list); - - return 0; } -const struct bt_mesh_dfu_slot *bt_mesh_dfu_slot_at(uint16_t idx) +const struct bt_mesh_dfu_slot *bt_mesh_dfu_slot_at(uint16_t img_idx) { struct slot *s; SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { - if (!idx--) { + if (!img_idx--) { return &s->slot; } } @@ -258,34 +276,33 @@ const struct bt_mesh_dfu_slot *bt_mesh_dfu_slot_at(uint16_t idx) return NULL; } -int bt_mesh_dfu_slot_get(const uint8_t *fwid, size_t fwid_len, - const struct bt_mesh_dfu_slot **slot) +int bt_mesh_dfu_slot_get(const uint8_t *fwid, size_t fwid_len, struct bt_mesh_dfu_slot **slot) { struct slot *s; int idx = 0; SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { if (slot_eq(&s->slot, fwid, fwid_len)) { - *slot = &s->slot; + if (slot) { + *slot = &s->slot; + } return idx; } - idx++; } return -ENOENT; } -int bt_mesh_dfu_slot_idx_get(const struct bt_mesh_dfu_slot *slot) +int bt_mesh_dfu_slot_img_idx_get(const struct bt_mesh_dfu_slot *dfu_slot) { struct slot *s; int idx = 0; SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { - if (&s->slot == slot) { + if (&s->slot == dfu_slot) { return idx; } - idx++; } @@ -295,11 +312,12 @@ int bt_mesh_dfu_slot_idx_get(const struct bt_mesh_dfu_slot *slot) size_t bt_mesh_dfu_slot_foreach(bt_mesh_dfu_slot_cb_t cb, void *user_data) { enum bt_mesh_dfu_iter iter; - struct slot *s; size_t cnt = 0; + struct slot *s; SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { cnt++; + if (!cb) { continue; } @@ -320,15 +338,6 @@ static int slot_data_load(const char *key, size_t len_rd, size_t len; uint16_t idx; - if (!strncmp(key, VALID_SLOTS_TAG, 1)) { - if (read_cb(cb_arg, valid_slots, - MIN(sizeof(valid_slots), len_rd)) < 0) { - return -EINVAL; - } - - return 0; - } - idx = strtol(key, NULL, 16); if (idx >= ARRAY_SIZE(slots)) { @@ -339,16 +348,34 @@ static int slot_data_load(const char *key, size_t len_rd, if (!strncmp(prop, PROP_HEADER, len)) { if (read_cb(cb_arg, &slots[idx], HEADER_SIZE) > 0) { - sys_slist_append(&list, &slots[idx].n); - } + struct slot *s, *prev = NULL; + + SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { + if (s->idx > slots[idx].idx) { + break; + } + + prev = s; + } + if (prev == NULL) { + sys_slist_prepend(&list, &slots[idx].n); + } else { + sys_slist_insert(&list, &prev->n, &slots[idx].n); + } + + if (slots[idx].idx >= slot_index) { + slot_index = slots[idx].idx + 1; + } + } return 0; } if (!strncmp(prop, PROP_FWID, len)) { if (read_cb(cb_arg, &slots[idx].slot.fwid, sizeof(slots[idx].slot.fwid)) < 0) { - slot_invalidate(&slots[idx]); + slots[idx].idx = 0; + sys_slist_find_and_remove(&list, &slots[idx].n); return 0; } @@ -359,7 +386,8 @@ static int slot_data_load(const char *key, size_t len_rd, if (!strncmp(prop, PROP_METADATA, len)) { if (read_cb(cb_arg, &slots[idx].slot.metadata, sizeof(slots[idx].slot.metadata)) < 0) { - slot_invalidate(&slots[idx]); + slots[idx].idx = 0; + sys_slist_find_and_remove(&list, &slots[idx].n); return 0; } @@ -367,16 +395,6 @@ static int slot_data_load(const char *key, size_t len_rd, return 0; } - if (!strncmp(prop, PROP_URI, len)) { - if (read_cb(cb_arg, &slots[idx].slot.uri, - sizeof(slots[idx].slot.uri)) < 0) { - slot_invalidate(&slots[idx]); - return 0; - } - - slots[idx].slot.uri_len = len_rd; - } - return 0; } diff --git a/subsys/bluetooth/mesh/dfu_slot.h b/subsys/bluetooth/mesh/dfu_slot.h index 1bc58f64de9..969c6d3d25b 100644 --- a/subsys/bluetooth/mesh/dfu_slot.h +++ b/subsys/bluetooth/mesh/dfu_slot.h @@ -16,50 +16,69 @@ typedef enum bt_mesh_dfu_iter (*bt_mesh_dfu_slot_cb_t)( const struct bt_mesh_dfu_slot *slot, void *user_data); -/** @brief Register a new DFU image slot for a distributable image. +/** @brief Get the number of slots committed to the firmware list. + * + * @return Number of committed slots. + */ +int bt_mesh_dfu_slot_count(void); + +/** @brief Reserve a new DFU image slot for a distributable image. * * A DFU image slot represents a single distributable DFU image with all its - * metadata. + * metadata. The slot data must be set using @ref bt_mesh_dfu_slot_info_set and + * @ref bt_mesh_dfu_slot_fwid_set, and the slot committed using + * @ref bt_mesh_dfu_slot_commit for the slot to be considered part of the slot + * list. * - * @note The slot is allocated as invalid. Call - * @ref bt_mesh_dfu_slot_valid_set to make it valid. + * @return A pointer to the reserved slot, or NULL if allocation failed. + */ +struct bt_mesh_dfu_slot *bt_mesh_dfu_slot_reserve(void); + +/** @brief Set the size and metadata for a reserved slot. * - * @param size Size of the image in bytes. - * @param fwid Firmware ID. - * @param fwid_len Length of the firmware ID, at most @c - * CONFIG_BT_MESH_DFU_FWID_MAXLEN. + * @param dfu_slot Pointer to the reserved slot for which to set the + * metadata. + * @param size The size of the image. * @param metadata Metadata or NULL. * @param metadata_len Length of the metadata, at most @c * CONFIG_BT_MESH_DFU_METADATA_MAXLEN. - * @param uri Image URI or NULL. - * @param uri_len Length of the image URI, at most @c - * CONFIG_BT_MESH_DFU_URI_MAXLEN. * - * @return A pointer to the allocated slot, or NULL if allocation failed. + * @return 0 on success, (negative) error code otherwise. */ -const struct bt_mesh_dfu_slot * -bt_mesh_dfu_slot_add(size_t size, const uint8_t *fwid, size_t fwid_len, - const uint8_t *metadata, size_t metadata_len, - const char *uri, size_t uri_len); +int bt_mesh_dfu_slot_info_set(struct bt_mesh_dfu_slot *dfu_slot, size_t size, + const uint8_t *metadata, size_t metadata_len); -/** @brief Set whether the given slot is valid. +/** @brief Set the new fwid for the incoming image for a reserved slot. * - * @param slot Allocated DFU image slot. - * @param valid New valid state of the slot. + * @param dfu_slot Pointer to the reserved slot for which to set the fwid. + * @param fwid Fwid to set. + * @param fwid_len Length of the fwid, at most @c + * CONFIG_BT_MESH_DFU_FWID_MAXLEN. * - * @return 0 on success, or (negative) error code on failure. + * @return 0 on success, (negative) error code otherwise. */ -int bt_mesh_dfu_slot_valid_set(const struct bt_mesh_dfu_slot *slot, bool valid); +int bt_mesh_dfu_slot_fwid_set(struct bt_mesh_dfu_slot *dfu_slot, + const uint8_t *fwid, size_t fwid_len); -/** @brief Check whether a slot is valid. +/** @brief Commit the reserved slot to the list of slots, and store it + * persistently. + * + * If the commit fails for any reason, the slot will still be in the reserved + * state after this call. + * + * @param dfu_slot Pointer to the reserved slot. * - * @param slot Slot to check. + * @return 0 on success, (negative) error code otherwise. + */ +int bt_mesh_dfu_slot_commit(struct bt_mesh_dfu_slot *dfu_slot); + +/** @brief Release a reserved slot so that it can be reserved again. * - * @return true if the slot is valid, false otherwise. + * @param dfu_slot Pointer to the reserved slot. */ -bool bt_mesh_dfu_slot_is_valid(const struct bt_mesh_dfu_slot *slot); +void bt_mesh_dfu_slot_release(const struct bt_mesh_dfu_slot *dfu_slot); -/** @brief Delete an allocated DFU image slot. +/** @brief Delete a committed DFU image slot. * * @param slot Slot to delete. Must be a valid pointer acquired from this * module. @@ -72,18 +91,19 @@ int bt_mesh_dfu_slot_del(const struct bt_mesh_dfu_slot *slot); * * @return 0 on success, or (negative) error code on failure. */ -int bt_mesh_dfu_slot_del_all(void); +void bt_mesh_dfu_slot_del_all(void); -/** @brief Get the DFU image slot at the given index. +/** @brief Get the DFU image slot at the given firmware image list index. * * @param idx DFU image slot index. * * @return The DFU image slot at the given index, or NULL if no slot exists with the * given index. */ -const struct bt_mesh_dfu_slot *bt_mesh_dfu_slot_at(uint16_t idx); +const struct bt_mesh_dfu_slot *bt_mesh_dfu_slot_at(uint16_t img_idx); -/** @brief Get the DFU image slot for the image with the given firmware ID. +/** @brief Get the committed DFU image slot for the image with the given + * firmware ID. * * @param fwid Firmware ID. * @param fwid_len Firmware ID length. @@ -91,16 +111,15 @@ const struct bt_mesh_dfu_slot *bt_mesh_dfu_slot_at(uint16_t idx); * * @return Slot index on success, or negative error code on failure. */ -int bt_mesh_dfu_slot_get(const uint8_t *fwid, size_t fwid_len, - const struct bt_mesh_dfu_slot **slot); +int bt_mesh_dfu_slot_get(const uint8_t *fwid, size_t fwid_len, struct bt_mesh_dfu_slot **slot); -/** @brief Get the DFU image slot index of the given slot. +/** @brief Get the index in the firmware image list for the given slot. * * @param slot Slot to find. * * @return Slot index on success, or negative error code on failure. */ -int bt_mesh_dfu_slot_idx_get(const struct bt_mesh_dfu_slot *slot); +int bt_mesh_dfu_slot_img_idx_get(const struct bt_mesh_dfu_slot *slot); /** @brief Iterate through all DFU image slots. * diff --git a/subsys/bluetooth/mesh/dfu_srv.c b/subsys/bluetooth/mesh/dfu_srv.c index 282a40074c9..462a777b46c 100644 --- a/subsys/bluetooth/mesh/dfu_srv.c +++ b/subsys/bluetooth/mesh/dfu_srv.c @@ -302,10 +302,10 @@ static int handle_start(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, status = BT_MESH_DFU_ERR_WRONG_PHASE; } else { status = BT_MESH_DFU_SUCCESS; + srv->update.ttl = ttl; + srv->blob.state.xfer.id = blob_id; } - srv->update.ttl = ttl; - srv->blob.state.xfer.id = blob_id; LOG_WRN("Busy. Phase: %u", srv->update.phase); goto rsp; } diff --git a/subsys/bluetooth/mesh/friend.c b/subsys/bluetooth/mesh/friend.c index 179755d9706..73b75eadf4f 100644 --- a/subsys/bluetooth/mesh/friend.c +++ b/subsys/bluetooth/mesh/friend.c @@ -1281,7 +1281,7 @@ static void friend_timeout(struct k_work *work) frnd->queue_size--; send_last: - buf = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_FRIEND_ADV, + buf = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_ADV_TAG_FRIEND, FRIEND_XMIT, K_NO_WAIT); if (!buf) { LOG_ERR("Unable to allocate friend adv buffer"); diff --git a/subsys/bluetooth/mesh/large_comp_data_srv.c b/subsys/bluetooth/mesh/large_comp_data_srv.c index 9f097f4355a..4ee591d9efd 100644 --- a/subsys/bluetooth/mesh/large_comp_data_srv.c +++ b/subsys/bluetooth/mesh/large_comp_data_srv.c @@ -53,30 +53,45 @@ static int handle_large_comp_data_get(struct bt_mesh_model *model, struct bt_mes ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, bt_hex(buf->data, buf->len)); - page = net_buf_simple_pull_u8(buf); + page = bt_mesh_comp_parse_page(buf); offset = net_buf_simple_pull_le16(buf); LOG_DBG("page %u offset %u", page, offset); bt_mesh_model_msg_init(&rsp, OP_LARGE_COMP_DATA_STATUS); - - if (page != 0U) { - LOG_DBG("Composition page %u not available", page); - page = 0U; - } - net_buf_simple_add_u8(&rsp, page); - - total_size = bt_mesh_comp_page_0_size(); net_buf_simple_add_le16(&rsp, offset); - net_buf_simple_add_le16(&rsp, total_size); - if (offset < total_size) { - err = bt_mesh_comp_data_get_page_0(&rsp, offset); - if (err && err != -E2BIG) { - LOG_ERR("comp_get_page_0 returned error"); + if (atomic_test_bit(bt_mesh.flags, BT_MESH_COMP_DIRTY) && page < 128) { + size_t msg_space; + + NET_BUF_SIMPLE_DEFINE(temp_buf, CONFIG_BT_MESH_COMP_PST_BUF_SIZE); + err = bt_mesh_comp_read(&temp_buf, page); + if (err) { + LOG_ERR("Could not read comp data p%d, err: %d", page, err); return err; } + + net_buf_simple_add_le16(&rsp, temp_buf.len); + if (offset > temp_buf.len) { + return 0; + } + + msg_space = net_buf_simple_tailroom(&rsp) - BT_MESH_MIC_SHORT; + net_buf_simple_add_mem( + &rsp, temp_buf.data + offset, + (msg_space < (temp_buf.len - offset)) ? msg_space : temp_buf.len - offset); + } else { + total_size = bt_mesh_comp_page_size(page); + net_buf_simple_add_le16(&rsp, total_size); + + if (offset < total_size) { + err = bt_mesh_comp_data_get_page(&rsp, page, offset); + if (err && err != -E2BIG) { + LOG_ERR("Could not read comp data p%d, err: %d", page, err); + return err; + } + } } if (bt_mesh_model_send(model, ctx, &rsp, NULL, NULL)) { diff --git a/subsys/bluetooth/mesh/net.c b/subsys/bluetooth/mesh/net.c index 2cfc709c648..61efaeb2c81 100644 --- a/subsys/bluetooth/mesh/net.c +++ b/subsys/bluetooth/mesh/net.c @@ -712,7 +712,7 @@ static void bt_mesh_net_relay(struct net_buf_simple *sbuf, transmit = bt_mesh_net_transmit_get(); } - buf = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_RELAY_ADV, + buf = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_ADV_TAG_RELAY, transmit, K_NO_WAIT); if (!buf) { LOG_DBG("Out of relay buffers"); diff --git a/subsys/bluetooth/mesh/od_priv_proxy.h b/subsys/bluetooth/mesh/od_priv_proxy.h new file mode 100644 index 00000000000..7baa7f067fc --- /dev/null +++ b/subsys/bluetooth/mesh/od_priv_proxy.h @@ -0,0 +1,7 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +void bt_mesh_od_priv_proxy_srv_store_schedule(void); diff --git a/subsys/bluetooth/mesh/od_priv_proxy_cli.c b/subsys/bluetooth/mesh/od_priv_proxy_cli.c index 3dc8722ff72..67f5ac961ad 100644 --- a/subsys/bluetooth/mesh/od_priv_proxy_cli.c +++ b/subsys/bluetooth/mesh/od_priv_proxy_cli.c @@ -54,23 +54,25 @@ const struct bt_mesh_model_op _bt_mesh_od_priv_proxy_cli_op[] = { BT_MESH_MODEL_OP_END }; -int bt_mesh_od_priv_proxy_cli_get(struct bt_mesh_msg_ctx *ctx, uint8_t *val) +int bt_mesh_od_priv_proxy_cli_get(uint16_t net_idx, uint16_t addr, uint8_t *val_rsp) { + struct bt_mesh_msg_ctx ctx = BT_MESH_MSG_CTX_INIT_DEV(net_idx, addr); const struct bt_mesh_msg_rsp_ctx rsp = { .ack = &cli->ack_ctx, .op = OP_OD_PRIV_PROXY_STATUS, - .user_data = val, + .user_data = val_rsp, .timeout = msg_timeout, }; BT_MESH_MODEL_BUF_DEFINE(msg, OP_OD_PRIV_PROXY_GET, 0); bt_mesh_model_msg_init(&msg, OP_OD_PRIV_PROXY_GET); - return bt_mesh_msg_ackd_send(cli->model, ctx, &msg, val ? &rsp : NULL); + return bt_mesh_msg_ackd_send(cli->model, &ctx, &msg, val_rsp ? &rsp : NULL); } -int bt_mesh_od_priv_proxy_cli_set(struct bt_mesh_msg_ctx *ctx, uint8_t val, uint8_t *val_rsp) +int bt_mesh_od_priv_proxy_cli_set(uint16_t net_idx, uint16_t addr, uint8_t val, uint8_t *val_rsp) { + struct bt_mesh_msg_ctx ctx = BT_MESH_MSG_CTX_INIT_DEV(net_idx, addr); const struct bt_mesh_msg_rsp_ctx rsp = { .ack = &cli->ack_ctx, .op = OP_OD_PRIV_PROXY_STATUS, @@ -83,7 +85,7 @@ int bt_mesh_od_priv_proxy_cli_set(struct bt_mesh_msg_ctx *ctx, uint8_t val, uint net_buf_simple_add_u8(&msg, val); - return bt_mesh_msg_ackd_send(cli->model, ctx, &msg, val_rsp ? &rsp : NULL); + return bt_mesh_msg_ackd_send(cli->model, &ctx, &msg, val_rsp ? &rsp : NULL); } void bt_mesh_od_priv_proxy_cli_timeout_set(int32_t timeout) diff --git a/subsys/bluetooth/mesh/od_priv_proxy_srv.c b/subsys/bluetooth/mesh/od_priv_proxy_srv.c index 32f5ea44179..b18a8ec7530 100644 --- a/subsys/bluetooth/mesh/od_priv_proxy_srv.c +++ b/subsys/bluetooth/mesh/od_priv_proxy_srv.c @@ -9,11 +9,28 @@ #include "access.h" #include "cfg.h" #include "foundation.h" +#include "settings.h" #define LOG_LEVEL CONFIG_BT_MESH_MODEL_LOG_LEVEL #include LOG_MODULE_REGISTER(bt_mesh_od_priv_proxy_srv); + +static struct bt_mesh_model *od_priv_proxy_srv; +static uint8_t on_demand_state; + +static int od_priv_proxy_store(bool delete) +{ + if (!IS_ENABLED(CONFIG_BT_SETTINGS)) { + return 0; + } + + const void *data = delete ? NULL : &on_demand_state; + size_t len = delete ? 0 : sizeof(uint8_t); + + return bt_mesh_model_data_store(od_priv_proxy_srv, false, "pp", data, len); +} + static int proxy_status_rsp(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx) { @@ -64,6 +81,8 @@ const struct bt_mesh_model_op _bt_mesh_od_priv_proxy_srv_op[] = { static int od_priv_proxy_srv_init(struct bt_mesh_model *mod) { + od_priv_proxy_srv = mod; + struct bt_mesh_model *priv_beacon_srv = bt_mesh_model_find( bt_mesh_model_elem(mod), BT_MESH_MODEL_ID_PRIV_BEACON_SRV); struct bt_mesh_model *sol_pdu_rpl_srv = bt_mesh_model_find( @@ -89,6 +108,52 @@ static int od_priv_proxy_srv_init(struct bt_mesh_model *mod) return 0; } +static void od_priv_proxy_srv_reset(struct bt_mesh_model *model) +{ + on_demand_state = 0; + od_priv_proxy_store(true); +} + +#ifdef CONFIG_BT_SETTINGS +static int od_priv_proxy_srv_settings_set(struct bt_mesh_model *model, const char *name, + size_t len_rd, settings_read_cb read_cb, void *cb_data) +{ + int err; + + if (len_rd == 0) { + LOG_DBG("Cleared configuration state"); + return 0; + } + + err = bt_mesh_settings_set(read_cb, cb_data, &on_demand_state, sizeof(uint8_t)); + if (err) { + LOG_ERR("Failed to set OD private proxy state"); + return err; + } + + bt_mesh_od_priv_proxy_set(on_demand_state); + return 0; +} + +static void od_priv_proxy_srv_pending_store(struct bt_mesh_model *model) +{ + on_demand_state = bt_mesh_od_priv_proxy_get(); + od_priv_proxy_store(false); +} +#endif + const struct bt_mesh_model_cb _bt_mesh_od_priv_proxy_srv_cb = { .init = od_priv_proxy_srv_init, + .reset = od_priv_proxy_srv_reset, +#ifdef CONFIG_BT_SETTINGS + .settings_set = od_priv_proxy_srv_settings_set, + .pending_store = od_priv_proxy_srv_pending_store, +#endif }; + +void bt_mesh_od_priv_proxy_srv_store_schedule(void) +{ + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_model_data_store_schedule(od_priv_proxy_srv); + } +} diff --git a/subsys/bluetooth/mesh/op_agg.c b/subsys/bluetooth/mesh/op_agg.c index 4f6baacc898..1580c7f0847 100644 --- a/subsys/bluetooth/mesh/op_agg.c +++ b/subsys/bluetooth/mesh/op_agg.c @@ -17,10 +17,7 @@ LOG_MODULE_REGISTER(bt_mesh_op_agg); #define LENGTH_SHORT_MAX BIT_MASK(7) NET_BUF_SIMPLE_DEFINE_STATIC(sdu, BT_MESH_TX_SDU_MAX); - -#if IS_ENABLED(CONFIG_BT_MESH_OP_AGG_CLI) NET_BUF_SIMPLE_DEFINE_STATIC(srcs, BT_MESH_TX_SDU_MAX); -#endif static struct op_agg_ctx agg_ctx = { .sdu = &sdu, diff --git a/subsys/bluetooth/mesh/pb_adv.c b/subsys/bluetooth/mesh/pb_adv.c index 27d55763298..cb5f53f32b7 100644 --- a/subsys/bluetooth/mesh/pb_adv.c +++ b/subsys/bluetooth/mesh/pb_adv.c @@ -55,11 +55,11 @@ LOG_MODULE_REGISTER(bt_mesh_pb_adv); /* Acked messages, will do retransmissions manually, taking acks into account: */ -#define RETRANSMITS_RELIABLE 0 +#define RETRANSMITS_RELIABLE CONFIG_BT_MESH_PB_ADV_TRANS_PDU_RETRANSMIT_COUNT /* PDU acks: */ -#define RETRANSMITS_ACK 2 +#define RETRANSMITS_ACK CONFIG_BT_MESH_PB_ADV_TRANS_ACK_RETRANSMIT_COUNT /* Link close retransmits: */ -#define RETRANSMITS_LINK_CLOSE 2 +#define RETRANSMITS_LINK_CLOSE CONFIG_BT_MESH_PB_ADV_LINK_CLOSE_RETRANSMIT_COUNT enum { ADV_LINK_ACTIVE, /* Link has been opened */ @@ -178,8 +178,15 @@ static void free_segments(void) } link.tx.buf[i] = NULL; - /* Mark as canceled */ - BT_MESH_ADV(buf)->busy = 0U; + + /* Terminate active adv */ + if (BT_MESH_ADV(buf)->busy == 0U) { + bt_mesh_adv_buf_terminate(buf); + } else { + /* Mark as canceled */ + BT_MESH_ADV(buf)->busy = 0U; + } + net_buf_unref(buf); } } @@ -251,7 +258,7 @@ static struct net_buf *adv_buf_create(uint8_t retransmits) { struct net_buf *buf; - buf = bt_mesh_adv_create(BT_MESH_ADV_PROV, BT_MESH_LOCAL_ADV, + buf = bt_mesh_adv_create(BT_MESH_ADV_PROV, BT_MESH_ADV_TAG_PROV, BT_MESH_TRANSMIT(retransmits, 20), BUF_TIMEOUT); if (!buf) { diff --git a/subsys/bluetooth/mesh/priv_beacon.h b/subsys/bluetooth/mesh/priv_beacon.h new file mode 100644 index 00000000000..0cbbdafd0b1 --- /dev/null +++ b/subsys/bluetooth/mesh/priv_beacon.h @@ -0,0 +1,7 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +void bt_mesh_priv_beacon_srv_store_schedule(void); diff --git a/subsys/bluetooth/mesh/priv_beacon_srv.c b/subsys/bluetooth/mesh/priv_beacon_srv.c index a1478efff80..377703e8352 100644 --- a/subsys/bluetooth/mesh/priv_beacon_srv.c +++ b/subsys/bluetooth/mesh/priv_beacon_srv.c @@ -11,11 +11,33 @@ #include "foundation.h" #include "beacon.h" #include "cfg.h" +#include "settings.h" #define LOG_LEVEL CONFIG_BT_MESH_MODEL_LOG_LEVEL #include LOG_MODULE_REGISTER(bt_mesh_priv_beacon_srv); +static struct bt_mesh_model *priv_beacon_srv; + +/* Private Beacon configuration server model states */ +struct { + uint8_t state; + uint8_t interval; + uint8_t proxy_state; +} priv_beacon_state; + +static int priv_beacon_store(bool delete) +{ + if (!IS_ENABLED(CONFIG_BT_SETTINGS)) { + return 0; + } + + const void *data = delete ? NULL : &priv_beacon_state; + size_t len = delete ? 0 : sizeof(priv_beacon_state); + + return bt_mesh_model_data_store(priv_beacon_srv, false, "pb", data, len); +} + static int beacon_status_rsp(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx) { @@ -179,11 +201,63 @@ static int priv_beacon_srv_init(struct bt_mesh_model *mod) return -EINVAL; } + priv_beacon_srv = mod; mod->keys[0] = BT_MESH_KEY_DEV_LOCAL; return 0; } +static void priv_beacon_srv_reset(struct bt_mesh_model *model) +{ + (void)memset(&priv_beacon_state, 0, sizeof(priv_beacon_state)); + priv_beacon_store(true); +} + +#ifdef CONFIG_BT_SETTINGS +static int priv_beacon_srv_settings_set(struct bt_mesh_model *model, const char *name, + size_t len_rd, settings_read_cb read_cb, void *cb_data) +{ + int err; + + if (len_rd == 0) { + LOG_DBG("Cleared configuration state"); + return 0; + } + + err = bt_mesh_settings_set(read_cb, cb_data, &priv_beacon_state, sizeof(priv_beacon_state)); + if (err) { + LOG_ERR("Failed to set Private Beacon state"); + return err; + } + + bt_mesh_priv_beacon_set(priv_beacon_state.state); + bt_mesh_priv_beacon_update_interval_set(priv_beacon_state.interval); + bt_mesh_priv_gatt_proxy_set(priv_beacon_state.proxy_state); + return 0; +} + +static void priv_beacon_srv_pending_store(struct bt_mesh_model *model) +{ + priv_beacon_state.state = bt_mesh_priv_beacon_get(); + priv_beacon_state.interval = bt_mesh_priv_beacon_update_interval_get(); + priv_beacon_state.proxy_state = bt_mesh_priv_gatt_proxy_get(); + + priv_beacon_store(false); +} +#endif + const struct bt_mesh_model_cb bt_mesh_priv_beacon_srv_cb = { .init = priv_beacon_srv_init, + .reset = priv_beacon_srv_reset, +#ifdef CONFIG_BT_SETTINGS + .settings_set = priv_beacon_srv_settings_set, + .pending_store = priv_beacon_srv_pending_store, +#endif }; + +void bt_mesh_priv_beacon_srv_store_schedule(void) +{ + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_model_data_store_schedule(priv_beacon_srv); + } +} diff --git a/subsys/bluetooth/mesh/prov.c b/subsys/bluetooth/mesh/prov.c index f44fffde649..fbbcde58185 100644 --- a/subsys/bluetooth/mesh/prov.c +++ b/subsys/bluetooth/mesh/prov.c @@ -181,12 +181,6 @@ int bt_mesh_prov_auth(bool is_provisioner, uint8_t method, uint8_t action, uint8 uint8_t auth_size = bt_mesh_prov_auth_size_get(); int err; - if (IS_ENABLED(CONFIG_BT_MESH_OOB_AUTH_REQUIRED) && - (method == AUTH_METHOD_NO_OOB || - bt_mesh_prov_link.algorithm == BT_MESH_PROV_AUTH_CMAC_AES128_AES_CCM)) { - return -EINVAL; - } - switch (method) { case AUTH_METHOD_NO_OOB: if (action || size) { diff --git a/subsys/bluetooth/mesh/prov_device.c b/subsys/bluetooth/mesh/prov_device.c index 48d852cb1a7..6e56519eefb 100644 --- a/subsys/bluetooth/mesh/prov_device.c +++ b/subsys/bluetooth/mesh/prov_device.c @@ -94,15 +94,16 @@ static void prov_invite(const uint8_t *data) bt_mesh_prov->input_size > 0 || bt_mesh_prov->static_val; if (IS_ENABLED(CONFIG_BT_MESH_ECDH_P256_HMAC_SHA256_AES_CCM)) { - algorithm_bm |= BIT(BT_MESH_PROV_AUTH_HMAC_SHA256_AES_CCM); + WRITE_BIT(algorithm_bm, BT_MESH_PROV_AUTH_HMAC_SHA256_AES_CCM, 1); } if (IS_ENABLED(CONFIG_BT_MESH_ECDH_P256_CMAC_AES128_AES_CCM)) { - algorithm_bm |= BIT(BT_MESH_PROV_AUTH_CMAC_AES128_AES_CCM); + WRITE_BIT(algorithm_bm, BT_MESH_PROV_AUTH_CMAC_AES128_AES_CCM, 1); } if (oob_availability && IS_ENABLED(CONFIG_BT_MESH_OOB_AUTH_REQUIRED)) { oob_type |= BT_MESH_OOB_AUTH_REQUIRED; + WRITE_BIT(algorithm_bm, BT_MESH_PROV_AUTH_CMAC_AES128_AES_CCM, 0); } /* Supported algorithms */ @@ -176,16 +177,31 @@ static void prov_start(const uint8_t *data) bt_mesh_prov_link.oob_action = data[3]; bt_mesh_prov_link.oob_size = data[4]; + if (IS_ENABLED(CONFIG_BT_MESH_OOB_AUTH_REQUIRED) && + (bt_mesh_prov_link.oob_method == AUTH_METHOD_NO_OOB || + bt_mesh_prov_link.algorithm == BT_MESH_PROV_AUTH_CMAC_AES128_AES_CCM)) { + prov_fail(PROV_ERR_NVAL_FMT); + return; + } + if (bt_mesh_prov_auth(false, data[2], data[3], data[4]) < 0) { LOG_ERR("Invalid authentication method: 0x%02x; " "action: 0x%02x; size: 0x%02x", data[2], data[3], data[4]); prov_fail(PROV_ERR_NVAL_FMT); + return; } if (atomic_test_bit(bt_mesh_prov_link.flags, OOB_STATIC_KEY)) { - memcpy(bt_mesh_prov_link.auth + auth_size - bt_mesh_prov->static_val_len, - bt_mesh_prov->static_val, bt_mesh_prov->static_val_len); - memset(bt_mesh_prov_link.auth, 0, auth_size - bt_mesh_prov->static_val_len); + /* Trim the Auth if it is longer than required length */ + memcpy(bt_mesh_prov_link.auth, bt_mesh_prov->static_val, + bt_mesh_prov->static_val_len > auth_size ? auth_size + : bt_mesh_prov->static_val_len); + + /* Padd with zeros if the Auth is shorter the required length*/ + if (bt_mesh_prov->static_val_len < auth_size) { + memset(bt_mesh_prov_link.auth + bt_mesh_prov->static_val_len, 0, + auth_size - bt_mesh_prov->static_val_len); + } } } @@ -342,6 +358,13 @@ static void prov_dh_key_gen(void) } } +static void prov_dh_key_gen_handler(struct k_work *work) +{ + prov_dh_key_gen(); +} + +static K_WORK_DEFINE(dh_gen_work, prov_dh_key_gen_handler); + static void prov_pub_key(const uint8_t *data) { LOG_DBG("Remote Public Key: %s", bt_hex(data, PUB_KEY_SIZE)); @@ -369,7 +392,7 @@ static void prov_pub_key(const uint8_t *data) PDU_LEN_PUB_KEY); } - prov_dh_key_gen(); + k_work_submit(&dh_gen_work); } static void notify_input_complete(void) @@ -675,7 +698,7 @@ int bt_mesh_prov_enable(bt_mesh_prov_bearer_t bearers) return -EALREADY; } -#if IS_ENABLED(CONFIG_BT_MESH_PROV_DEVICE_LOG_LEVEL) +#if defined(CONFIG_BT_MESH_PROV_DEVICE_LOG_LEVEL) if (CONFIG_BT_MESH_PROV_DEVICE_LOG_LEVEL > 2) { struct bt_uuid_128 uuid = { .uuid = { BT_UUID_TYPE_128 } }; diff --git a/subsys/bluetooth/mesh/provisioner.c b/subsys/bluetooth/mesh/provisioner.c index 25f0a299480..a65bd25ab53 100644 --- a/subsys/bluetooth/mesh/provisioner.c +++ b/subsys/bluetooth/mesh/provisioner.c @@ -254,8 +254,7 @@ static void prov_capabilities(const uint8_t *data) return; } - if (IS_ENABLED(CONFIG_BT_MESH_OOB_AUTH_REQUIRED) && - (caps.oob_type & BT_MESH_OOB_AUTH_REQUIRED)) { + if (caps.oob_type & BT_MESH_OOB_AUTH_REQUIRED) { bool oob_availability = caps.output_size > 0 || caps.input_size > 0 || (caps.oob_type & BT_MESH_STATIC_OOB_AVAILABLE); @@ -451,6 +450,13 @@ static void prov_dh_key_gen(void) send_confirm(); } +static void prov_dh_key_gen_handler(struct k_work *work) +{ + prov_dh_key_gen(); +} + +static K_WORK_DEFINE(dh_gen_work, prov_dh_key_gen_handler); + static void prov_pub_key(const uint8_t *data) { LOG_DBG("Remote Public Key: %s", bt_hex(data, PUB_KEY_SIZE)); @@ -461,7 +467,7 @@ static void prov_pub_key(const uint8_t *data) memcpy(bt_mesh_prov_link.conf_inputs.pub_key_device, data, PUB_KEY_SIZE); bt_mesh_prov_link.bearer->clear_tx(); - prov_dh_key_gen(); + k_work_submit(&dh_gen_work); } static void notify_input_complete(void) @@ -752,18 +758,21 @@ int bt_mesh_auth_method_set_output(bt_mesh_output_action_t action, uint8_t size) int bt_mesh_auth_method_set_static(const uint8_t *static_val, uint8_t size) { - uint8_t auth_size = bt_mesh_prov_auth_size_get(); - - if (!size || !static_val || size > auth_size) { + if (!size || !static_val) { return -EINVAL; } prov_set_method(AUTH_METHOD_STATIC, 0, 0); - memcpy(bt_mesh_prov_link.auth + auth_size - size, static_val, size); - if (size < auth_size) { - (void)memset(bt_mesh_prov_link.auth, 0, auth_size - size); + /* Trim the Auth if it is longer than required length */ + memcpy(bt_mesh_prov_link.auth, static_val, + size > PROV_AUTH_MAX_LEN ? PROV_AUTH_MAX_LEN : size); + + /* Padd with zeros if the Auth is shorter the required length*/ + if (size < PROV_AUTH_MAX_LEN) { + memset(bt_mesh_prov_link.auth + size, 0, PROV_AUTH_MAX_LEN - size); } + return 0; } @@ -855,6 +864,72 @@ int bt_mesh_pb_remote_open(struct bt_mesh_rpr_cli *cli, return link_open(uuid, &pb_remote_cli, net_idx, addr, 0, &ctx, 0); } +/* Remote Provision done where client and server is on same node, skip open link + * and sending of reprovision message, just execute reprovisioning on it self. + */ +static int reprovision_local_client_server(uint16_t addr) +{ + int err; + const uint8_t *pub_key; + const uint8_t *priv_key = NULL; + + if (atomic_test_and_set_bit(bt_mesh_prov_link.flags, LINK_ACTIVE)) { + return -EBUSY; + } + + LOG_DBG("net_idx %u iv_index 0x%08x, addr 0x%04x", + prov_device.node->net_idx, bt_mesh_cdb.iv_index, addr); + + atomic_set_bit(bt_mesh_prov_link.flags, REPROVISION); + atomic_set_bit(bt_mesh_prov_link.flags, PROVISIONER); + bt_mesh_prov_link.addr = addr; + bt_mesh_prov_link.bearer = &pb_remote_cli; + bt_mesh_prov_link.role = &role_provisioner; + prov_device.net_idx = prov_device.node->net_idx; + prov_device.attention_duration = 0; + + if (IS_ENABLED(CONFIG_BT_MESH_PROV_OOB_PUBLIC_KEY) && + bt_mesh_prov->public_key_be && bt_mesh_prov->private_key_be) { + LOG_DBG("Use OOB Public and Private key"); + pub_key = bt_mesh_prov->public_key_be; + priv_key = bt_mesh_prov->private_key_be; + } else { + pub_key = bt_mesh_pub_key_get(); + } + + if (!pub_key) { + LOG_ERR("No public key available"); + return -ENOEXEC; + } + + if (bt_mesh_dhkey_gen(pub_key, priv_key, bt_mesh_prov_link.dhkey)) { + LOG_ERR("Failed to generate DHKey"); + return -ENOEXEC; + } + LOG_DBG("DHkey: %s", bt_hex(bt_mesh_prov_link.dhkey, DH_KEY_SIZE)); + + err = bt_mesh_dev_key(bt_mesh_prov_link.dhkey, + bt_mesh_prov_link.prov_salt, prov_device.new_dev_key); + if (err) { + LOG_ERR("Unable to generate device key"); + return err; + } + + bt_mesh_dev_key_cand(prov_device.new_dev_key); + /* Mark the link that was never opened as closed. */ + atomic_set_bit(bt_mesh_prov_link.flags, COMPLETE); + bt_mesh_reprovision(addr); + bt_mesh_dev_key_cand_activate(); + + if (bt_mesh_prov->reprovisioned) { + LOG_DBG("Application reprovisioned callback 0x%04x", bt_mesh_primary_addr()); + bt_mesh_prov->reprovisioned(bt_mesh_primary_addr()); + } + + prov_link_closed(PROV_BEARER_LINK_STATUS_SUCCESS); + return 0; +} + int bt_mesh_pb_remote_open_node(struct bt_mesh_rpr_cli *cli, struct bt_mesh_rpr_node *srv, uint16_t addr, bool composition_change) @@ -875,6 +950,11 @@ int bt_mesh_pb_remote_open_node(struct bt_mesh_rpr_cli *cli, return -ENOENT; } + /* Check if server is on same device as client */ + if (IS_ENABLED(CONFIG_BT_MESH_RPR_SRV) && bt_mesh_has_addr(srv->addr)) { + return reprovision_local_client_server(addr); + } + return link_open(NULL, &pb_remote_cli, prov_device.node->net_idx, addr, 0, &ctx, 0); } diff --git a/subsys/bluetooth/mesh/proxy_srv.c b/subsys/bluetooth/mesh/proxy_srv.c index 62c46582fa0..12775b93651 100644 --- a/subsys/bluetooth/mesh/proxy_srv.c +++ b/subsys/bluetooth/mesh/proxy_srv.c @@ -698,9 +698,11 @@ static void gatt_proxy_solicited(struct bt_mesh_subnet *sub) if (sub->priv_net_id_sent > 0) { timeout = sub->priv_net_id_sent + MSEC_PER_SEC * bt_mesh_od_priv_proxy_get(); + remaining = MIN(timeout - now, INT32_MAX); + } else { + remaining = MSEC_PER_SEC * bt_mesh_od_priv_proxy_get(); } - remaining = MIN(timeout - now, INT32_MAX); if ((timeout > 0 && now > timeout) || (remaining / MSEC_PER_SEC < 1)) { LOG_DBG("Advertising Private Network ID timed out " "after solicitation"); diff --git a/subsys/bluetooth/mesh/rpr_srv.c b/subsys/bluetooth/mesh/rpr_srv.c index b38bec4cd40..8fb28e56472 100644 --- a/subsys/bluetooth/mesh/rpr_srv.c +++ b/subsys/bluetooth/mesh/rpr_srv.c @@ -267,13 +267,15 @@ static void scan_ext_report_send(void) bt_mesh_model_msg_init(&buf, RPR_OP_EXTENDED_SCAN_REPORT); net_buf_simple_add_u8(&buf, BT_MESH_RPR_SUCCESS); net_buf_simple_add_mem(&buf, srv.scan.dev->uuid, 16); - if (!(srv.scan.dev->flags & BT_MESH_RPR_UNPROV_FOUND)) { + + if (srv.scan.dev->flags & BT_MESH_RPR_UNPROV_FOUND) { + net_buf_simple_add_le16(&buf, srv.scan.dev->oob); + } else { LOG_DBG("not found"); goto send; } if (srv.scan.dev->flags & BT_MESH_RPR_UNPROV_EXT_ADV_RXD) { - net_buf_simple_add_le16(&buf, srv.scan.dev->oob); net_buf_simple_add_mem(&buf, srv.scan.adv_data->data, srv.scan.adv_data->len); LOG_DBG("adv data: %s", @@ -1335,7 +1337,6 @@ static void rpr_srv_reset(struct bt_mesh_model *mod) atomic_clear(srv.flags); srv.link.dev = NULL; srv.scan.dev = NULL; - srv.mod = NULL; } const struct bt_mesh_model_cb _bt_mesh_rpr_srv_cb = { diff --git a/subsys/bluetooth/mesh/shell/cfg.c b/subsys/bluetooth/mesh/shell/cfg.c index d47f04bcc00..c4a62a996e3 100644 --- a/subsys/bluetooth/mesh/shell/cfg.c +++ b/subsys/bluetooth/mesh/shell/cfg.c @@ -98,9 +98,10 @@ static int cmd_get_comp(const struct shell *sh, size_t argc, char *argv[]) return 0; } - if (page != 0 && page != 1 && page != 128 && page != 129) { - shell_print(sh, "Got page %d. No parser available.", - page); + if (page != 0 && page != 128 && + ((page != 1 && page != 129) || !IS_ENABLED(CONFIG_BT_MESH_COMP_PAGE_1)) && + ((page != 2 && page != 130) || !IS_ENABLED(CONFIG_BT_MESH_COMP_PAGE_2))) { + shell_print(sh, "Got page %d. No parser available.", page); return 0; } @@ -254,6 +255,41 @@ static int cmd_get_comp(const struct shell *sh, size_t argc, char *argv[]) } } + if (IS_ENABLED(CONFIG_BT_MESH_COMP_PAGE_2) && (page == 2 || page == 130)) { + /* size of 32 is chosen arbitrary, as sufficient for testing purposes */ + NET_BUF_SIMPLE_DEFINE(p2_elem_offset_buf, 32); + NET_BUF_SIMPLE_DEFINE(p2_data_buf, 32); + struct bt_mesh_comp_p2_record p2_elem = { + .elem_buf = &p2_elem_offset_buf, + .data_buf = &p2_data_buf + }; + + if (!buf.len) { + shell_error(sh, "Composition data empty"); + return 0; + } + shell_print(sh, "Got Composition Data for 0x%04x, page: %d:", + bt_mesh_shell_target_ctx.dst, page); + + while (bt_mesh_comp_p2_record_pull(&buf, &p2_elem)) { + + shell_print(sh, "\tMesh Profile id: %04x ", p2_elem.id); + shell_print(sh, "\t\tVersion: %d.%d.%d ", p2_elem.version.x, + p2_elem.version.y, p2_elem.version.z); + shell_print(sh, "\t\tElement offsets:"); + + while (p2_elem.elem_buf->len) { + shell_print(sh, "\t\t\t%d ", + net_buf_simple_pull_u8(p2_elem.elem_buf)); + } + + if (p2_elem.data_buf->len) { + shell_print(sh, "\t\t%d bytes of additional data is available", + p2_elem.data_buf->len); + } + } + } + if (buf.len) { shell_print(sh, "\t\t...truncated data!"); } diff --git a/subsys/bluetooth/mesh/shell/dfd.c b/subsys/bluetooth/mesh/shell/dfd.c index 94d656f3689..0415929dfec 100644 --- a/subsys/bluetooth/mesh/shell/dfd.c +++ b/subsys/bluetooth/mesh/shell/dfd.c @@ -29,7 +29,7 @@ static void print_dfd_status(const struct shell *sh, struct bt_mesh_dfd_srv *srv srv->phase); if (srv->phase != BT_MESH_DFD_PHASE_IDLE && srv->dfu.xfer.slot) { - shell_fprintf(sh, SHELL_NORMAL, ", \"group\": 0x%04x, \"app_idx\": %d, " + shell_fprintf(sh, SHELL_NORMAL, ", \"group\": %d, \"app_idx\": %d, " "\"ttl\": %d, \"timeout_base\": %d, \"xfer_mode\": %d, " "\"apply\": %d, \"slot_idx\": %d", srv->inputs.group, srv->inputs.app_idx, srv->inputs.ttl, srv->inputs.timeout_base, @@ -43,7 +43,7 @@ static void print_fw_status(const struct shell *sh, enum bt_mesh_dfd_status stat uint16_t idx, const uint8_t *fwid, size_t fwid_len) { shell_fprintf(sh, SHELL_NORMAL, "{ \"status\": %d, \"slot_cnt\": %d, \"idx\": %d", - status, bt_mesh_dfu_slot_foreach(NULL, NULL), idx); + status, bt_mesh_dfu_slot_count(), idx); if (fwid) { shell_fprintf(sh, SHELL_NORMAL, ", \"fwid\": \""); for (size_t i = 0; i < fwid_len; i++) { @@ -165,7 +165,7 @@ static int cmd_dfd_receivers_get(const struct shell *sh, size_t argc, char *argv for (int i = 0; i < cnt; i++) { const struct bt_mesh_dfu_target *t = &dfd_srv->targets[i + first]; - shell_print(sh, "\t\t\"%d\": { \"blob_addr\": 0x%04x, \"phase\": %d, " + shell_print(sh, "\t\t\"%d\": { \"blob_addr\": %d, \"phase\": %d, " "\"status\": %d, \"blob_status\": %d, \"progress\": %d, " "\"img_idx\": %d }%s", i + first, t->blob.addr, t->phase, t->status, t->blob.status, progress, t->img_idx, (i == cnt - 1) ? "" : ","); @@ -325,10 +325,9 @@ static int cmd_dfd_fw_get(const struct shell *sh, size_t argc, char *argv[]) return -EINVAL; } - const struct bt_mesh_dfu_slot *slot; - int idx = bt_mesh_dfu_slot_get(fwid, fwid_len, &slot); + int idx = bt_mesh_dfu_slot_get(fwid, fwid_len, NULL); - if (idx >= 0 && bt_mesh_dfu_slot_is_valid(slot)) { + if (idx >= 0) { print_fw_status(sh, BT_MESH_DFD_SUCCESS, idx, fwid, fwid_len); } else { print_fw_status(sh, BT_MESH_DFD_ERR_FW_NOT_FOUND, 0xffff, fwid, fwid_len); @@ -349,7 +348,7 @@ static int cmd_dfd_fw_get_by_idx(const struct shell *sh, size_t argc, char *argv return err; } - if (slot && bt_mesh_dfu_slot_is_valid(slot)) { + if (slot) { print_fw_status(sh, BT_MESH_DFD_SUCCESS, idx, slot->fwid, slot->fwid_len); } else { print_fw_status(sh, BT_MESH_DFD_ERR_FW_NOT_FOUND, idx, NULL, 0); diff --git a/subsys/bluetooth/mesh/shell/dfu.c b/subsys/bluetooth/mesh/shell/dfu.c index 71d27b158e4..8d7fc96e014 100644 --- a/subsys/bluetooth/mesh/shell/dfu.c +++ b/subsys/bluetooth/mesh/shell/dfu.c @@ -375,13 +375,12 @@ static int cmd_dfu_metadata_encode(const struct shell *sh, size_t argc, char *ar static int cmd_dfu_slot_add(const struct shell *sh, size_t argc, char *argv[]) { - const struct bt_mesh_dfu_slot *slot; + struct bt_mesh_dfu_slot *slot; size_t size; uint8_t fwid[CONFIG_BT_MESH_DFU_FWID_MAXLEN]; size_t fwid_len = 0; uint8_t metadata[CONFIG_BT_MESH_DFU_METADATA_MAXLEN]; size_t metadata_len = 0; - const char *uri = ""; int err = 0; size = shell_strtoul(argv[1], 0, &err); @@ -390,32 +389,33 @@ static int cmd_dfu_slot_add(const struct shell *sh, size_t argc, char *argv[]) return err; } - if (argc > 2) { - fwid_len = hex2bin(argv[2], strlen(argv[2]), fwid, - sizeof(fwid)); + shell_print(sh, "Adding slot (size: %u)", size); + slot = bt_mesh_dfu_slot_reserve(); + + if (!slot) { + shell_print(sh, "Failed to reserve slot."); + return 0; } + fwid_len = hex2bin(argv[2], strlen(argv[2]), fwid, + sizeof(fwid)); + bt_mesh_dfu_slot_fwid_set(slot, fwid, fwid_len); + if (argc > 3) { metadata_len = hex2bin(argv[3], strlen(argv[3]), metadata, sizeof(metadata)); } - if (argc > 4) { - uri = argv[4]; - } + bt_mesh_dfu_slot_info_set(slot, size, metadata, metadata_len); - shell_print(sh, "Adding slot (size: %u)", size); - - slot = bt_mesh_dfu_slot_add(size, fwid, fwid_len, metadata, - metadata_len, uri, strlen(uri)); - if (!slot) { - shell_print(sh, "Failed."); - return 0; + err = bt_mesh_dfu_slot_commit(slot); + if (err) { + shell_print(sh, "Failed to commit slot: %d", err); + bt_mesh_dfu_slot_release(slot); + return err; } - bt_mesh_dfu_slot_valid_set(slot, true); - - shell_print(sh, "Slot added. ID: %u", bt_mesh_dfu_slot_idx_get(slot)); + shell_print(sh, "Slot added. Index: %u", bt_mesh_dfu_slot_img_idx_get(slot)); return 0; } @@ -451,14 +451,7 @@ static int cmd_dfu_slot_del(const struct shell *sh, size_t argc, char *argv[]) static int cmd_dfu_slot_del_all(const struct shell *sh, size_t argc, char *argv[]) { - int err; - - err = bt_mesh_dfu_slot_del_all(); - if (err) { - shell_print(sh, "Failed deleting all slots (err: %d)", err); - return 0; - } - + bt_mesh_dfu_slot_del_all(); shell_print(sh, "All slots deleted."); return 0; } @@ -468,7 +461,6 @@ static void slot_info_print(const struct shell *sh, const struct bt_mesh_dfu_slo { char fwid[2 * CONFIG_BT_MESH_DFU_FWID_MAXLEN + 1]; char metadata[2 * CONFIG_BT_MESH_DFU_METADATA_MAXLEN + 1]; - char uri[CONFIG_BT_MESH_DFU_URI_MAXLEN + 1]; size_t len; len = bin2hex(slot->fwid, slot->fwid_len, fwid, sizeof(fwid)); @@ -476,8 +468,6 @@ static void slot_info_print(const struct shell *sh, const struct bt_mesh_dfu_slo len = bin2hex(slot->metadata, slot->metadata_len, metadata, sizeof(metadata)); metadata[len] = '\0'; - memcpy(uri, slot->uri, slot->uri_len); - uri[slot->uri_len] = '\0'; if (idx != NULL) { shell_print(sh, "Slot %u:", *idx); @@ -487,7 +477,6 @@ static void slot_info_print(const struct shell *sh, const struct bt_mesh_dfu_slo shell_print(sh, "\tSize: %u bytes", slot->size); shell_print(sh, "\tFWID: %s", fwid); shell_print(sh, "\tMetadata: %s", metadata); - shell_print(sh, "\tURI: %s", uri); } static int cmd_dfu_slot_get(const struct shell *sh, size_t argc, char *argv[]) @@ -970,8 +959,8 @@ SHELL_STATIC_SUBCMD_SET_CREATE( SHELL_STATIC_SUBCMD_SET_CREATE( dfu_slot_cmds, SHELL_CMD_ARG(add, NULL, - " [ [ []]]", - cmd_dfu_slot_add, 2, 3), + " []", + cmd_dfu_slot_add, 3, 1), SHELL_CMD_ARG(del, NULL, "", cmd_dfu_slot_del, 2, 0), SHELL_CMD_ARG(del-all, NULL, NULL, cmd_dfu_slot_del_all, 1, 0), SHELL_CMD_ARG(get, NULL, "", cmd_dfu_slot_get, 2, 0), diff --git a/subsys/bluetooth/mesh/shell/shell.c b/subsys/bluetooth/mesh/shell/shell.c index 243166441fe..c5592db02cf 100644 --- a/subsys/bluetooth/mesh/shell/shell.c +++ b/subsys/bluetooth/mesh/shell/shell.c @@ -611,7 +611,7 @@ static void link_close(bt_mesh_prov_bearer_t bearer) shell_print_ctx("Provisioning link closed on %s", bearer2str(bearer)); } -static uint8_t static_val[16]; +static uint8_t static_val[32]; struct bt_mesh_prov bt_mesh_shell_prov = { .uuid = dev_uuid, @@ -645,7 +645,7 @@ static int cmd_static_oob(const struct shell *sh, size_t argc, char *argv[]) bt_mesh_shell_prov.static_val_len = 0U; } else { bt_mesh_shell_prov.static_val_len = hex2bin(argv[1], strlen(argv[1]), - static_val, 16); + static_val, 32); if (bt_mesh_shell_prov.static_val_len) { bt_mesh_shell_prov.static_val = static_val; } else { @@ -886,7 +886,7 @@ static int cmd_auth_method_set_output(const struct shell *sh, size_t argc, char static int cmd_auth_method_set_static(const struct shell *sh, size_t argc, char *argv[]) { size_t len; - uint8_t static_oob_auth[16]; + uint8_t static_oob_auth[32]; int err = 0; len = hex2bin(argv[1], strlen(argv[1]), static_oob_auth, sizeof(static_oob_auth)); diff --git a/subsys/bluetooth/mesh/solicitation.c b/subsys/bluetooth/mesh/solicitation.c index 3f639fbf0d6..e2100fa42db 100644 --- a/subsys/bluetooth/mesh/solicitation.c +++ b/subsys/bluetooth/mesh/solicitation.c @@ -153,12 +153,12 @@ static bool sol_pdu_decrypt(struct bt_mesh_subnet *sub, void *data) net_buf_simple_init(out, 0); net_buf_simple_add_mem(out, in->data, in->len); - err = bt_mesh_net_obfuscate(out->data, 0, sub->keys[i].msg.privacy); + err = bt_mesh_net_obfuscate(out->data, 0, &sub->keys[i].msg.privacy); if (err) { LOG_DBG("obfuscation err %d", err); continue; } - err = bt_mesh_net_decrypt(sub->keys[i].msg.enc, out, + err = bt_mesh_net_decrypt(&sub->keys[i].msg.enc, out, 0, BT_MESH_NONCE_SOLICITATION); if (!err) { LOG_DBG("Decrypted PDU %s", bt_hex(out->data, out->len)); @@ -190,13 +190,14 @@ void bt_mesh_sol_recv(struct net_buf_simple *buf, uint8_t uuid_list_len) if (bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED || bt_mesh_priv_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED || bt_mesh_od_priv_proxy_get() == 0) { + LOG_DBG("Not soliciting"); return; } /* Get rid of ad_type that was checked in bt_mesh_scan_cb */ type = net_buf_simple_pull_u8(buf); if (type != BT_DATA_UUID16_SOME && type != BT_DATA_UUID16_ALL) { - LOG_ERR("Invalid type 0x%x, expected 0x%x or 0x%x", + LOG_DBG("Invalid type 0x%x, expected 0x%x or 0x%x", type, BT_DATA_UUID16_SOME, BT_DATA_UUID16_ALL); return; } @@ -215,6 +216,7 @@ void bt_mesh_sol_recv(struct net_buf_simple *buf, uint8_t uuid_list_len) } if (!sol_uuid_found) { + LOG_DBG("No solicitation UUID found"); return; } @@ -230,6 +232,7 @@ void bt_mesh_sol_recv(struct net_buf_simple *buf, uint8_t uuid_list_len) } if (buf->len <= reported_len - 3) { + LOG_DBG("Invalid length (%u) Solicitation PDU", buf->len); return; } @@ -237,12 +240,13 @@ void bt_mesh_sol_recv(struct net_buf_simple *buf, uint8_t uuid_list_len) } if (!svc_data_found) { + LOG_DBG("No solicitation service data found"); return; } type = net_buf_simple_pull_u8(buf); if (type != 0) { - LOG_ERR("Invalid type %d, expected 0x00", type); + LOG_DBG("Invalid type %d, expected 0x00", type); return; } @@ -304,7 +308,7 @@ static int sol_pdu_create(struct bt_mesh_subnet *sub, struct net_buf_simple *pdu /* DST = 0x0000 */ net_buf_simple_add_le16(pdu, 0x0000); - err = bt_mesh_net_encrypt(sub->keys[SUBNET_KEY_TX_IDX(sub)].msg.enc, + err = bt_mesh_net_encrypt(&sub->keys[SUBNET_KEY_TX_IDX(sub)].msg.enc, pdu, 0, BT_MESH_NONCE_SOLICITATION); if (err) { @@ -313,7 +317,7 @@ static int sol_pdu_create(struct bt_mesh_subnet *sub, struct net_buf_simple *pdu } err = bt_mesh_net_obfuscate(pdu->data, 0, - sub->keys[SUBNET_KEY_TX_IDX(sub)].msg.privacy); + &sub->keys[SUBNET_KEY_TX_IDX(sub)].msg.privacy); if (err) { LOG_ERR("Obfuscation failed, err=%d", err); return err; diff --git a/subsys/bluetooth/mesh/statistic.c b/subsys/bluetooth/mesh/statistic.c index 5b1ffe7e0f5..21c451bee73 100644 --- a/subsys/bluetooth/mesh/statistic.c +++ b/subsys/bluetooth/mesh/statistic.c @@ -24,22 +24,22 @@ void bt_mesh_stat_reset(void) void bt_mesh_stat_planned_count(struct bt_mesh_adv *adv) { - if (adv->tag & BT_MESH_LOCAL_ADV) { + if (adv->tag == BT_MESH_ADV_TAG_LOCAL) { stat.tx_local_planned++; - } else if (adv->tag & BT_MESH_RELAY_ADV) { + } else if (adv->tag == BT_MESH_ADV_TAG_RELAY) { stat.tx_adv_relay_planned++; - } else if (adv->tag & BT_MESH_FRIEND_ADV) { + } else if (adv->tag == BT_MESH_ADV_TAG_FRIEND) { stat.tx_friend_planned++; } } void bt_mesh_stat_succeeded_count(struct bt_mesh_adv *adv) { - if (adv->tag & BT_MESH_LOCAL_ADV) { + if (adv->tag == BT_MESH_ADV_TAG_LOCAL) { stat.tx_local_succeeded++; - } else if (adv->tag & BT_MESH_RELAY_ADV) { + } else if (adv->tag == BT_MESH_ADV_TAG_RELAY) { stat.tx_adv_relay_succeeded++; - } else if (adv->tag & BT_MESH_FRIEND_ADV) { + } else if (adv->tag == BT_MESH_ADV_TAG_FRIEND) { stat.tx_friend_succeeded++; } } diff --git a/subsys/bluetooth/mesh/transport.c b/subsys/bluetooth/mesh/transport.c index 0343993e348..f4c49aa6856 100644 --- a/subsys/bluetooth/mesh/transport.c +++ b/subsys/bluetooth/mesh/transport.c @@ -124,7 +124,7 @@ static int send_unseg(struct bt_mesh_net_tx *tx, struct net_buf_simple *sdu, { struct net_buf *buf; - buf = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_LOCAL_ADV, + buf = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_ADV_TAG_LOCAL, tx->xmit, BUF_TIMEOUT); if (!buf) { LOG_ERR("Out of network buffers"); @@ -414,7 +414,7 @@ static void seg_tx_send_unacked(struct seg_tx *tx) continue; } - seg = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_LOCAL_ADV, + seg = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_ADV_TAG_LOCAL, tx->xmit, BUF_TIMEOUT); if (!seg) { LOG_DBG("Allocating segment failed"); diff --git a/subsys/bluetooth/mesh/transport_legacy.c b/subsys/bluetooth/mesh/transport_legacy.c index 475a0429f75..23e103b4370 100644 --- a/subsys/bluetooth/mesh/transport_legacy.c +++ b/subsys/bluetooth/mesh/transport_legacy.c @@ -131,7 +131,7 @@ static int send_unseg(struct bt_mesh_net_tx *tx, struct net_buf_simple *sdu, { struct net_buf *buf; - buf = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_LOCAL_ADV, + buf = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_ADV_TAG_LOCAL, tx->xmit, BUF_TIMEOUT); if (!buf) { LOG_ERR("Out of network buffers"); @@ -401,7 +401,7 @@ static void seg_tx_send_unacked(struct seg_tx *tx) continue; } - seg = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_LOCAL_ADV, + seg = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_ADV_TAG_LOCAL, tx->xmit, BUF_TIMEOUT); if (!seg) { LOG_DBG("Allocating segment failed"); diff --git a/subsys/bluetooth/shell/bt.c b/subsys/bluetooth/shell/bt.c index d1d1238baf6..890eafe2210 100644 --- a/subsys/bluetooth/shell/bt.c +++ b/subsys/bluetooth/shell/bt.c @@ -97,10 +97,14 @@ static const char *phy2str(uint8_t phy) { switch (phy) { case 0: return "No packets"; - case BT_GAP_LE_PHY_1M: return "LE 1M"; - case BT_GAP_LE_PHY_2M: return "LE 2M"; - case BT_GAP_LE_PHY_CODED: return "LE Coded"; - default: return "Unknown"; + case BT_GAP_LE_PHY_1M: + return "LE 1M"; + case BT_GAP_LE_PHY_2M: + return "LE 2M"; + case BT_GAP_LE_PHY_CODED: + return "LE Coded"; + default: + return "Unknown"; } } #endif @@ -121,6 +125,67 @@ static void print_le_addr(const char *desc, const bt_addr_le_t *addr) } #endif /* CONFIG_BT_CONN || (CONFIG_BT_BROADCASTER && CONFIG_BT_EXT_ADV) */ +#if defined(CONFIG_BT_TRANSMIT_POWER_CONTROL) +static const char *tx_power_flag2str(int8_t flag) +{ + switch (flag) { + case 0: + return "Neither Max nor Min Tx Power"; + case 1: + return "Tx Power Level is at minimum"; + case 2: + return "Tx Power Level is at maximum"; + /* Current Tx Power Level is the only available one*/ + case 3: + return "Tx Power Level is at minimum & maximum."; + default: + return "Unknown"; + } +} + +static const char *tx_power_report_reason2str(uint8_t reason) +{ + switch (reason) { + case BT_HCI_LE_TX_POWER_REPORT_REASON_LOCAL_CHANGED: + return "Local Tx Power changed"; + case BT_HCI_LE_TX_POWER_REPORT_REASON_REMOTE_CHANGED: + return "Remote Tx Power changed"; + case BT_HCI_LE_TX_POWER_REPORT_REASON_READ_REMOTE_COMPLETED: + return "Completed to read remote Tx Power"; + default: + return "Unknown"; + } +} + +static const char *tx_pwr_ctrl_phy2str(enum bt_conn_le_tx_power_phy phy) +{ + switch (phy) { + case BT_CONN_LE_TX_POWER_PHY_NONE: + return "None"; + case BT_CONN_LE_TX_POWER_PHY_1M: + return "LE 1M"; + case BT_CONN_LE_TX_POWER_PHY_2M: + return "LE 2M"; + case BT_CONN_LE_TX_POWER_PHY_CODED_S8: + return "LE Coded S8"; + case BT_CONN_LE_TX_POWER_PHY_CODED_S2: + return "LE Coded S2"; + default: + return "Unknown"; + } +} + +static const char *enabled2str(bool enabled) +{ + if (enabled) { + return "Enabled"; + } else { + return "Disabled"; + } +} + +#endif /* CONFIG_BT_TRANSMIT_POWER_CONTROL */ + #if defined(CONFIG_BT_CENTRAL) static int cmd_scan_off(const struct shell *sh); static int cmd_connect_le(const struct shell *sh, size_t argc, char *argv[]); @@ -807,6 +872,19 @@ void le_phy_updated(struct bt_conn *conn, } #endif +#if defined(CONFIG_BT_TRANSMIT_POWER_CONTROL) +void tx_power_report(struct bt_conn *conn, + const struct bt_conn_le_tx_power_report *report) +{ + shell_print(ctx_shell, "Tx Power Report: Reason: %s, PHY: %s, Tx Power Level: %d", + tx_power_report_reason2str(report->reason), tx_pwr_ctrl_phy2str(report->phy), + report->tx_power_level); + shell_print(ctx_shell, "Tx Power Level Flag Info: %s, Delta: %d", + tx_power_flag2str(report->tx_power_level_flag), report->delta); +} +#endif + + static struct bt_conn_cb conn_callbacks = { .connected = connected, .disconnected = disconnected, @@ -827,6 +905,9 @@ static struct bt_conn_cb conn_callbacks = { #if defined(CONFIG_BT_USER_PHY_UPDATE) .le_phy_updated = le_phy_updated, #endif +#if defined(CONFIG_BT_TRANSMIT_POWER_CONTROL) + .tx_power_report = tx_power_report, +#endif }; #endif /* CONFIG_BT_CONN */ @@ -2604,6 +2685,102 @@ static int cmd_per_adv_set_info_transfer(const struct shell *sh, size_t argc, } #endif /* CONFIG_BT_PER_ADV_SYNC_TRANSFER_SENDER && CONFIG_BT_PER_ADV */ +#if defined(CONFIG_BT_TRANSMIT_POWER_CONTROL) +static int cmd_read_remote_tx_power(const struct shell *sh, size_t argc, char *argv[]) +{ + if (argc < 3) { + int err = 0; + enum bt_conn_le_tx_power_phy phy = strtoul(argv[1], NULL, 16); + + err = bt_conn_le_get_remote_tx_power_level(default_conn, phy); + + if (!err) { + shell_print(sh, "Read Remote TX Power for PHY %s", + tx_pwr_ctrl_phy2str(phy)); + } else { + shell_print(sh, "error %d", err); + } + } else { + shell_help(sh); + return SHELL_CMD_HELP_PRINTED; + } + return 0; +} + +static int cmd_read_local_tx_power(const struct shell *sh, size_t argc, char *argv[]) +{ + int err = 0; + + if (argc < 3) { + struct bt_conn_le_tx_power tx_power_level; + + tx_power_level.phy = strtoul(argv[1], NULL, 16); + + int8_t unachievable_current_level = -100; + /* Arbitrary, these are output parameters.*/ + tx_power_level.current_level = unachievable_current_level; + tx_power_level.max_level = 6; + + if (default_conn == NULL) { + shell_error(sh, "Conn handle error, at least one connection is required."); + return -ENOEXEC; + } + err = bt_conn_le_get_tx_power_level(default_conn, &tx_power_level); + if (err) { + shell_print(sh, "Commad returned error error %d", err); + return err; + } + if (tx_power_level.current_level == unachievable_current_level) { + shell_print(sh, "We received no current tx power level."); + return -EIO; + } + shell_print(sh, "Read local TX Power: current level: %d, PHY: %s, Max Level: %d", + tx_power_level.current_level, + tx_pwr_ctrl_phy2str((enum bt_conn_le_tx_power_phy)tx_power_level.phy), + tx_power_level.max_level); + } else { + shell_help(sh); + return SHELL_CMD_HELP_PRINTED; + } + + return err; +} + +static int cmd_set_power_report_enable(const struct shell *sh, size_t argc, char *argv[]) +{ + if (argc < 4) { + int err = 0; + bool local_enable = 0; + bool remote_enable = 0; + + if (*argv[1] == '1') { + local_enable = 1; + } + if (*argv[2] == '1') { + remote_enable = 1; + } + if (default_conn == NULL) { + shell_error(sh, "Conn handle error, at least one connection is required."); + return -ENOEXEC; + } + err = bt_conn_le_set_tx_power_report_enable(default_conn, local_enable, + remote_enable); + if (!err) { + shell_print(sh, "Tx Power Report: local: %s, remote: %s", + enabled2str(local_enable), enabled2str(remote_enable)); + } else { + shell_print(sh, "error %d", err); + } + } else { + shell_help(sh); + return SHELL_CMD_HELP_PRINTED; + } + return 0; +} + +#endif + + #if defined(CONFIG_BT_CONN) #if defined(CONFIG_BT_CENTRAL) static int cmd_connect_le(const struct shell *sh, size_t argc, char *argv[]) @@ -4011,6 +4188,11 @@ SHELL_STATIC_SUBCMD_SET_CREATE(bt_cmds, cmd_default_handler), SHELL_CMD_ARG(scan-verbose-output, NULL, "", cmd_scan_verbose_output, 2, 0), #endif /* CONFIG_BT_OBSERVER */ +#if defined(CONFIG_BT_TRANSMIT_POWER_CONTROL) + SHELL_CMD_ARG(read-remote-tx-power, NULL, HELP_NONE, cmd_read_remote_tx_power, 2, 0), + SHELL_CMD_ARG(read-local-tx-power, NULL, HELP_NONE, cmd_read_local_tx_power, 2, 0), + SHELL_CMD_ARG(set-power-report-enable, NULL, HELP_NONE, cmd_set_power_report_enable, 3, 0), +#endif #if defined(CONFIG_BT_BROADCASTER) SHELL_CMD_ARG(advertise, NULL, " [mode: discov, non_discov] " diff --git a/subsys/dfu/Kconfig b/subsys/dfu/Kconfig index f998b2ef9f2..121fa3964f6 100644 --- a/subsys/dfu/Kconfig +++ b/subsys/dfu/Kconfig @@ -89,7 +89,7 @@ if !MCUBOOT config UPDATEABLE_IMAGE_NUMBER int "Number of updateable images" default 1 - range 1 2 + range 1 3 help If value is set to 2 or greater then, this enables support needed when application is combined with MCUboot multi-image boot. diff --git a/subsys/fs/littlefs_fs.c b/subsys/fs/littlefs_fs.c index 3058f402d73..c4c75bb48c4 100644 --- a/subsys/fs/littlefs_fs.c +++ b/subsys/fs/littlefs_fs.c @@ -1054,7 +1054,12 @@ struct fs_mount_t FS_FSTAB_ENTRY(DT_DRV_INST(inst)) = { \ .type = FS_LITTLEFS, \ .mnt_point = DT_INST_PROP(inst, mount_point), \ .fs_data = &fs_data_##inst, \ - .storage_dev = (void *)DT_FIXED_PARTITION_ID(FS_PARTITION(inst)), \ + .storage_dev = (void *) \ + COND_CODE_1(USE_PARTITION_MANAGER, \ + (COND_CODE_1(FIXED_PARTITION_EXISTS(littlefs_storage), \ + (FIXED_PARTITION_ID(littlefs_storage)), \ + (FIXED_PARTITION_ID(storage)))), \ + (DT_FIXED_PARTITION_ID(FS_PARTITION(inst)))), \ .flags = FSTAB_ENTRY_DT_MOUNT_FLAGS(DT_DRV_INST(inst)), \ }; diff --git a/subsys/fs/nvs/Kconfig b/subsys/fs/nvs/Kconfig index de4f282ee98..df0329a7c41 100644 --- a/subsys/fs/nvs/Kconfig +++ b/subsys/fs/nvs/Kconfig @@ -27,6 +27,15 @@ config NVS_LOOKUP_CACHE_SIZE Number of entries in Non-volatile Storage lookup cache. It is recommended that it be a power of 2. +config NVS_LOOKUP_CACHE_FOR_SETTINGS + bool "Non-volatile Storage lookup cache optimized for settings" + depends on NVS_LOOKUP_CACHE + help + Use the lookup cache hash function that results in the least number of + collissions and, in turn, the best NVS performance provided that the NVS + is used as the settings backend only. This option should NOT be enabled + if the NVS is also written to directly, outside the settings layer. + module = NVS module-str = nvs source "subsys/logging/Kconfig.template.log_config" diff --git a/subsys/fs/nvs/nvs.c b/subsys/fs/nvs/nvs.c index 8c4575700a8..14a4db5e347 100644 --- a/subsys/fs/nvs/nvs.c +++ b/subsys/fs/nvs/nvs.c @@ -13,6 +13,11 @@ #include #include "nvs_priv.h" +#ifdef CONFIG_NVS_LOOKUP_CACHE_FOR_SETTINGS +#include +#include +#endif + #include LOG_MODULE_REGISTER(fs_nvs, CONFIG_NVS_LOG_LEVEL); @@ -21,6 +26,45 @@ static int nvs_ate_valid(struct nvs_fs *fs, const struct nvs_ate *entry); #ifdef CONFIG_NVS_LOOKUP_CACHE +#ifdef CONFIG_NVS_LOOKUP_CACHE_FOR_SETTINGS + +static inline size_t nvs_lookup_cache_pos(uint16_t id) +{ + /* + * 1. The NVS settings backend uses up to (NVS_NAME_ID_OFFSET - 1) NVS IDs to + store keys and equal number of NVS IDs to store values. + * 2. For each key-value pair, the value is stored at NVS ID greater by exactly + * NVS_NAME_ID_OFFSET than NVS ID that holds the key. + * 3. The backend tries to minimize the range of NVS IDs used to store keys. + * That is, NVS IDs are allocated sequentially, and freed NVS IDs are reused + * before allocating new ones. + * + * Therefore, to assure the least number of collisions in the lookup cache, + * the least significant bit of the hash indicates whether the given NVS ID + * represents a key or a value, and remaining bits of the hash are set to + * the ordinal number of the key-value pair. Consequently, the hash function + * provides the following mapping: + * + * 1st settings key => hash 0 + * 1st settings value => hash 1 + * 2nd settings key => hash 2 + * 2nd settings value => hash 3 + * ... + */ + BUILD_ASSERT(IS_POWER_OF_TWO(NVS_NAMECNT_ID), "NVS_NAMECNT_ID is not power of 2"); + BUILD_ASSERT(IS_POWER_OF_TWO(NVS_NAME_ID_OFFSET), "NVS_NAME_ID_OFFSET is not power of 2"); + + uint16_t key_value_bit; + uint16_t key_value_ord; + + key_value_bit = (id >> LOG2(NVS_NAME_ID_OFFSET)) & 1; + key_value_ord = id & (NVS_NAME_ID_OFFSET - 1); + + return ((key_value_ord << 1) | key_value_bit) % CONFIG_NVS_LOOKUP_CACHE_SIZE; +} + +#else /* CONFIG_NVS_LOOKUP_CACHE_FOR_SETTINGS */ + static inline size_t nvs_lookup_cache_pos(uint16_t id) { uint16_t hash; @@ -36,6 +80,8 @@ static inline size_t nvs_lookup_cache_pos(uint16_t id) return hash % CONFIG_NVS_LOOKUP_CACHE_SIZE; } +#endif /* CONFIG_NVS_LOOKUP_CACHE_FOR_SETTINGS */ + static int nvs_lookup_cache_rebuild(struct nvs_fs *fs) { int rc; diff --git a/subsys/ipc/ipc_service/backends/ipc_rpmsg_static_vrings.c b/subsys/ipc/ipc_service/backends/ipc_rpmsg_static_vrings.c index 41f0a77ea22..5d04d029e8c 100644 --- a/subsys/ipc/ipc_service/backends/ipc_rpmsg_static_vrings.c +++ b/subsys/ipc/ipc_service/backends/ipc_rpmsg_static_vrings.c @@ -774,8 +774,8 @@ static int backend_init(const struct device *instance) return 0; } -#define DEFINE_BACKEND_DEVICE(i) \ - static struct backend_config_t backend_config_##i = { \ +#define BACKEND_CONFIG_POPULATE(i) \ + { \ .role = DT_ENUM_IDX_OR(DT_DRV_INST(i), role, ROLE_HOST), \ .shm_size = DT_REG_SIZE(DT_INST_PHANDLE(i, memory_region)), \ .shm_addr = DT_REG_ADDR(DT_INST_PHANDLE(i, memory_region)), \ @@ -790,8 +790,10 @@ static int backend_init(const struct device *instance) .buffer_size = DT_INST_PROP_OR(i, zephyr_buffer_size, \ RPMSG_BUFFER_SIZE), \ .id = i, \ - }; \ - \ + } + +#define BACKEND_DEVICE_DEFINE(i) \ + static struct backend_config_t backend_config_##i = BACKEND_CONFIG_POPULATE(i); \ static struct backend_data_t backend_data_##i; \ \ DEVICE_DT_INST_DEFINE(i, \ @@ -803,20 +805,23 @@ static int backend_init(const struct device *instance) CONFIG_IPC_SERVICE_REG_BACKEND_PRIORITY, \ &backend_ops); -DT_INST_FOREACH_STATUS_OKAY(DEFINE_BACKEND_DEVICE) +DT_INST_FOREACH_STATUS_OKAY(BACKEND_DEVICE_DEFINE) -#define BACKEND_CONFIG_INIT(n) &backend_config_##n, +#define BACKEND_CONFIG_DEFINE(i) BACKEND_CONFIG_POPULATE(i), #if defined(CONFIG_IPC_SERVICE_BACKEND_RPMSG_SHMEM_RESET) static int shared_memory_prepare(void) { - static const struct backend_config_t *config[] = { - DT_INST_FOREACH_STATUS_OKAY(BACKEND_CONFIG_INIT) + const struct backend_config_t *backend_config; + const struct backend_config_t backend_configs[] = { + DT_INST_FOREACH_STATUS_OKAY(BACKEND_CONFIG_DEFINE) }; - for (int i = 0; i < DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT); i++) { - if (config[i]->role == ROLE_HOST) { - memset((void *) config[i]->shm_addr, 0, VDEV_STATUS_SIZE); + for (backend_config = backend_configs; + backend_config < backend_configs + ARRAY_SIZE(backend_configs); + backend_config++) { + if (backend_config->role == ROLE_HOST) { + memset((void *) backend_config->shm_addr, 0, VDEV_STATUS_SIZE); } } diff --git a/subsys/ipc/rpmsg_service/rpmsg_backend.h b/subsys/ipc/rpmsg_service/rpmsg_backend.h index a74e46b8520..9996e1d74d9 100644 --- a/subsys/ipc/rpmsg_service/rpmsg_backend.h +++ b/subsys/ipc/rpmsg_service/rpmsg_backend.h @@ -13,8 +13,35 @@ extern "C" { #endif +#if CONFIG_PARTITION_MANAGER_ENABLED + +#include "pm_config.h" + +#if defined(PM_RPMSG_NRF53_SRAM_ADDRESS) || defined(PM__RPMSG_NRF53_SRAM_ADDRESS) + +#if defined(PM_RPMSG_NRF53_SRAM_ADDRESS) +#define VDEV_START_ADDR PM_RPMSG_NRF53_SRAM_ADDRESS +#define VDEV_SIZE PM_RPMSG_NRF53_SRAM_SIZE +#else +/* The current image is a child image in a different domain than the image + * which defined the required values. To reach the values of the parent domain + * we use the 'PM__' variant of the define. + */ +#define VDEV_START_ADDR PM__RPMSG_NRF53_SRAM_ADDRESS +#define VDEV_SIZE PM__RPMSG_NRF53_SRAM_SIZE +#endif /* defined(PM_RPMSG_NRF53_SRAM_ADDRESS) */ + +#else #define VDEV_START_ADDR DT_REG_ADDR(DT_CHOSEN(zephyr_ipc_shm)) #define VDEV_SIZE DT_REG_SIZE(DT_CHOSEN(zephyr_ipc_shm)) +#endif /* defined(PM_RPMSG_NRF53_SRAM_ADDRESS) || defined(PM__RPMSG_NRF53_SRAM_ADDRESS) */ + +#else + +#define VDEV_START_ADDR DT_REG_ADDR(DT_CHOSEN(zephyr_ipc_shm)) +#define VDEV_SIZE DT_REG_SIZE(DT_CHOSEN(zephyr_ipc_shm)) + +#endif /* CONFIG_PARTITION_MANAGER_ENABLED */ #define VDEV_STATUS_ADDR VDEV_START_ADDR #define VDEV_STATUS_SIZE 0x400 diff --git a/subsys/mgmt/mcumgr/CMakeLists.txt b/subsys/mgmt/mcumgr/CMakeLists.txt index 39d4a4ca8ce..ad088eca067 100644 --- a/subsys/mgmt/mcumgr/CMakeLists.txt +++ b/subsys/mgmt/mcumgr/CMakeLists.txt @@ -16,3 +16,11 @@ add_subdirectory(transport) add_subdirectory_ifdef(CONFIG_SMP_CLIENT smp_client) zephyr_library_link_libraries(mgmt_mcumgr) + +if (CONFIG_BOOT_IMAGE_ACCESS_HOOKS) + zephyr_include_directories( + ${ZEPHYR_MCUBOOT_MODULE_DIR}/boot/bootutil/include + ${ZEPHYR_MCUBOOT_MODULE_DIR}/boot/zephyr/include + ) + zephyr_library_sources(bootutil_hooks/nrf53_hooks.c) +endif() diff --git a/subsys/mgmt/mcumgr/Kconfig b/subsys/mgmt/mcumgr/Kconfig index 8af4ffe2738..a25ee0dc625 100644 --- a/subsys/mgmt/mcumgr/Kconfig +++ b/subsys/mgmt/mcumgr/Kconfig @@ -7,6 +7,7 @@ menuconfig MCUMGR depends on NET_BUF depends on ZCBOR imply CRC + imply BOOT_IMAGE_ACCESS_HOOKS if (SOC_NRF5340_CPUAPP_QKAA && MCUMGR_GRP_IMG) help This option enables the mcumgr management library. diff --git a/subsys/mgmt/mcumgr/bootutil_hooks/nrf53_hooks.c b/subsys/mgmt/mcumgr/bootutil_hooks/nrf53_hooks.c new file mode 100644 index 00000000000..9971a4e0843 --- /dev/null +++ b/subsys/mgmt/mcumgr/bootutil_hooks/nrf53_hooks.c @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "bootutil/bootutil_public.h" + +int boot_read_swap_state_primary_slot_hook(int image_index, + struct boot_swap_state *state) +{ + if (image_index == 1) { + /* Pretend that primary slot of image 1 unpopulated */ + state->magic = BOOT_MAGIC_UNSET; + state->swap_type = BOOT_SWAP_TYPE_NONE; + state->image_num = image_index; + state->copy_done = BOOT_FLAG_UNSET; + state->image_ok = BOOT_FLAG_UNSET; + + /* Prevent bootutil from trying to obtain true info */ + return 0; + } + + return BOOT_HOOK_REGULAR; +} diff --git a/subsys/mgmt/mcumgr/grp/img_mgmt/Kconfig b/subsys/mgmt/mcumgr/grp/img_mgmt/Kconfig index 641e16bd703..81fc57d9238 100644 --- a/subsys/mgmt/mcumgr/grp/img_mgmt/Kconfig +++ b/subsys/mgmt/mcumgr/grp/img_mgmt/Kconfig @@ -46,7 +46,7 @@ endif config MCUMGR_GRP_IMG_UPDATABLE_IMAGE_NUMBER int "Number of supported images" default UPDATEABLE_IMAGE_NUMBER - range 1 2 + range 1 3 help Sets how many application images are supported (pairs of secondary and primary slots). Setting this to 2 requires MCUMGR_TRANSPORT_NETBUF_SIZE to be at least 512b. diff --git a/subsys/mgmt/mcumgr/grp/img_mgmt/src/img_mgmt.c b/subsys/mgmt/mcumgr/grp/img_mgmt/src/img_mgmt.c index 0d733ce7abc..305cad41c44 100644 --- a/subsys/mgmt/mcumgr/grp/img_mgmt/src/img_mgmt.c +++ b/subsys/mgmt/mcumgr/grp/img_mgmt/src/img_mgmt.c @@ -33,51 +33,64 @@ #include #endif -#ifndef CONFIG_FLASH_LOAD_OFFSET -#error MCUmgr requires application to be built with CONFIG_FLASH_LOAD_OFFSET set \ - to be able to figure out application running slot. -#endif - -#define FIXED_PARTITION_IS_RUNNING_APP_PARTITION(label) \ - (FIXED_PARTITION_OFFSET(label) == CONFIG_FLASH_LOAD_OFFSET) +#if USE_PARTITION_MANAGER +#include -#if FIXED_PARTITION_EXISTS(slot0_partition) -#if FIXED_PARTITION_IS_RUNNING_APP_PARTITION(slot0_partition) -#define NUMBER_OF_ACTIVE_IMAGE 0 -#endif +#ifdef PM_MCUBOOT_SECONDARY_PAD_SIZE +BUILD_ASSERT(PM_MCUBOOT_PAD_SIZE == PM_MCUBOOT_SECONDARY_PAD_SIZE); #endif -#if !defined(NUMBER_OF_ACTIVE_IMAGE) && FIXED_PARTITION_EXISTS(slot0_ns_partition) -#if FIXED_PARTITION_IS_RUNNING_APP_PARTITION(slot0_ns_partition) -#define NUMBER_OF_ACTIVE_IMAGE 0 -#endif +#if CONFIG_BUILD_WITH_TFM + #define PM_ADDRESS_OFFSET (PM_MCUBOOT_PAD_SIZE + PM_TFM_SIZE) +#else + #define PM_ADDRESS_OFFSET (PM_MCUBOOT_PAD_SIZE) #endif -#if !defined(NUMBER_OF_ACTIVE_IMAGE) && FIXED_PARTITION_EXISTS(slot1_partition) -#if FIXED_PARTITION_IS_RUNNING_APP_PARTITION(slot1_partition) -#define NUMBER_OF_ACTIVE_IMAGE 0 -#endif -#endif +#define FIXED_PARTITION_IS_RUNNING_APP_PARTITION(label) \ + (FIXED_PARTITION_OFFSET(label) == (PM_ADDRESS - PM_ADDRESS_OFFSET)) -#if !defined(NUMBER_OF_ACTIVE_IMAGE) && FIXED_PARTITION_EXISTS(slot2_partition) -#if FIXED_PARTITION_IS_RUNNING_APP_PARTITION(slot2_partition) -#define NUMBER_OF_ACTIVE_IMAGE 1 -#endif +#else /* ! USE_PARTITION_MANAGER */ +#ifndef CONFIG_FLASH_LOAD_OFFSET +#error MCUmgr requires application to be built with CONFIG_FLASH_LOAD_OFFSET set \ + to be able to figure out application running slot. #endif -#if !defined(NUMBER_OF_ACTIVE_IMAGE) && FIXED_PARTITION_EXISTS(slot3_partition) -#if FIXED_PARTITION_IS_RUNNING_APP_PARTITION(slot3_partition) -#define NUMBER_OF_ACTIVE_IMAGE 1 -#endif +#define FIXED_PARTITION_IS_RUNNING_APP_PARTITION(label) \ + (FIXED_PARTITION_OFFSET(label) == CONFIG_FLASH_LOAD_OFFSET) +#endif /* USE_PARTITION_MANAGER */ + +BUILD_ASSERT(sizeof(struct image_header) == IMAGE_HEADER_SIZE, + "struct image_header not required size"); + +#if CONFIG_MCUMGR_GRP_IMG_UPDATABLE_IMAGE_NUMBER >= 2 +#if FIXED_PARTITION_EXISTS(slot0_ns_partition) && \ + FIXED_PARTITION_IS_RUNNING_APP_PARTITION(slot0_ns_partition) +#define ACTIVE_IMAGE_IS 0 +#elif FIXED_PARTITION_EXISTS(slot0_partition) && \ + FIXED_PARTITION_IS_RUNNING_APP_PARTITION(slot0_partition) +#define ACTIVE_IMAGE_IS 0 +#elif FIXED_PARTITION_EXISTS(slot1_partition) && \ + FIXED_PARTITION_IS_RUNNING_APP_PARTITION(slot1_partition) +#define ACTIVE_IMAGE_IS 0 +#elif FIXED_PARTITION_EXISTS(slot2_partition) && \ + FIXED_PARTITION_IS_RUNNING_APP_PARTITION(slot2_partition) +#define ACTIVE_IMAGE_IS 1 +#elif FIXED_PARTITION_EXISTS(slot3_partition) && \ + FIXED_PARTITION_IS_RUNNING_APP_PARTITION(slot3_partition) +#define ACTIVE_IMAGE_IS 1 +#elif FIXED_PARTITION_EXISTS(slot4_partition) && \ + FIXED_PARTITION_IS_RUNNING_APP_PARTITION(slot4_partition) +#define ACTIVE_IMAGE_IS 2 +#elif FIXED_PARTITION_EXISTS(slot5_partition) && \ + FIXED_PARTITION_IS_RUNNING_APP_PARTITION(slot5_partition) +#define ACTIVE_IMAGE_IS 2 +#else +#define ACTIVE_IMAGE_IS 0 #endif - -#ifndef NUMBER_OF_ACTIVE_IMAGE -#error "Unsupported code parition is set as active application partition" +#else +#define ACTIVE_IMAGE_IS 0 #endif -_Static_assert(sizeof(struct image_header) == IMAGE_HEADER_SIZE, - "struct image_header not required size"); - LOG_MODULE_REGISTER(mcumgr_img_grp, CONFIG_MCUMGR_GRP_IMG_LOG_LEVEL); struct img_mgmt_state g_img_mgmt_state; @@ -159,7 +172,7 @@ int img_mgmt_active_slot(int image) int img_mgmt_active_image(void) { - return NUMBER_OF_ACTIVE_IMAGE; + return ACTIVE_IMAGE_IS; } /* diff --git a/subsys/mgmt/mcumgr/grp/img_mgmt/src/img_mgmt_state.c b/subsys/mgmt/mcumgr/grp/img_mgmt/src/img_mgmt_state.c index a782acb92ee..61515c14655 100644 --- a/subsys/mgmt/mcumgr/grp/img_mgmt/src/img_mgmt_state.c +++ b/subsys/mgmt/mcumgr/grp/img_mgmt/src/img_mgmt_state.c @@ -55,6 +55,14 @@ LOG_MODULE_DECLARE(mcumgr_img_grp, CONFIG_MCUMGR_GRP_IMG_LOG_LEVEL); #define REPORT_SLOT_PENDING BIT(1) #define REPORT_SLOT_CONFIRMED BIT(2) #define REPORT_SLOT_PERMANENT BIT(3) + +#if defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP_WITH_REVERT) +#define DIRECT_XIP_BOOT_UNSET 0 +#define DIRECT_XIP_BOOT_ONCE 1 +#define DIRECT_XIP_BOOT_REVERT 2 +#define DIRECT_XIP_BOOT_FOREVER 3 +#endif + /** * Collects information about the specified image slot. */ @@ -139,7 +147,8 @@ img_mgmt_state_flags(int query_slot) } #endif -#ifndef CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP +#if !defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP) && \ + !defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP_WITH_REVERT) int img_mgmt_get_next_boot_slot(int image, enum img_mgmt_next_boot_type *type) { const int active_slot = img_mgmt_active_slot(image); @@ -188,27 +197,116 @@ int img_mgmt_get_next_boot_slot(int image, enum img_mgmt_next_boot_type *type) return slot; } #else + +#if defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP_WITH_REVERT) + +static int read_directxip_state(int slot) +{ + struct boot_swap_state bss; + int fa_id = img_mgmt_flash_area_id(slot); + const struct flash_area *fa; + int rc = 0; + + __ASSERT(fa_id != -1, "Could not map slot to area ID"); + + rc = flash_area_open(fa_id, &fa); + if (rc < 0) { + return rc; + } + rc = boot_read_swap_state(fa, &bss); + flash_area_close(fa); + if (rc != 0) { + LOG_ERR("Failed to read state of slot %d with error %d", slot, rc); + return -1; + } + + if (bss.magic == BOOT_MAGIC_GOOD) { + if (bss.image_ok == BOOT_FLAG_SET) { + return DIRECT_XIP_BOOT_FOREVER; + } else if (bss.copy_done == BOOT_FLAG_SET) { + return DIRECT_XIP_BOOT_REVERT; + } + return DIRECT_XIP_BOOT_ONCE; + } + return DIRECT_XIP_BOOT_UNSET; +} +#endif /* defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP_WITH_REVERT) */ + int img_mgmt_get_next_boot_slot(int image, enum img_mgmt_next_boot_type *type) { struct image_version aver; struct image_version over; int active_slot = img_mgmt_active_slot(image); int other_slot = img_mgmt_get_opposite_slot(active_slot); +#if defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP_WITH_REVERT) + int active_slot_state; + int other_slot_state; +#endif /* defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP_WITH_REVERT) */ + enum img_mgmt_next_boot_type lt = NEXT_BOOT_TYPE_NORMAL; + int return_slot = active_slot; - if (type != NULL) { - *type = NEXT_BOOT_TYPE_NORMAL; - } int rcs = img_mgmt_read_info(other_slot, &over, NULL, NULL); int rca = img_mgmt_read_info(active_slot, &aver, NULL, NULL); +#if defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP_WITH_REVERT) + active_slot_state = read_directxip_state(active_slot); + other_slot_state = read_directxip_state(other_slot); + if (rca != 0 || + (rcs != 0 && rcs != IMG_MGMT_ERR_NO_IMAGE)) { + /* We do not really know what will happen, as we can not + * read states from bootloader. + */ + LOG_ERR("img_mgmt_read_info_failed rca = %d, rcs = %d", + rca, rcs); + goto out; + } + if (other_slot_state < 0 || active_slot_state < 0) { + LOG_ERR("Slot state read failed with status: active %d, other %d", + active_slot_state, other_slot_state); + /* We do not really know what will happen, as we can not + * read states from bootloader. + */ + goto out; + } + + /* There is not other image, the active one will boot next time */ + if (rcs == IMG_MGMT_ERR_NO_IMAGE) { + goto out; + } + + if (active_slot_state == DIRECT_XIP_BOOT_REVERT) { + lt = NEXT_BOOT_TYPE_REVERT; + return_slot = other_slot; + } else if (other_slot_state == DIRECT_XIP_BOOT_UNSET) { + if (active_slot_state == DIRECT_XIP_BOOT_ONCE) { + lt = NEXT_BOOT_TYPE_TEST; + } + } else if (img_mgmt_vercmp(&aver, &over) < 0) { + if (other_slot_state == DIRECT_XIP_BOOT_FOREVER) { + return_slot = other_slot; + } else if (other_slot_state == DIRECT_XIP_BOOT_ONCE) { + lt = NEXT_BOOT_TYPE_TEST; + return_slot = other_slot; + } + } +out: + +#else if (rcs == 0 && rca == 0 && img_mgmt_vercmp(&aver, &over) < 0) { - return other_slot; + return_slot = other_slot; } +#endif /* defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP_WITH_REVERT) */ - return active_slot; + if (type != NULL) { + *type = lt; + } + + return return_slot; } -#endif +#endif /* !defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP) && \ + * !defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP_WITH_REVERT) + */ /** @@ -422,11 +520,53 @@ img_mgmt_state_read(struct smp_streamer *ctxt) return ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE; } -int img_mgmt_set_next_boot_slot(int slot, bool confirm) +static int img_mgmt_set_next_boot_slot_common(int slot, int active_slot, bool confirm) { const struct flash_area *fa; int area_id = img_mgmt_flash_area_id(slot); int rc = 0; + + if (flash_area_open(area_id, &fa) != 0) { + return IMG_MGMT_ERR_FLASH_OPEN_FAILED; + } + + rc = boot_set_next(fa, slot == active_slot, confirm); + if (rc != 0) { + /* Failed to set next slot for boot as desired */ + LOG_ERR("Faled boot_set_next with code %d, for slot %d," + " with active slot %d and confirm %d", + rc, slot, active_slot, confirm); + + /* Translate from boot util error code to IMG mgmt group error code */ + if (rc == BOOT_EFLASH) { + rc = IMG_MGMT_ERR_FLASH_WRITE_FAILED; + } else if (rc == BOOT_EBADVECT) { + rc = IMG_MGMT_ERR_INVALID_IMAGE_VECTOR_TABLE; + } else if (rc == BOOT_EBADIMAGE) { + rc = IMG_MGMT_ERR_INVALID_IMAGE_HEADER_MAGIC; + } else { + rc = IMG_MGMT_ERR_UNKNOWN; + } + } + flash_area_close(fa); + +#if defined(CONFIG_MCUMGR_GRP_IMG_STATUS_HOOKS) + if (rc == 0 && slot == active_slot && confirm) { + int32_t err_rc; + uint16_t err_group; + + /* Confirm event is only sent for active slot */ + (void)mgmt_callback_notify(MGMT_EVT_OP_IMG_MGMT_DFU_CONFIRMED, NULL, 0, &err_rc, + &err_group); + } +#endif + + return rc; +} + +#ifndef CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP_WITH_REVERT +int img_mgmt_set_next_boot_slot(int slot, bool confirm) +{ /* image the requested slot is defined within */ int image = img_mgmt_slot_to_image(slot); /* active_slot is slot that is considered active/primary/executing @@ -498,43 +638,25 @@ int img_mgmt_set_next_boot_slot(int slot, bool confirm) /* Allow confirming slot == active_slot */ } - if (flash_area_open(area_id, &fa) != 0) { - return IMG_MGMT_ERR_FLASH_OPEN_FAILED; - } - - rc = boot_set_next(fa, slot == active_slot, confirm); - if (rc != 0) { - /* Failed to set next slot for boot as desired */ - LOG_ERR("Faled boot_set_next with code %d, for slot %d," - " with active slot %d and confirm %d", - rc, slot, active_slot, confirm); - - /* Translate from boot util error code to IMG mgmt group error code */ - if (rc == BOOT_EFLASH) { - rc = IMG_MGMT_ERR_FLASH_WRITE_FAILED; - } else if (rc == BOOT_EBADVECT) { - rc = IMG_MGMT_ERR_INVALID_IMAGE_VECTOR_TABLE; - } else if (rc == BOOT_EBADIMAGE) { - rc = IMG_MGMT_ERR_INVALID_IMAGE_HEADER_MAGIC; - } else { - rc = IMG_MGMT_ERR_UNKNOWN; - } - } - flash_area_close(fa); + return img_mgmt_set_next_boot_slot_common(slot, active_slot, confirm); +} +#else +int img_mgmt_set_next_boot_slot(int slot, bool confirm) +{ + int active_image = img_mgmt_active_image(); + int active_slot = img_mgmt_active_slot(active_image); -#if defined(CONFIG_MCUMGR_GRP_IMG_STATUS_HOOKS) - if (slot == active_slot && confirm) { - int32_t err_rc; - uint16_t err_group; + LOG_DBG("(%d, %s)", slot, confirm ? "confirm" : "test"); + LOG_DBG("aimg = %d, aslot = %d, slot = %d", + active_image, active_slot, slot); - /* Confirm event is only sent for active slot */ - (void)mgmt_callback_notify(MGMT_EVT_OP_IMG_MGMT_DFU_CONFIRMED, NULL, 0, &err_rc, - &err_group); + if (slot == active_slot && !confirm) { + return IMG_MGMT_ERR_IMAGE_SETTING_TEST_TO_ACTIVE_DENIED; } -#endif - return rc; + return img_mgmt_set_next_boot_slot_common(slot, active_slot, confirm); } +#endif /** * Command handler: image state write diff --git a/subsys/mgmt/mcumgr/grp/img_mgmt/src/zephyr_img_mgmt.c b/subsys/mgmt/mcumgr/grp/img_mgmt/src/zephyr_img_mgmt.c index c3641986fd2..addde1e9a2c 100644 --- a/subsys/mgmt/mcumgr/grp/img_mgmt/src/zephyr_img_mgmt.c +++ b/subsys/mgmt/mcumgr/grp/img_mgmt/src/zephyr_img_mgmt.c @@ -25,12 +25,26 @@ LOG_MODULE_DECLARE(mcumgr_img_grp, CONFIG_MCUMGR_GRP_IMG_LOG_LEVEL); #define SLOT1_PARTITION slot1_partition #define SLOT2_PARTITION slot2_partition #define SLOT3_PARTITION slot3_partition +#define SLOT4_PARTITION slot4_partition +#define SLOT5_PARTITION slot5_partition + +/* SLOT0_PARTITION and SLOT1_PARTITION are not checked because + * there is not conditional code that depends on them. If they do + * not exist compilation will fail, but in case if some of other + * partitions do not exist, code will compile and will not work + * properly. + */ +#if CONFIG_MCUMGR_GRP_IMG_UPDATABLE_IMAGE_NUMBER >= 2 +BUILD_ASSERT(FIXED_PARTITION_EXISTS(SLOT2_PARTITION) && + FIXED_PARTITION_EXISTS(SLOT3_PARTITION), + "Missing partitions?"); +#endif -BUILD_ASSERT(CONFIG_MCUMGR_GRP_IMG_UPDATABLE_IMAGE_NUMBER == 1 || - (CONFIG_MCUMGR_GRP_IMG_UPDATABLE_IMAGE_NUMBER == 2 && - FIXED_PARTITION_EXISTS(SLOT2_PARTITION) && - FIXED_PARTITION_EXISTS(SLOT3_PARTITION)), +#if CONFIG_MCUMGR_GRP_IMG_UPDATABLE_IMAGE_NUMBER == 3 +BUILD_ASSERT(FIXED_PARTITION_EXISTS(SLOT4_PARTITION) && + FIXED_PARTITION_EXISTS(SLOT5_PARTITION), "Missing partitions?"); +#endif /** * Determines if the specified area of flash is completely unwritten. @@ -137,6 +151,18 @@ img_mgmt_flash_area_id(int slot) break; #endif +#if FIXED_PARTITION_EXISTS(SLOT4_PARTITION) + case 4: + fa_id = FIXED_PARTITION_ID(SLOT4_PARTITION); + break; +#endif + +#if FIXED_PARTITION_EXISTS(SLOT5_PARTITION) + case 5: + fa_id = FIXED_PARTITION_ID(SLOT5_PARTITION); + break; +#endif + default: fa_id = -1; break; @@ -194,7 +220,7 @@ static int img_mgmt_get_unused_slot_area_id(int slot) return slot != -1 ? img_mgmt_flash_area_id(slot) : -1; #endif } -#elif CONFIG_MCUMGR_GRP_IMG_UPDATABLE_IMAGE_NUMBER == 2 +#elif CONFIG_MCUMGR_GRP_IMG_UPDATABLE_IMAGE_NUMBER >= 2 static int img_mgmt_get_unused_slot_area_id(int image) { int area_id = -1; diff --git a/subsys/mgmt/mcumgr/grp/img_mgmt_client/CMakeLists.txt b/subsys/mgmt/mcumgr/grp/img_mgmt_client/CMakeLists.txt index 693891896d8..cae17e865ff 100644 --- a/subsys/mgmt/mcumgr/grp/img_mgmt_client/CMakeLists.txt +++ b/subsys/mgmt/mcumgr/grp/img_mgmt_client/CMakeLists.txt @@ -13,3 +13,4 @@ zephyr_library_sources( ) zephyr_library_include_directories(include) +zephyr_library_link_libraries(MCUBOOT_BOOTUTIL) diff --git a/subsys/mgmt/mcumgr/grp/img_mgmt_client/src/img_mgmt_client.c b/subsys/mgmt/mcumgr/grp/img_mgmt_client/src/img_mgmt_client.c index b0e38640e8c..9ab8ee02dcd 100644 --- a/subsys/mgmt/mcumgr/grp/img_mgmt_client/src/img_mgmt_client.c +++ b/subsys/mgmt/mcumgr/grp/img_mgmt_client/src/img_mgmt_client.c @@ -126,7 +126,7 @@ static int image_state_res_fn(struct net_buf *nb, void *user_data) goto out; } /* Check that mandatory parameters have decoded */ - if (hash.len != IMG_MGMT_HASH_LEN || !version.len || + if (hash.len != IMG_MGMT_DATA_SHA_LEN || !version.len || !zcbor_map_decode_bulk_key_found(list_res_decode, ARRAY_SIZE(list_res_decode), "slot")) { LOG_ERR("Missing mandatory parametrs"); @@ -138,7 +138,7 @@ static int image_state_res_fn(struct net_buf *nb, void *user_data) image_info->image_list[image_info->image_list_length].img_num = img_num; image_info->image_list[image_info->image_list_length].slot_num = slot_num; memcpy(image_info->image_list[image_info->image_list_length].hash, - hash.value, IMG_MGMT_HASH_LEN); + hash.value, IMG_MGMT_DATA_SHA_LEN); if (version.len > IMG_MGMT_VER_MAX_STR_LEN) { LOG_WRN("Version truncated len %d -> %d", version.len, IMG_MGMT_VER_MAX_STR_LEN); @@ -277,7 +277,7 @@ static size_t upload_message_header_size(struct img_gr_upload *upload_state) /* Write hash when it defined and offset is 0 */ if (ok && upload_state->hash_initialized) { ok = zcbor_tstr_put_lit(zse, "sha") && - zcbor_bstr_encode_ptr(zse, upload_state->sha256, IMG_MGMT_HASH_LEN); + zcbor_bstr_encode_ptr(zse, upload_state->sha256, IMG_MGMT_DATA_SHA_LEN); } if (ok) { @@ -311,7 +311,7 @@ int img_mgmt_client_upload_init(struct img_mgmt_client *client, size_t image_siz client->upload.offset = 0; client->upload.image_num = image_num; if (image_hash) { - memcpy(client->upload.sha256, image_hash, IMG_MGMT_HASH_LEN); + memcpy(client->upload.sha256, image_hash, IMG_MGMT_DATA_SHA_LEN); client->upload.hash_initialized = true; } else { client->upload.hash_initialized = false; @@ -395,7 +395,7 @@ int img_mgmt_client_upload(struct img_mgmt_client *client, const uint8_t *data, if (ok && active_client->upload.hash_initialized) { ok = zcbor_tstr_put_lit(zse, "sha") && zcbor_bstr_encode_ptr(zse, active_client->upload.sha256, - IMG_MGMT_HASH_LEN); + IMG_MGMT_DATA_SHA_LEN); } } @@ -485,7 +485,7 @@ int img_mgmt_client_state_write(struct img_mgmt_client *client, char *hash, bool /* Write hash data */ if (ok && hash) { ok = zcbor_tstr_put_lit(zse, "hash") && - zcbor_bstr_encode_ptr(zse, hash, IMG_MGMT_HASH_LEN); + zcbor_bstr_encode_ptr(zse, hash, IMG_MGMT_DATA_SHA_LEN); } /* Close map */ if (ok) { diff --git a/subsys/mgmt/mcumgr/grp/os_mgmt/CMakeLists.txt b/subsys/mgmt/mcumgr/grp/os_mgmt/CMakeLists.txt index e712acf6e94..35123fa1159 100644 --- a/subsys/mgmt/mcumgr/grp/os_mgmt/CMakeLists.txt +++ b/subsys/mgmt/mcumgr/grp/os_mgmt/CMakeLists.txt @@ -1,6 +1,6 @@ # # Copyright (c) 2018-2021 mcumgr authors -# Copyright (c) 2022 Nordic Semiconductor ASA +# Copyright (c) 2022-2023 Nordic Semiconductor ASA # # SPDX-License-Identifier: Apache-2.0 # @@ -12,6 +12,12 @@ zephyr_library_sources(src/os_mgmt.c) zephyr_library_include_directories(include) +if (CONFIG_MCUMGR_GRP_OS_BOOTLOADER_INFO) + zephyr_include_directories( + ${ZEPHYR_MCUBOOT_MODULE_DIR}/boot/bootutil/include + ) +endif() + if(DEFINED CONFIG_MCUMGR_GRP_OS_INFO_BUILD_DATE_TIME) set(MCUMGR_GRP_OS_INFO_BUILD_DATE_TIME_DIR ${PROJECT_BINARY_DIR}/os_mgmt_auto) file(MAKE_DIRECTORY ${MCUMGR_GRP_OS_INFO_BUILD_DATE_TIME_DIR}) diff --git a/subsys/mgmt/mcumgr/grp/os_mgmt/Kconfig b/subsys/mgmt/mcumgr/grp/os_mgmt/Kconfig index 897db735982..fa743b53caf 100644 --- a/subsys/mgmt/mcumgr/grp/os_mgmt/Kconfig +++ b/subsys/mgmt/mcumgr/grp/os_mgmt/Kconfig @@ -173,6 +173,16 @@ config MCUMGR_GRP_OS_INFO_BUILD_DATE_TIME endif +if BOOTLOADER_MCUBOOT + +config MCUMGR_GRP_OS_BOOTLOADER_INFO + bool "Bootloader information" + help + Allows to query MCUmgr about bootloader used by device and various bootloader + parameters. + +endif # BOOTLOADER_MCUBOOT + module = MCUMGR_GRP_OS module-str = mcumgr_grp_os source "subsys/logging/Kconfig.template.log_config" diff --git a/subsys/mgmt/mcumgr/grp/os_mgmt/src/os_mgmt.c b/subsys/mgmt/mcumgr/grp/os_mgmt/src/os_mgmt.c index 55a6fa81564..a42e2f4022f 100644 --- a/subsys/mgmt/mcumgr/grp/os_mgmt/src/os_mgmt.c +++ b/subsys/mgmt/mcumgr/grp/os_mgmt/src/os_mgmt.c @@ -33,10 +33,15 @@ #include #endif -#ifdef CONFIG_MCUMGR_GRP_OS_INFO +#if defined(CONFIG_MCUMGR_GRP_OS_INFO) || defined(CONFIG_MCUMGR_GRP_OS_BOOTLOADER_INFO) #include #include +#if defined(CONFIG_MCUMGR_GRP_OS_INFO) #include +#endif +#if defined(CONFIG_MCUMGR_GRP_OS_BOOTLOADER_INFO) +#include +#endif #include #if defined(CONFIG_NET_HOSTNAME_ENABLE) #include @@ -370,6 +375,64 @@ os_mgmt_mcumgr_params(struct smp_streamer *ctxt) } #endif +#if defined(CONFIG_MCUMGR_GRP_OS_BOOTLOADER_INFO) + +#if IS_ENABLED(CONFIG_MCUBOOT_BOOTLOADER_MODE_SINGLE_APP) +#define BOOTLOADER_MODE MCUBOOT_MODE_SINGLE_SLOT +#elif IS_ENABLED(CONFIG_MCUBOOT_BOOTLOADER_MODE_SWAP_SCRATCH) +#define BOOTLOADER_MODE MCUBOOT_MODE_SWAP_USING_SCRATCH +#elif IS_ENABLED(CONFIG_MCUBOOT_BOOTLOADER_MODE_OVERWRITE_ONLY) +#define BOOTLOADER_MODE MCUBOOT_MODE_UPGRADE_ONLY +#elif IS_ENABLED(CONFIG_MCUBOOT_BOOTLOADER_MODE_SWAP_WITHOUT_SCRATCH) +#define BOOTLOADER_MODE MCUBOOT_MODE_SWAP_USING_MOVE +#elif IS_ENABLED(CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP) +#define BOOTLOADER_MODE MCUBOOT_MODE_DIRECT_XIP +#elif IS_ENABLED(CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP_WITH_REVERT) +#define BOOTLOADER_MODE MCUBOOT_MODE_DIRECT_XIP_WITH_REVERT +#else +#define BOOTLOADER_MODE -1 +#endif + +static int +os_mgmt_bootloader_info(struct smp_streamer *ctxt) +{ + zcbor_state_t *zse = ctxt->writer->zs; + zcbor_state_t *zsd = ctxt->reader->zs; + struct zcbor_string query = { 0 }; + size_t decoded; + bool ok; + + struct zcbor_map_decode_key_val bootloader_info[] = { + ZCBOR_MAP_DECODE_KEY_DECODER("query", zcbor_tstr_decode, &query), + }; + + if (zcbor_map_decode_bulk(zsd, bootloader_info, ARRAY_SIZE(bootloader_info), &decoded)) { + return MGMT_ERR_EINVAL; + } + + /* If no parameter is recognized then just introduce the bootloader. */ + if (decoded == 0) { + ok = zcbor_tstr_put_lit(zse, "bootloader") && + zcbor_tstr_put_lit(zse, "MCUboot"); + } else if (zcbor_map_decode_bulk_key_found(bootloader_info, ARRAY_SIZE(bootloader_info), + "query") && + (sizeof("mode") - 1) == query.len && + memcmp("mode", query.value, query.len) == 0) { + + ok = zcbor_tstr_put_lit(zse, "mode") && + zcbor_int32_put(zse, BOOTLOADER_MODE); +#if IS_ENABLED(MCUBOOT_BOOTLOADER_NO_DOWNGRADE) + ok = zcbor_tstr_put_lit(zse, "no-downgrade") && + zcbor_bool_encode(zse, true); +#endif + } else { + return OS_MGMT_ERR_QUERY_YIELDS_NO_ANSWER; + } + + return ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE; +} +#endif + #ifdef CONFIG_MCUMGR_GRP_OS_INFO /** * Command handler: os info @@ -733,6 +796,11 @@ static const struct mgmt_handler os_mgmt_group_handlers[] = { os_mgmt_info, NULL }, #endif +#ifdef CONFIG_MCUMGR_GRP_OS_BOOTLOADER_INFO + [OS_MGMT_ID_BOOTLOADER_INFO] = { + os_mgmt_bootloader_info, NULL + }, +#endif }; #define OS_MGMT_GROUP_SZ ARRAY_SIZE(os_mgmt_group_handlers) diff --git a/subsys/net/conn_mgr/Kconfig b/subsys/net/conn_mgr/Kconfig index e72e614d54c..05686a663eb 100644 --- a/subsys/net/conn_mgr/Kconfig +++ b/subsys/net/conn_mgr/Kconfig @@ -24,6 +24,7 @@ source "subsys/net/Kconfig.template.log_config.net" config NET_CONNECTION_MANAGER_MONITOR_STACK_SIZE int "Size of the stack allocated for the conn_mgr_monitor thread" + default 8192 if WPA_SUPP default 512 help Sets the stack size which will be used by the connection manager for connectivity monitoring. diff --git a/subsys/net/ip/CMakeLists.txt b/subsys/net/ip/CMakeLists.txt index 70655e2f61c..7dc54935c5c 100644 --- a/subsys/net/ip/CMakeLists.txt +++ b/subsys/net/ip/CMakeLists.txt @@ -31,6 +31,7 @@ zephyr_library_sources(net_tc.c) zephyr_library_sources_ifdef(CONFIG_NET_IP connection.c) zephyr_library_sources_ifdef(CONFIG_NET_6LO 6lo.c) zephyr_library_sources_ifdef(CONFIG_NET_DHCPV4 dhcpv4.c) +zephyr_library_sources_ifdef(CONFIG_NET_DHCPV6 dhcpv6.c) zephyr_library_sources_ifdef(CONFIG_NET_IPV4_AUTO ipv4_autoconf.c) zephyr_library_sources_ifdef(CONFIG_NET_IPV4 icmpv4.c ipv4.c) zephyr_library_sources_ifdef(CONFIG_NET_IPV4_IGMP igmp.c) diff --git a/subsys/net/ip/Kconfig b/subsys/net/ip/Kconfig index d7edb988553..d2ca36b845c 100644 --- a/subsys/net/ip/Kconfig +++ b/subsys/net/ip/Kconfig @@ -290,6 +290,12 @@ config NET_RX_DEFAULT_PRIORITY What is the default network RX packet priority if user has not set one. The value 0 means lowest priority and 7 is the highest. +config NET_ALLOW_ANY_PRIORITY + bool "Allow any network packet priority to be used" + help + If this is set, then any user given network packet priority can be used. Otherwise + the network packet priorities are limited to 0-7 range. + config NET_IP_ADDR_CHECK bool "Check IP address validity before sending IP packet" default y diff --git a/subsys/net/ip/Kconfig.ipv6 b/subsys/net/ip/Kconfig.ipv6 index b776d3756eb..18fb4b0ccc4 100644 --- a/subsys/net/ip/Kconfig.ipv6 +++ b/subsys/net/ip/Kconfig.ipv6 @@ -166,6 +166,12 @@ config NET_MAX_6LO_CONTEXTS 6lowpan context options table size. The value depends on your network and memory consumption. More 6CO options uses more memory. +config NET_DHCPV6 + bool "DHCPv6 client" + select NET_MGMT + select NET_MGMT_EVENT + depends on NET_UDP + if NET_6LO module = NET_6LO module-dep = NET_LOG @@ -192,5 +198,13 @@ module-str = Log level for IPv6 neighbor cache module-help = Enables IPv6 Neighbor Cache code to output debug messages. source "subsys/net/Kconfig.template.log_config.net" +if NET_DHCPV6 +module = NET_DHCPV6 +module-dep = NET_LOG +module-str = Log level for DHCPv6 client +module-help = Enables DHCPv6 client code to output debug messages. +source "subsys/net/Kconfig.template.log_config.net" +endif # NET_DHCPV6 + endif # NET_NATIVE_IPV6 endif # NET_IPV6 diff --git a/subsys/net/ip/dhcpv6.c b/subsys/net/ip/dhcpv6.c new file mode 100644 index 00000000000..fdfef3c5147 --- /dev/null +++ b/subsys/net/ip/dhcpv6.c @@ -0,0 +1,2204 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** @file + * @brief DHCPv6 client implementation + */ + +#include +LOG_MODULE_REGISTER(net_dhcpv6, CONFIG_NET_DHCPV6_LOG_LEVEL); + +#include +#include +#include +#include + +#include "dhcpv6_internal.h" +#include "ipv6.h" +#include "net_private.h" +#include "udp_internal.h" + +/* Maximum number of options client can request. */ +#define DHCPV6_MAX_OPTION_REQUEST 2 + +struct dhcpv6_options_include { + bool clientid : 1; + bool serverid : 1; + bool elapsed_time : 1; + bool ia_na : 1; + bool iaaddr : 1; + bool ia_pd : 1; + bool iaprefix : 1; + uint16_t oro[DHCPV6_MAX_OPTION_REQUEST]; +}; + +static K_MUTEX_DEFINE(lock); + +/* All_DHCP_Relay_Agents_and_Servers (ff02::1:2) */ +static const struct in6_addr all_dhcpv6_ra_and_servers = { { { 0xff, 0x02, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0x01, 0, 0x02 } } }; + +static sys_slist_t dhcpv6_ifaces = SYS_SLIST_STATIC_INIT(&dhcpv6_ifaces); +static struct k_work_delayable dhcpv6_timeout_work; +static struct net_mgmt_event_callback dhcpv6_mgmt_cb; + +const char *net_dhcpv6_state_name(enum net_dhcpv6_state state) +{ + static const char * const name[] = { + "disabled", + "init", + "soliciting", + "requesting", + "confirming", + "renewing", + "rebinding", + "information requesting", + "bound", + }; + + __ASSERT_NO_MSG(state >= 0 && state < sizeof(name)); + return name[state]; +} + +static void dhcpv6_generate_tid(struct net_if *iface) +{ + sys_rand_get(iface->config.dhcpv6.tid, sizeof(iface->config.dhcpv6.tid)); +} + +static void dhcvp6_update_deadlines(struct net_if *iface, int64_t now, + uint32_t t1, uint32_t t2, + uint32_t preferred_lifetime, + uint32_t valid_lifetime) +{ + uint64_t t1_abs, t2_abs, expire_abs; + + /* In case server does not set T1/T2 values, the time choice is left to + * the client discretion. + * Here, we use recommendations for the servers, where it's advised to + * set T1/T2 as 0.5 and 0.8 of the preferred lifetime. + */ + if (t1 == 0 && t2 == 0) { + if (preferred_lifetime == DHCPV6_INFINITY) { + t1 = DHCPV6_INFINITY; + t2 = DHCPV6_INFINITY; + } else { + t1 = preferred_lifetime * 0.5; + t2 = preferred_lifetime * 0.8; + } + } else if (t1 == 0) { + if (t2 == DHCPV6_INFINITY) { + t1 = DHCPV6_INFINITY; + } else { + t1 = t2 * 0.625; /* 0.5 / 0.8 */ + } + } else if (t2 == 0) { + if (t1 == DHCPV6_INFINITY) { + t2 = DHCPV6_INFINITY; + } else { + t2 = t1 * 1.6; /* 0.8 / 0.5 */ + /* Overflow check. */ + if (t2 < t1) { + t2 = DHCPV6_INFINITY; + } + } + } else if (t1 >= t2) { + NET_ERR("Invalid T1(%u)/T2(%u) values.", t1, t2); + return; + } + + if (t1 == DHCPV6_INFINITY || + u64_add_overflow(now, 1000ULL * t1, &t1_abs)) { + t1_abs = UINT64_MAX; + } + + if (t2 == DHCPV6_INFINITY || + u64_add_overflow(now, 1000ULL * t2, &t2_abs)) { + t2_abs = UINT64_MAX; + } + + if (valid_lifetime == DHCPV6_INFINITY || + u64_add_overflow(now, 1000ULL * valid_lifetime, &expire_abs)) { + expire_abs = UINT64_MAX; + } + + if (iface->config.dhcpv6.t1 > t1_abs) { + iface->config.dhcpv6.t1 = t1_abs; + } + + if (iface->config.dhcpv6.t2 > t2_abs) { + iface->config.dhcpv6.t2 = t2_abs; + } + + if (iface->config.dhcpv6.expire < expire_abs) { + iface->config.dhcpv6.expire = expire_abs; + } +} + +static void dhcpv6_set_timeout(struct net_if *iface, uint64_t timeout) +{ + int64_t now = k_uptime_get(); + + NET_DBG("sched dhcpv6 timeout iface=%p timeout=%llums", iface, timeout); + + if (u64_add_overflow(now, timeout, &iface->config.dhcpv6.timeout)) { + iface->config.dhcpv6.timeout = UINT64_MAX; + } +} + +static void dhcpv6_reschedule(void) +{ + k_work_reschedule(&dhcpv6_timeout_work, K_NO_WAIT); +} + +static int randomize_timeout(int multiplier, int timeout) +{ + int factor; + + /* DHCPv6 RFC8415, ch. 15. the randomization factor should be a random + * number between -0.1 nand +0.1. As we operate on integers here, we + * scale it to -100 and +100, and divide the result by 1000. + */ + factor = (int)(sys_rand32_get() % 201) - 100; + + return (multiplier * timeout) + ((factor * timeout) / 1000); +} + +static int dhcpv6_initial_retransmit_time(int init_retransmit_time) +{ + /* DHCPv6 RFC8415, ch. 15. Retransmission time for the first msg. */ + return randomize_timeout(1, init_retransmit_time); +} + +static uint32_t dhcpv6_next_retransmit_time(int prev_retransmit_time, + int max_retransmit_time) +{ + int retransmit_time; + + /* DHCPv6 RFC8415, ch. 15. Retransmission time for the subsequent msg. */ + retransmit_time = randomize_timeout(2, prev_retransmit_time); + + if (max_retransmit_time == 0) { + return retransmit_time; + } + + if (retransmit_time > max_retransmit_time) { + retransmit_time = randomize_timeout(1, max_retransmit_time); + } + + return retransmit_time; +} + +/* DHCPv6 packet encoding functions */ + +static int dhcpv6_add_header(struct net_pkt *pkt, enum dhcpv6_msg_type type, + uint8_t *tid) +{ + int ret; + + ret = net_pkt_write_u8(pkt, type); + if (ret < 0) { + return ret; + } + + ret = net_pkt_write(pkt, tid, DHCPV6_TID_SIZE); + + return ret; +} + +static int dhcpv6_add_option_header(struct net_pkt *pkt, + enum dhcpv6_option_code code, + uint16_t length) +{ + int ret; + + ret = net_pkt_write_be16(pkt, code); + if (ret < 0) { + return ret; + } + + ret = net_pkt_write_be16(pkt, length); + + return ret; +} + +static int dhcpv6_add_option_clientid(struct net_pkt *pkt, + struct net_dhcpv6_duid_storage *clientid) +{ + int ret; + + ret = dhcpv6_add_option_header(pkt, DHCPV6_OPTION_CODE_CLIENTID, + clientid->length); + if (ret < 0) { + return ret; + } + + ret = net_pkt_write(pkt, &clientid->duid, clientid->length); + + return ret; +} + +static int dhcpv6_add_option_serverid(struct net_pkt *pkt, + struct net_dhcpv6_duid_storage *serverid) +{ + int ret; + + ret = dhcpv6_add_option_header(pkt, DHCPV6_OPTION_CODE_SERVERID, + serverid->length); + if (ret < 0) { + return ret; + } + + ret = net_pkt_write(pkt, &serverid->duid, serverid->length); + + return ret; +} + + +static int dhcpv6_add_option_elapsed_time(struct net_pkt *pkt, uint64_t since) +{ + uint64_t elapsed; + int ret; + + ret = dhcpv6_add_option_header(pkt, DHCPV6_OPTION_CODE_ELAPSED_TIME, + DHCPV6_OPTION_ELAPSED_TIME_SIZE); + if (ret < 0) { + return ret; + } + + /* Elapsed time should be expressed in hundredths of a second. */ + elapsed = (k_uptime_get() - since) / 10ULL; + if (elapsed > 0xFFFF) { + elapsed = 0xFFFF; + } + + ret = net_pkt_write_be16(pkt, (uint16_t)elapsed); + + return ret; +} + +static int dhcpv6_add_option_ia_na(struct net_pkt *pkt, struct dhcpv6_ia_na *ia_na, + bool include_addr) +{ + uint16_t optlen; + int ret; + + if (!include_addr) { + optlen = DHCPV6_OPTION_IA_NA_HEADER_SIZE; + } else { + optlen = DHCPV6_OPTION_IA_NA_HEADER_SIZE + + DHCPV6_OPTION_HEADER_SIZE + + DHCPV6_OPTION_IAADDR_HEADER_SIZE; + } + + ret = dhcpv6_add_option_header(pkt, DHCPV6_OPTION_CODE_IA_NA, optlen); + if (ret < 0) { + return ret; + } + + ret = net_pkt_write_be32(pkt, ia_na->iaid); + if (ret < 0) { + return ret; + } + + ret = net_pkt_write_be32(pkt, ia_na->t1); + if (ret < 0) { + return ret; + } + + ret = net_pkt_write_be32(pkt, ia_na->t2); + if (ret < 0) { + return ret; + } + + if (!include_addr) { + return 0; + } + + ret = dhcpv6_add_option_header(pkt, DHCPV6_OPTION_CODE_IAADDR, + DHCPV6_OPTION_IAADDR_HEADER_SIZE); + if (ret < 0) { + return ret; + } + + ret = net_pkt_write(pkt, &ia_na->iaaddr.addr, sizeof(ia_na->iaaddr.addr)); + if (ret < 0) { + return ret; + } + + ret = net_pkt_write_be32(pkt, ia_na->iaaddr.preferred_lifetime); + if (ret < 0) { + return ret; + } + + ret = net_pkt_write_be32(pkt, ia_na->iaaddr.valid_lifetime); + + return ret; +} + +static int dhcpv6_add_option_ia_pd(struct net_pkt *pkt, struct dhcpv6_ia_pd *ia_pd, + bool include_prefix) +{ + uint16_t optlen; + int ret; + + if (!include_prefix) { + optlen = DHCPV6_OPTION_IA_PD_HEADER_SIZE; + } else { + optlen = DHCPV6_OPTION_IA_PD_HEADER_SIZE + + DHCPV6_OPTION_HEADER_SIZE + + DHCPV6_OPTION_IAPREFIX_HEADER_SIZE; + } + + ret = dhcpv6_add_option_header(pkt, DHCPV6_OPTION_CODE_IA_PD, + optlen); + if (ret < 0) { + return ret; + } + + ret = net_pkt_write_be32(pkt, ia_pd->iaid); + if (ret < 0) { + return ret; + } + + ret = net_pkt_write_be32(pkt, ia_pd->t1); + if (ret < 0) { + return ret; + } + + ret = net_pkt_write_be32(pkt, ia_pd->t2); + if (ret < 0) { + return ret; + } + + if (!include_prefix) { + return 0; + } + + ret = dhcpv6_add_option_header(pkt, DHCPV6_OPTION_CODE_IAPREFIX, + DHCPV6_OPTION_IAPREFIX_HEADER_SIZE); + if (ret < 0) { + return ret; + } + + ret = net_pkt_write_be32(pkt, ia_pd->iaprefix.preferred_lifetime); + if (ret < 0) { + return ret; + } + + ret = net_pkt_write_be32(pkt, ia_pd->iaprefix.valid_lifetime); + if (ret < 0) { + return ret; + } + + ret = net_pkt_write_u8(pkt, ia_pd->iaprefix.prefix_len); + if (ret < 0) { + return ret; + } + + ret = net_pkt_write(pkt, &ia_pd->iaprefix.prefix, + sizeof(ia_pd->iaprefix.prefix)); + + return ret; +} + +static int dhcpv6_add_option_oro(struct net_pkt *pkt, uint16_t *codes, + int code_cnt) +{ + int ret; + + ret = dhcpv6_add_option_header(pkt, DHCPV6_OPTION_CODE_ORO, + sizeof(uint16_t) * code_cnt); + if (ret < 0) { + return ret; + } + + for (int i = 0; i < code_cnt; i++) { + ret = net_pkt_write_be16(pkt, codes[i]); + if (ret < 0) { + return ret; + } + } + + return ret; +} + +static size_t dhcpv6_calculate_message_size(struct dhcpv6_options_include *options) +{ + size_t msg_size = sizeof(struct dhcpv6_msg_hdr); + uint8_t oro_cnt = 0; + + if (options->clientid) { + msg_size += DHCPV6_OPTION_HEADER_SIZE; + msg_size += sizeof(struct net_dhcpv6_duid_storage); + } + + if (options->serverid) { + msg_size += DHCPV6_OPTION_HEADER_SIZE; + msg_size += sizeof(struct net_dhcpv6_duid_storage); + } + + if (options->elapsed_time) { + msg_size += DHCPV6_OPTION_HEADER_SIZE; + msg_size += DHCPV6_OPTION_ELAPSED_TIME_SIZE; + } + + if (options->ia_na) { + msg_size += DHCPV6_OPTION_HEADER_SIZE; + msg_size += DHCPV6_OPTION_IA_NA_HEADER_SIZE; + } + + if (options->iaaddr) { + msg_size += DHCPV6_OPTION_HEADER_SIZE; + msg_size += DHCPV6_OPTION_IAADDR_HEADER_SIZE; + } + + if (options->ia_pd) { + msg_size += DHCPV6_OPTION_HEADER_SIZE; + msg_size += DHCPV6_OPTION_IA_PD_HEADER_SIZE; + } + + if (options->iaprefix) { + msg_size += DHCPV6_OPTION_HEADER_SIZE; + msg_size += DHCPV6_OPTION_IAPREFIX_HEADER_SIZE; + } + + for (uint8_t i = 0; i < ARRAY_SIZE(options->oro); i++) { + if (options->oro[i] == 0) { + break; + } + + oro_cnt++; + } + + if (oro_cnt > 0) { + msg_size += DHCPV6_OPTION_HEADER_SIZE; + msg_size += oro_cnt * sizeof(uint16_t); + } + + return msg_size; +} + +static int dhcpv6_add_options(struct net_if *iface, struct net_pkt *pkt, + struct dhcpv6_options_include *options) +{ + uint8_t oro_cnt = 0; + int ret; + + if (options->clientid) { + ret = dhcpv6_add_option_clientid( + pkt, &iface->config.dhcpv6.clientid); + if (ret < 0) { + goto fail; + } + } + + if (options->serverid) { + ret = dhcpv6_add_option_serverid( + pkt, &iface->config.dhcpv6.serverid); + if (ret < 0) { + goto fail; + } + } + + if (options->elapsed_time) { + ret = dhcpv6_add_option_elapsed_time( + pkt, iface->config.dhcpv6.exchange_start); + if (ret < 0) { + goto fail; + } + } + + if (options->ia_na) { + struct dhcpv6_ia_na ia_na = { + .iaid = iface->config.dhcpv6.addr_iaid, + }; + + if (options->iaaddr) { + memcpy(&ia_na.iaaddr.addr, &iface->config.dhcpv6.addr, + sizeof(ia_na.iaaddr.addr)); + } + + ret = dhcpv6_add_option_ia_na(pkt, &ia_na, options->iaaddr); + if (ret < 0) { + goto fail; + } + } + + if (options->ia_pd) { + struct dhcpv6_ia_pd ia_pd = { + .iaid = iface->config.dhcpv6.prefix_iaid, + }; + + if (options->iaprefix) { + memcpy(&ia_pd.iaprefix.prefix, &iface->config.dhcpv6.prefix, + sizeof(ia_pd.iaprefix.prefix)); + ia_pd.iaprefix.prefix_len = iface->config.dhcpv6.prefix_len; + } + + ret = dhcpv6_add_option_ia_pd(pkt, &ia_pd, options->iaprefix); + if (ret < 0) { + goto fail; + } + } + + for (uint8_t i = 0; i < ARRAY_SIZE(options->oro); i++) { + if (options->oro[i] == 0) { + break; + } + + oro_cnt++; + } + + if (oro_cnt > 0) { + ret = dhcpv6_add_option_oro(pkt, options->oro, oro_cnt); + if (ret < 0) { + goto fail; + } + } + + return 0; + +fail: + return ret; +} + +static struct net_pkt *dhcpv6_create_message(struct net_if *iface, + enum dhcpv6_msg_type msg_type, + struct dhcpv6_options_include *options) +{ + struct in6_addr *local_addr; + struct net_pkt *pkt; + size_t msg_size; + + local_addr = net_if_ipv6_get_ll(iface, NET_ADDR_ANY_STATE); + if (local_addr == NULL) { + NET_ERR("No LL address"); + return NULL; + } + + msg_size = dhcpv6_calculate_message_size(options); + + pkt = net_pkt_alloc_with_buffer(iface, msg_size, AF_INET6, + IPPROTO_UDP, K_FOREVER); + if (pkt == NULL) { + return NULL; + } + + if (net_ipv6_create(pkt, local_addr, &all_dhcpv6_ra_and_servers) < 0 || + net_udp_create(pkt, htons(DHCPV6_CLIENT_PORT), + htons(DHCPV6_SERVER_PORT)) < 0) { + goto fail; + } + + dhcpv6_generate_tid(iface); + + if (dhcpv6_add_header(pkt, msg_type, iface->config.dhcpv6.tid) < 0) { + goto fail; + } + + if (dhcpv6_add_options(iface, pkt, options) < 0) { + goto fail; + } + + net_pkt_cursor_init(pkt); + net_ipv6_finalize(pkt, IPPROTO_UDP); + + return pkt; + +fail: + net_pkt_unref(pkt); + + return NULL; +} + +static int dhcpv6_send_solicit(struct net_if *iface) +{ + int ret; + struct net_pkt *pkt; + struct dhcpv6_options_include options = { + .clientid = true, + .elapsed_time = true, + .ia_na = iface->config.dhcpv6.params.request_addr, + .ia_pd = iface->config.dhcpv6.params.request_prefix, + .oro = { DHCPV6_OPTION_CODE_SOL_MAX_RT }, + }; + + pkt = dhcpv6_create_message(iface, DHCPV6_MSG_TYPE_SOLICIT, &options); + if (pkt == NULL) { + return -ENOMEM; + } + + ret = net_send_data(pkt); + if (ret < 0) { + net_pkt_unref(pkt); + } + + return ret; +} + +static int dhcpv6_send_request(struct net_if *iface) +{ + int ret; + struct net_pkt *pkt; + struct dhcpv6_options_include options = { + .clientid = true, + .serverid = true, + .elapsed_time = true, + .ia_na = iface->config.dhcpv6.params.request_addr, + .ia_pd = iface->config.dhcpv6.params.request_prefix, + .oro = { DHCPV6_OPTION_CODE_SOL_MAX_RT }, + }; + + pkt = dhcpv6_create_message(iface, DHCPV6_MSG_TYPE_REQUEST, &options); + if (pkt == NULL) { + return -ENOMEM; + } + + ret = net_send_data(pkt); + if (ret < 0) { + net_pkt_unref(pkt); + } + + return ret; +} + +static int dhcpv6_send_renew(struct net_if *iface) +{ + int ret; + struct net_pkt *pkt; + struct dhcpv6_options_include options = { + .clientid = true, + .serverid = true, + .elapsed_time = true, + .ia_na = iface->config.dhcpv6.params.request_addr, + .iaaddr = iface->config.dhcpv6.params.request_addr, + .ia_pd = iface->config.dhcpv6.params.request_prefix, + .iaprefix = iface->config.dhcpv6.params.request_prefix, + .oro = { DHCPV6_OPTION_CODE_SOL_MAX_RT }, + }; + + pkt = dhcpv6_create_message(iface, DHCPV6_MSG_TYPE_RENEW, &options); + if (pkt == NULL) { + return -ENOMEM; + } + + ret = net_send_data(pkt); + if (ret < 0) { + net_pkt_unref(pkt); + } + + return ret; +} + +static int dhcpv6_send_rebind(struct net_if *iface) +{ + int ret; + struct net_pkt *pkt; + struct dhcpv6_options_include options = { + .clientid = true, + .elapsed_time = true, + .ia_na = iface->config.dhcpv6.params.request_addr, + .iaaddr = iface->config.dhcpv6.params.request_addr, + .ia_pd = iface->config.dhcpv6.params.request_prefix, + .iaprefix = iface->config.dhcpv6.params.request_prefix, + .oro = { DHCPV6_OPTION_CODE_SOL_MAX_RT }, + }; + + pkt = dhcpv6_create_message(iface, DHCPV6_MSG_TYPE_REBIND, &options); + if (pkt == NULL) { + return -ENOMEM; + } + + ret = net_send_data(pkt); + if (ret < 0) { + net_pkt_unref(pkt); + } + + return ret; +} + +static int dhcpv6_send_confirm(struct net_if *iface) +{ + int ret; + struct net_pkt *pkt; + struct dhcpv6_options_include options = { + .clientid = true, + .elapsed_time = true, + .ia_na = true, + .iaaddr = true, + }; + + pkt = dhcpv6_create_message(iface, DHCPV6_MSG_TYPE_CONFIRM, &options); + if (pkt == NULL) { + return -ENOMEM; + } + + ret = net_send_data(pkt); + if (ret < 0) { + net_pkt_unref(pkt); + } + + return ret; +} + +/* DHCPv6 packet parsing functions */ + +static int dhcpv6_parse_option_clientid(struct net_pkt *pkt, uint16_t length, + struct net_dhcpv6_duid_storage *clientid) +{ + struct net_dhcpv6_duid_raw duid; + int ret; + + if (length > sizeof(struct net_dhcpv6_duid_raw)) { + NET_ERR("DUID too large to handle"); + return -EMSGSIZE; + } + + ret = net_pkt_read(pkt, &duid, length); + if (ret < 0) { + return ret; + } + + clientid->length = length; + memcpy(&clientid->duid, &duid, length); + + return 0; +} + +static int dhcpv6_parse_option_serverid(struct net_pkt *pkt, uint16_t length, + struct net_dhcpv6_duid_storage *serverid) +{ + struct net_dhcpv6_duid_raw duid; + int ret; + + if (length > sizeof(struct net_dhcpv6_duid_raw)) { + NET_ERR("DUID too large to handle"); + return -EMSGSIZE; + } + + ret = net_pkt_read(pkt, &duid, length); + if (ret < 0) { + return ret; + } + + serverid->length = length; + memcpy(&serverid->duid, &duid, length); + + return 0; +} + +static int dhcpv6_parse_option_preference(struct net_pkt *pkt, uint16_t length, + uint8_t *preference) +{ + if (length != DHCPV6_OPTION_PREFERENCE_SIZE) { + return -EBADMSG; + } + + if (net_pkt_read_u8(pkt, preference) < 0) { + return -EBADMSG; + } + + return 0; +} + +static int dhcpv6_parse_option_status_code(struct net_pkt *pkt, + uint16_t length, uint16_t *status) +{ + int ret; + + if (length < DHCPV6_OPTION_STATUS_CODE_HEADER_SIZE) { + NET_ERR("Invalid IAADDR option size"); + return -EMSGSIZE; + } + + ret = net_pkt_read_be16(pkt, status); + if (ret < 0) { + return ret; + } + + NET_DBG("status code %d", *status); + + length -= DHCPV6_OPTION_STATUS_CODE_HEADER_SIZE; + if (length > 0) { + /* Ignore status message */ + ret = net_pkt_skip(pkt, length); + } + + return ret; +} + +static int dhcpv6_parse_option_iaaddr(struct net_pkt *pkt, uint16_t length, + struct dhcpv6_iaaddr *iaaddr) +{ + int ret; + + if (length < DHCPV6_OPTION_IAADDR_HEADER_SIZE) { + NET_ERR("Invalid IAADDR option size"); + return -EMSGSIZE; + } + + ret = net_pkt_read(pkt, &iaaddr->addr, sizeof(iaaddr->addr)); + if (ret < 0) { + return ret; + } + + ret = net_pkt_read_be32(pkt, &iaaddr->preferred_lifetime); + if (ret < 0) { + return ret; + } + + ret = net_pkt_read_be32(pkt, &iaaddr->valid_lifetime); + if (ret < 0) { + return ret; + } + + /* DHCPv6 RFC8415, ch. 21.6 The client MUST discard any addresses for + * which the preferred lifetime is greater than the valid lifetime. + */ + if (iaaddr->preferred_lifetime > iaaddr->valid_lifetime) { + return -EBADMSG; + } + + NET_DBG("addr %s preferred_lifetime %d valid_lifetime %d", + net_sprint_ipv6_addr(&iaaddr->addr), iaaddr->preferred_lifetime, + iaaddr->valid_lifetime); + + iaaddr->status = DHCPV6_STATUS_SUCCESS; + + length -= DHCPV6_OPTION_IAADDR_HEADER_SIZE; + while (length > 0) { + uint16_t code, sublen; + + ret = net_pkt_read_be16(pkt, &code); + if (ret < 0) { + return ret; + } + + ret = net_pkt_read_be16(pkt, &sublen); + if (ret < 0) { + return ret; + } + + switch (code) { + case DHCPV6_OPTION_CODE_STATUS_CODE: + ret = dhcpv6_parse_option_status_code(pkt, sublen, + &iaaddr->status); + if (ret < 0) { + return ret; + } + + break; + default: + net_pkt_skip(pkt, sublen); + NET_DBG("Unexpected option %d length %d", code, sublen); + break; + } + + length -= (sublen + 4); + } + + return 0; +} + +static int dhcpv6_parse_option_ia_na(struct net_pkt *pkt, uint16_t length, + struct dhcpv6_ia_na *ia_na) +{ + int ret; + + if (length < DHCPV6_OPTION_IA_NA_HEADER_SIZE) { + NET_ERR("Invalid IA_NA option size"); + return -EMSGSIZE; + } + + ret = net_pkt_read_be32(pkt, &ia_na->iaid); + if (ret < 0) { + return ret; + } + + ret = net_pkt_read_be32(pkt, &ia_na->t1); + if (ret < 0) { + return ret; + } + + ret = net_pkt_read_be32(pkt, &ia_na->t2); + if (ret < 0) { + return ret; + } + + /* DHCPv6 RFC8415, ch. 21.4 If a client receives an IA_NA with T1 + * greater than T2 and both T1 and T2 are greater than 0, the client + * discards the IA_NA option and processes the remainder of the message + * as though the server had not included the invalid IA_NA option. + */ + if (ia_na->t1 != 0 && ia_na->t2 != 0 && ia_na->t1 > ia_na->t2) { + return -ENOENT; + } + + NET_DBG("iaid %d t1 %d t2 %d", ia_na->iaid, ia_na->t1, ia_na->t2); + + /* In case there's no IAADDR option, make this visible be setting + * error status. If the option is present, option parser will overwrite + * the value. + */ + ia_na->iaaddr.status = DHCPV6_STATUS_NO_ADDR_AVAIL; + ia_na->status = DHCPV6_STATUS_SUCCESS; + + length -= DHCPV6_OPTION_IA_NA_HEADER_SIZE; + while (length > 0) { + uint16_t code, sublen; + + ret = net_pkt_read_be16(pkt, &code); + if (ret < 0) { + return ret; + } + + ret = net_pkt_read_be16(pkt, &sublen); + if (ret < 0) { + return ret; + } + + switch (code) { + case DHCPV6_OPTION_CODE_IAADDR: + ret = dhcpv6_parse_option_iaaddr(pkt, sublen, + &ia_na->iaaddr); + if (ret < 0) { + return ret; + } + + break; + + case DHCPV6_OPTION_CODE_STATUS_CODE: + ret = dhcpv6_parse_option_status_code(pkt, sublen, + &ia_na->status); + if (ret < 0) { + return ret; + } + + break; + + default: + net_pkt_skip(pkt, sublen); + NET_DBG("Unexpected option %d length %d", code, sublen); + break; + } + + length -= (sublen + 4); + } + + return 0; +} + +static int dhcpv6_parse_option_iaprefix(struct net_pkt *pkt, uint16_t length, + struct dhcpv6_iaprefix *iaprefix) +{ + int ret; + + if (length < DHCPV6_OPTION_IAPREFIX_HEADER_SIZE) { + NET_ERR("Invalid IAPREFIX option size"); + return -EMSGSIZE; + } + + ret = net_pkt_read_be32(pkt, &iaprefix->preferred_lifetime); + if (ret < 0) { + return ret; + } + + ret = net_pkt_read_be32(pkt, &iaprefix->valid_lifetime); + if (ret < 0) { + return ret; + } + + ret = net_pkt_read_u8(pkt, &iaprefix->prefix_len); + if (ret < 0) { + return ret; + } + + ret = net_pkt_read(pkt, &iaprefix->prefix, sizeof(iaprefix->prefix)); + if (ret < 0) { + return ret; + } + + /* DHCPv6 RFC8415, ch. 21.22 The client MUST discard any prefixes for + * which the preferred lifetime is greater than the valid lifetime. + */ + if (iaprefix->preferred_lifetime > iaprefix->valid_lifetime) { + return -EBADMSG; + } + + NET_DBG("prefix %s/%u preferred_lifetime %d valid_lifetime %d", + net_sprint_ipv6_addr(&iaprefix->prefix), iaprefix->prefix_len, + iaprefix->preferred_lifetime, iaprefix->valid_lifetime); + + iaprefix->status = DHCPV6_STATUS_SUCCESS; + + length -= DHCPV6_OPTION_IAPREFIX_HEADER_SIZE; + while (length > 0) { + uint16_t code, sublen; + + ret = net_pkt_read_be16(pkt, &code); + if (ret < 0) { + return ret; + } + + ret = net_pkt_read_be16(pkt, &sublen); + if (ret < 0) { + return ret; + } + + switch (code) { + case DHCPV6_OPTION_CODE_STATUS_CODE: + ret = dhcpv6_parse_option_status_code(pkt, sublen, + &iaprefix->status); + if (ret < 0) { + return ret; + } + + break; + default: + net_pkt_skip(pkt, sublen); + NET_DBG("Unexpected option %d length %d", code, sublen); + break; + } + + length -= (sublen + 4); + } + + return 0; +} + +static int dhcpv6_parse_option_ia_pd(struct net_pkt *pkt, uint16_t length, + struct dhcpv6_ia_pd *ia_pd) +{ + int ret; + + if (length < DHCPV6_OPTION_IA_PD_HEADER_SIZE) { + NET_ERR("Invalid IA_PD option size"); + return -EMSGSIZE; + } + + ret = net_pkt_read_be32(pkt, &ia_pd->iaid); + if (ret < 0) { + return ret; + } + + ret = net_pkt_read_be32(pkt, &ia_pd->t1); + if (ret < 0) { + return ret; + } + + ret = net_pkt_read_be32(pkt, &ia_pd->t2); + if (ret < 0) { + return ret; + } + + /* DHCPv6 RFC8415, ch. 21.21 If a client receives an IA_PD with T1 + * greater than T2 and both T1 and T2 are greater than 0, the client + * discards the IA_PD option and processes the remainder of the message + * as though the server had not included the IA_PD option. + */ + if (ia_pd->t1 != 0 && ia_pd->t2 != 0 && ia_pd->t1 > ia_pd->t2) { + return -ENOENT; + } + + NET_DBG("iaid %d t1 %d t2 %d", ia_pd->iaid, ia_pd->t1, ia_pd->t2); + + /* In case there's no IAPREFIX option, make this visible be setting + * error status. If the option is present, option parser will overwrite + * the value. + */ + ia_pd->iaprefix.status = DHCPV6_STATUS_NO_PREFIX_AVAIL; + ia_pd->status = DHCPV6_STATUS_SUCCESS; + + length -= DHCPV6_OPTION_IA_PD_HEADER_SIZE; + while (length > 0) { + uint16_t code, sublen; + + ret = net_pkt_read_be16(pkt, &code); + if (ret < 0) { + return ret; + } + + ret = net_pkt_read_be16(pkt, &sublen); + if (ret < 0) { + return ret; + } + + switch (code) { + case DHCPV6_OPTION_CODE_IAPREFIX: + ret = dhcpv6_parse_option_iaprefix(pkt, sublen, + &ia_pd->iaprefix); + if (ret < 0) { + return ret; + } + + break; + + case DHCPV6_OPTION_CODE_STATUS_CODE: + ret = dhcpv6_parse_option_status_code(pkt, sublen, + &ia_pd->status); + if (ret < 0) { + return ret; + } + + break; + default: + net_pkt_skip(pkt, sublen); + NET_DBG("Unexpected option %d length %d", code, sublen); + break; + } + + length -= (sublen + 4); + } + + return 0; +} + +static int dhcpv6_find_option(struct net_pkt *pkt, enum dhcpv6_option_code opt_code, + uint16_t *opt_len) +{ + uint16_t length; + uint16_t code; + + while (net_pkt_read_be16(pkt, &code) == 0) { + if (net_pkt_read_be16(pkt, &length) < 0) { + return -EBADMSG; + } + + if (code == opt_code) { + *opt_len = length; + return 0; + } + + net_pkt_skip(pkt, length); + } + + return -ENOENT; +} + +static int dhcpv6_find_clientid(struct net_pkt *pkt, + struct net_dhcpv6_duid_storage *clientid) +{ + struct net_pkt_cursor backup; + uint16_t length; + int ret; + + net_pkt_cursor_backup(pkt, &backup); + + ret = dhcpv6_find_option(pkt, DHCPV6_OPTION_CODE_CLIENTID, &length); + if (ret == 0) { + ret = dhcpv6_parse_option_clientid(pkt, length, clientid); + } + + net_pkt_cursor_restore(pkt, &backup); + + return ret; +} + +static int dhcpv6_find_serverid(struct net_pkt *pkt, + struct net_dhcpv6_duid_storage *serverid) +{ + struct net_pkt_cursor backup; + uint16_t length; + int ret; + + net_pkt_cursor_backup(pkt, &backup); + + ret = dhcpv6_find_option(pkt, DHCPV6_OPTION_CODE_SERVERID, &length); + if (ret == 0) { + ret = dhcpv6_parse_option_serverid(pkt, length, serverid); + } + + net_pkt_cursor_restore(pkt, &backup); + + return ret; +} + +static int dhcpv6_find_server_preference(struct net_pkt *pkt, + uint8_t *preference) +{ + struct net_pkt_cursor backup; + uint16_t length; + int ret; + + net_pkt_cursor_backup(pkt, &backup); + + ret = dhcpv6_find_option(pkt, DHCPV6_OPTION_CODE_PREFERENCE, &length); + if (ret == 0) { + ret = dhcpv6_parse_option_preference(pkt, length, preference); + } else if (ret == -ENOENT) { + /* In case no preference option is present, default to 0. + * DHCPv6 RFC8415, ch. 18.2.1. + */ + *preference = 0; + ret = 0; + } + + net_pkt_cursor_restore(pkt, &backup); + + return ret; +} + +static int dhcpv6_find_ia_na(struct net_pkt *pkt, struct dhcpv6_ia_na *ia_na) +{ + struct net_pkt_cursor backup; + uint16_t length; + int ret; + + net_pkt_cursor_backup(pkt, &backup); + + ret = dhcpv6_find_option(pkt, DHCPV6_OPTION_CODE_IA_NA, &length); + if (ret == 0) { + ret = dhcpv6_parse_option_ia_na(pkt, length, ia_na); + } + + net_pkt_cursor_restore(pkt, &backup); + + return ret; +} + +static int dhcpv6_find_ia_pd(struct net_pkt *pkt, struct dhcpv6_ia_pd *ia_pd) +{ + struct net_pkt_cursor backup; + uint16_t length; + int ret; + + net_pkt_cursor_backup(pkt, &backup); + + ret = dhcpv6_find_option(pkt, DHCPV6_OPTION_CODE_IA_PD, &length); + if (ret == 0) { + ret = dhcpv6_parse_option_ia_pd(pkt, length, ia_pd); + } + + net_pkt_cursor_restore(pkt, &backup); + + return ret; +} + +static int dhcpv6_find_status_code(struct net_pkt *pkt, uint16_t *status) +{ + struct net_pkt_cursor backup; + uint16_t length; + int ret; + + net_pkt_cursor_backup(pkt, &backup); + + ret = dhcpv6_find_option(pkt, DHCPV6_OPTION_CODE_STATUS_CODE, &length); + if (ret == 0) { + ret = dhcpv6_parse_option_status_code(pkt, length, status); + } else if (ret == -ENOENT) { + /* In case no status option is present, default to success. + * DHCPv6 RFC8415, ch. 21.13. + */ + *status = DHCPV6_STATUS_SUCCESS; + ret = 0; + } + + net_pkt_cursor_restore(pkt, &backup); + + return ret; +} + +/* DHCPv6 state changes */ + +static void dhcpv6_enter_init(struct net_if *iface) +{ + uint32_t timeout; + + /* RFC8415 requires to wait a random period up to 1 second before + * sending the initial solicit/information request/confirm. + */ + timeout = sys_rand32_get() % DHCPV6_SOL_MAX_DELAY; + + dhcpv6_set_timeout(iface, timeout); +} + +static void dhcpv6_enter_soliciting(struct net_if *iface) +{ + iface->config.dhcpv6.retransmit_timeout = + dhcpv6_initial_retransmit_time(DHCPV6_SOL_TIMEOUT); + iface->config.dhcpv6.retransmissions = 0; + iface->config.dhcpv6.server_preference = -1; + iface->config.dhcpv6.exchange_start = k_uptime_get(); + + (void)dhcpv6_send_solicit(iface); + dhcpv6_set_timeout(iface, iface->config.dhcpv6.retransmit_timeout); +} + +static void dhcpv6_enter_requesting(struct net_if *iface) +{ + iface->config.dhcpv6.retransmit_timeout = + dhcpv6_initial_retransmit_time(DHCPV6_REQ_TIMEOUT); + iface->config.dhcpv6.retransmissions = 0; + iface->config.dhcpv6.exchange_start = k_uptime_get(); + + (void)dhcpv6_send_request(iface); + dhcpv6_set_timeout(iface, iface->config.dhcpv6.retransmit_timeout); +} + +static void dhcpv6_enter_renewing(struct net_if *iface) +{ + iface->config.dhcpv6.retransmit_timeout = + dhcpv6_initial_retransmit_time(DHCPV6_REN_TIMEOUT); + iface->config.dhcpv6.retransmissions = 0; + iface->config.dhcpv6.exchange_start = k_uptime_get(); + + (void)dhcpv6_send_renew(iface); + dhcpv6_set_timeout(iface, iface->config.dhcpv6.retransmit_timeout); +} + +static void dhcpv6_enter_rebinding(struct net_if *iface) +{ + iface->config.dhcpv6.retransmit_timeout = + dhcpv6_initial_retransmit_time(DHCPV6_REB_TIMEOUT); + iface->config.dhcpv6.retransmissions = 0; + iface->config.dhcpv6.exchange_start = k_uptime_get(); + + (void)dhcpv6_send_rebind(iface); + dhcpv6_set_timeout(iface, iface->config.dhcpv6.retransmit_timeout); +} + +static void dhcpv6_enter_confirming(struct net_if *iface) +{ + iface->config.dhcpv6.retransmit_timeout = + dhcpv6_initial_retransmit_time(DHCPV6_CNF_TIMEOUT); + iface->config.dhcpv6.retransmissions = 0; + iface->config.dhcpv6.exchange_start = k_uptime_get(); + + (void)dhcpv6_send_confirm(iface); + dhcpv6_set_timeout(iface, iface->config.dhcpv6.retransmit_timeout); +} + +static void dhcpv6_enter_bound(struct net_if *iface) +{ + iface->config.dhcpv6.timeout = iface->config.dhcpv6.t1; + + net_mgmt_event_notify_with_info(NET_EVENT_IPV6_DHCP_BOUND, iface, + &iface->config.dhcpv6, + sizeof(iface->config.dhcpv6)); +} + +static void dhcpv6_enter_state(struct net_if *iface, enum net_dhcpv6_state state) +{ + iface->config.dhcpv6.state = state; + + NET_DBG("enter state=%s", + net_dhcpv6_state_name(iface->config.dhcpv6.state)); + + switch (iface->config.dhcpv6.state) { + case NET_DHCPV6_DISABLED: + break; + case NET_DHCPV6_INIT: + return dhcpv6_enter_init(iface); + case NET_DHCPV6_SOLICITING: + return dhcpv6_enter_soliciting(iface); + case NET_DHCPV6_REQUESTING: + return dhcpv6_enter_requesting(iface); + case NET_DHCPV6_CONFIRMING: + return dhcpv6_enter_confirming(iface); + case NET_DHCPV6_RENEWING: + return dhcpv6_enter_renewing(iface); + case NET_DHCPV6_REBINDING: + return dhcpv6_enter_rebinding(iface); + case NET_DHCPV6_INFO_REQUESTING: + break; + case NET_DHCPV6_BOUND: + return dhcpv6_enter_bound(iface); + } +} + +/* DHCPv6 input processing */ + +static int dhcpv6_handle_advertise(struct net_if *iface, struct net_pkt *pkt, + uint8_t *tid) +{ + struct net_dhcpv6_duid_storage duid = { 0 }; + struct dhcpv6_ia_pd ia_pd = { 0 }; + struct dhcpv6_ia_na ia_na = { 0 }; + uint8_t server_preference = 0; + uint16_t status = 0; + int ret; + + if (iface->config.dhcpv6.state != NET_DHCPV6_SOLICITING) { + return -EINVAL; + } + + /* Verify client ID. */ + ret = dhcpv6_find_clientid(pkt, &duid); + if (ret < 0) { + NET_ERR("Client ID missing"); + return ret; + } + + if (iface->config.dhcpv6.clientid.length != duid.length || + memcmp(&iface->config.dhcpv6.clientid.duid, &duid.duid, + iface->config.dhcpv6.clientid.length) != 0) { + NET_ERR("Client ID mismatch"); + return -EBADMSG; + } + + /* Verify server ID is present. */ + memset(&duid, 0, sizeof(duid)); + ret = dhcpv6_find_serverid(pkt, &duid); + if (ret < 0) { + NET_ERR("Server ID missing"); + return ret; + } + + /* Verify TID. */ + if (memcmp(iface->config.dhcpv6.tid, tid, + sizeof(iface->config.dhcpv6.tid)) != 0) { + NET_INFO("TID mismatch"); + return -EBADMSG; + } + + /* Verify status code. */ + ret = dhcpv6_find_status_code(pkt, &status); + if (ret < 0) { + return ret; + } + + if (status != DHCPV6_STATUS_SUCCESS) { + /* Ignore. */ + return 0; + } + + /* TODO Process SOL_MAX_RT/INF_MAX_RT options. */ + + /* Verify server preference. */ + ret = dhcpv6_find_server_preference(pkt, &server_preference); + if (ret < 0) { + return ret; + } + + if ((int16_t)server_preference < iface->config.dhcpv6.server_preference) { + /* Ignore. */ + return 0; + } + + /* Find/verify address. */ + if (iface->config.dhcpv6.params.request_addr) { + ret = dhcpv6_find_ia_na(pkt, &ia_na); + if (ret < 0) { + NET_ERR("Address missing"); + return ret; + } + + if (ia_na.status != DHCPV6_STATUS_SUCCESS || + ia_na.iaaddr.status != DHCPV6_STATUS_SUCCESS) { + /* Ignore. */ + return 0; + } + } + + /* Find/verify prefix. */ + if (iface->config.dhcpv6.params.request_prefix) { + ret = dhcpv6_find_ia_pd(pkt, &ia_pd); + if (ret < 0) { + NET_ERR("Prefix missing"); + return ret; + } + + if (ia_pd.status != DHCPV6_STATUS_SUCCESS || + ia_pd.iaprefix.status != DHCPV6_STATUS_SUCCESS) { + /* Ignore. */ + return 0; + } + } + + /* Valid advertisement received, store received offer. */ + memcpy(&iface->config.dhcpv6.serverid, &duid, + sizeof(iface->config.dhcpv6.serverid)); + iface->config.dhcpv6.server_preference = server_preference; + + /* DHCPv6 RFC8415, ch. 18.2.1, if client received Advertise + * message with maximum preference, or after the first + * retransmission period, it should proceed with the exchange, + * w/o further wait. + */ + if (server_preference == DHCPV6_MAX_SERVER_PREFERENCE || + iface->config.dhcpv6.retransmissions > 0) { + /* Reschedule immediately */ + dhcpv6_enter_state(iface, NET_DHCPV6_REQUESTING); + dhcpv6_reschedule(); + } + + return 0; +} + +static int dhcpv6_handle_reply(struct net_if *iface, struct net_pkt *pkt, + uint8_t *tid) +{ + struct net_dhcpv6_duid_storage duid = { 0 }; + struct dhcpv6_ia_pd ia_pd = { 0 }; + struct dhcpv6_ia_na ia_na = { 0 }; + int64_t now = k_uptime_get(); + uint16_t status = 0; + bool rediscover = false; + int ret; + + if (iface->config.dhcpv6.state != NET_DHCPV6_REQUESTING && + iface->config.dhcpv6.state != NET_DHCPV6_CONFIRMING && + iface->config.dhcpv6.state != NET_DHCPV6_RENEWING && + iface->config.dhcpv6.state != NET_DHCPV6_REBINDING) { + return -EINVAL; + } + + /* Verify client ID. */ + ret = dhcpv6_find_clientid(pkt, &duid); + if (ret < 0) { + NET_ERR("Client ID missing"); + return ret; + } + + if (iface->config.dhcpv6.clientid.length != duid.length || + memcmp(&iface->config.dhcpv6.clientid.duid, &duid.duid, + iface->config.dhcpv6.clientid.length) != 0) { + NET_ERR("Client ID mismatch"); + return -EBADMSG; + } + + /* Verify server ID is present. */ + memset(&duid, 0, sizeof(duid)); + ret = dhcpv6_find_serverid(pkt, &duid); + if (ret < 0) { + NET_ERR("Server ID missing"); + return ret; + } + + /* Verify TID. */ + if (memcmp(iface->config.dhcpv6.tid, tid, + sizeof(iface->config.dhcpv6.tid)) != 0) { + NET_INFO("TID mismatch"); + return -EBADMSG; + } + + /* TODO Process SOL_MAX_RT/INF_MAX_RT options. */ + + /* Verify status code. */ + ret = dhcpv6_find_status_code(pkt, &status); + if (ret < 0) { + return ret; + } + + if (status == DHCPV6_STATUS_UNSPEC_FAIL) { + /* Ignore and try again later. */ + return 0; + } + + /* DHCPv6 RFC8415, ch. 18.2.10.1. If the client receives a NotOnLink + * status from the server in response to (...) Request, the client can + * either reissue the message without specifying any addresses or + * restart the DHCP server discovery process. + * + * Restart discovery for our case. + */ + if (iface->config.dhcpv6.state == NET_DHCPV6_REQUESTING && + status == DHCPV6_STATUS_NOT_ON_LINK) { + rediscover = true; + goto out; + } + + /* In case of Confirm Reply, status success indicates the client can + * still use the address. + */ + if (iface->config.dhcpv6.state == NET_DHCPV6_CONFIRMING) { + if (status != DHCPV6_STATUS_SUCCESS) { + rediscover = true; + } + + goto out; + } + + /* Find/verify address. */ + if (iface->config.dhcpv6.params.request_addr) { + ret = dhcpv6_find_ia_na(pkt, &ia_na); + if (ret < 0) { + NET_ERR("Address missing"); + return ret; + } + + if (iface->config.dhcpv6.addr_iaid != ia_na.iaid) { + return -EBADMSG; + } + } + + /* Find/verify prefix. */ + if (iface->config.dhcpv6.params.request_prefix) { + ret = dhcpv6_find_ia_pd(pkt, &ia_pd); + if (ret < 0) { + NET_ERR("Prefix missing"); + return ret; + } + + if (iface->config.dhcpv6.prefix_iaid != ia_pd.iaid) { + return -EBADMSG; + } + } + + /* Valid response received, store received data. */ + iface->config.dhcpv6.t1 = UINT64_MAX; + iface->config.dhcpv6.t2 = UINT64_MAX; + iface->config.dhcpv6.expire = now; + + if (iface->config.dhcpv6.params.request_addr) { + struct net_if_addr *ifaddr; + + if (ia_na.status == DHCPV6_STATUS_NO_ADDR_AVAIL || + ia_na.iaaddr.status == DHCPV6_STATUS_NO_ADDR_AVAIL || + ia_na.iaaddr.valid_lifetime == 0) { + /* Remove old lease. */ + net_if_ipv6_addr_rm(iface, &iface->config.dhcpv6.addr); + memset(&iface->config.dhcpv6.addr, 0, sizeof(struct in6_addr)); + rediscover = true; + goto prefix; + } + + /* TODO On nobiding (renew/rebind) go to requesting */ + + if (!net_ipv6_addr_cmp(&iface->config.dhcpv6.addr, + net_ipv6_unspecified_address()) && + !net_ipv6_addr_cmp(&iface->config.dhcpv6.addr, + &ia_na.iaaddr.addr)) { + /* Remove old lease. */ + net_if_ipv6_addr_rm(iface, &iface->config.dhcpv6.addr); + } + + memcpy(&iface->config.dhcpv6.addr, &ia_na.iaaddr.addr, + sizeof(iface->config.dhcpv6.addr)); + + dhcvp6_update_deadlines(iface, now, ia_na.t1, ia_na.t2, + ia_na.iaaddr.preferred_lifetime, + ia_na.iaaddr.valid_lifetime); + + ifaddr = net_if_ipv6_addr_lookup_by_iface(iface, &ia_na.iaaddr.addr); + if (ifaddr != NULL) { + net_if_ipv6_addr_update_lifetime( + ifaddr, ia_na.iaaddr.valid_lifetime); + } else if (net_if_ipv6_addr_add(iface, &ia_na.iaaddr.addr, NET_ADDR_DHCP, + ia_na.iaaddr.valid_lifetime) == NULL) { + NET_ERR("Failed to configure DHCPv6 address"); + net_dhcpv6_stop(iface); + return -EFAULT; + } + } + +prefix: + if (iface->config.dhcpv6.params.request_prefix) { + struct net_if_ipv6_prefix *ifprefix; + + if (ia_pd.status == DHCPV6_STATUS_NO_PREFIX_AVAIL || + ia_pd.iaprefix.status == DHCPV6_STATUS_NO_PREFIX_AVAIL || + ia_pd.iaprefix.valid_lifetime == 0) { + /* Remove old lease. */ + net_if_ipv6_prefix_rm(iface, &iface->config.dhcpv6.prefix, + iface->config.dhcpv6.prefix_len); + memset(&iface->config.dhcpv6.prefix, 0, sizeof(struct in6_addr)); + iface->config.dhcpv6.prefix_len = 0; + rediscover = true; + goto out; + } + + if (!net_ipv6_addr_cmp(&iface->config.dhcpv6.prefix, + net_ipv6_unspecified_address()) && + (!net_ipv6_addr_cmp(&iface->config.dhcpv6.prefix, + &ia_pd.iaprefix.prefix) || + iface->config.dhcpv6.prefix_len != ia_pd.iaprefix.prefix_len)) { + /* Remove old lease. */ + net_if_ipv6_prefix_rm(iface, &iface->config.dhcpv6.prefix, + iface->config.dhcpv6.prefix_len); + } + + iface->config.dhcpv6.prefix_len = ia_pd.iaprefix.prefix_len; + + memcpy(&iface->config.dhcpv6.prefix, &ia_pd.iaprefix.prefix, + sizeof(iface->config.dhcpv6.prefix)); + + dhcvp6_update_deadlines(iface, now, ia_pd.t1, ia_pd.t2, + ia_pd.iaprefix.preferred_lifetime, + ia_pd.iaprefix.valid_lifetime); + + ifprefix = net_if_ipv6_prefix_lookup(iface, &ia_pd.iaprefix.prefix, + ia_pd.iaprefix.prefix_len); + if (ifprefix != NULL) { + net_if_ipv6_prefix_set_timer(ifprefix, ia_pd.iaprefix.valid_lifetime); + } else if (net_if_ipv6_prefix_add(iface, &ia_pd.iaprefix.prefix, + ia_pd.iaprefix.prefix_len, + ia_pd.iaprefix.valid_lifetime) == NULL) { + NET_ERR("Failed to configure DHCPv6 prefix"); + net_dhcpv6_stop(iface); + return -EFAULT; + } + } + +out: + if (rediscover) { + dhcpv6_enter_state(iface, NET_DHCPV6_SOLICITING); + } else { + dhcpv6_enter_state(iface, NET_DHCPV6_BOUND); + } + + dhcpv6_reschedule(); + + return 0; +} + +static int dhcpv6_handle_reconfigure(struct net_if *iface, struct net_pkt *pkt) +{ + /* Reconfigure not supported yet. */ + return -ENOTSUP; +} + +static enum net_verdict dhcpv6_input(struct net_conn *conn, + struct net_pkt *pkt, + union net_ip_header *ip_hdr, + union net_proto_header *proto_hdr, + void *user_data) +{ + struct net_if *iface; + uint8_t msg_type; + uint8_t tid[DHCPV6_TID_SIZE]; + int ret; + + if (!conn) { + NET_ERR("Invalid connection"); + return NET_DROP; + } + + if (!pkt) { + NET_ERR("Invalid packet"); + return NET_DROP; + } + + iface = net_pkt_iface(pkt); + if (!iface) { + NET_ERR("No interface"); + return NET_DROP; + } + + net_pkt_cursor_init(pkt); + + if (net_pkt_skip(pkt, NET_IPV6UDPH_LEN)) { + NET_ERR("Missing IPv6/UDP header"); + return NET_DROP; + } + + if (net_pkt_read_u8(pkt, &msg_type) < 0) { + NET_ERR("Missing message type"); + return NET_DROP; + } + + if (net_pkt_read(pkt, tid, sizeof(tid)) < 0) { + NET_ERR("Missing transaction ID"); + return NET_DROP; + } + + NET_DBG("Received DHCPv6 packet [type=%d, tid=0x%02x%02x%02x]", + msg_type, tid[0], tid[1], tid[2]); + + switch (msg_type) { + case DHCPV6_MSG_TYPE_ADVERTISE: + ret = dhcpv6_handle_advertise(iface, pkt, tid); + break; + case DHCPV6_MSG_TYPE_REPLY: + ret = dhcpv6_handle_reply(iface, pkt, tid); + break; + case DHCPV6_MSG_TYPE_RECONFIGURE: + ret = dhcpv6_handle_reconfigure(iface, pkt); + break; + case DHCPV6_MSG_TYPE_SOLICIT: + case DHCPV6_MSG_TYPE_REQUEST: + case DHCPV6_MSG_TYPE_CONFIRM: + case DHCPV6_MSG_TYPE_RENEW: + case DHCPV6_MSG_TYPE_REBIND: + case DHCPV6_MSG_TYPE_RELEASE: + case DHCPV6_MSG_TYPE_DECLINE: + case DHCPV6_MSG_TYPE_INFORMATION_REQUEST: + case DHCPV6_MSG_TYPE_RELAY_FORW: + case DHCPV6_MSG_TYPE_RELAY_REPL: + default: + goto drop; + } + + if (ret < 0) { + goto drop; + } + + net_pkt_unref(pkt); + + return NET_OK; + +drop: + return NET_DROP; +} + +/* DHCPv6 timer management */ + +static uint64_t dhcpv6_timeleft(struct net_if *iface, int64_t now) +{ + uint64_t timeout = iface->config.dhcpv6.timeout; + + if (timeout > now) { + return timeout - now; + } + + return 0; +} + +static uint64_t dhcpv6_manage_timers(struct net_if *iface, int64_t now) +{ + uint64_t timeleft = dhcpv6_timeleft(iface, now); + + NET_DBG("iface %p state=%s timeleft=%llu", iface, + net_dhcpv6_state_name(iface->config.dhcpv6.state), timeleft); + + if (timeleft != 0U) { + return iface->config.dhcpv6.timeout; + } + + if (!net_if_is_up(iface)) { + /* An interface is down, the registered event handler will + * restart DHCP procedure when the interface is back up. + */ + return UINT64_MAX; + } + + switch (iface->config.dhcpv6.state) { + case NET_DHCPV6_DISABLED: + break; + case NET_DHCPV6_INIT: { + bool have_addr = false; + bool have_prefix = false; + + if (iface->config.dhcpv6.params.request_addr && + !net_ipv6_addr_cmp(&iface->config.dhcpv6.addr, + net_ipv6_unspecified_address())) { + have_addr = true; + } + + if (iface->config.dhcpv6.params.request_prefix && + !net_ipv6_addr_cmp(&iface->config.dhcpv6.prefix, + net_ipv6_unspecified_address())) { + have_prefix = true; + } + + if ((have_addr || have_prefix) && now < iface->config.dhcpv6.expire) { + /* Try to confirm the address/prefix. In case + * prefix is requested, Rebind is used with + * Confirm timings. + */ + iface->config.dhcpv6.expire = now + DHCPV6_CNF_MAX_RD; + + if (!iface->config.dhcpv6.params.request_prefix) { + dhcpv6_enter_state(iface, NET_DHCPV6_CONFIRMING); + } else { + dhcpv6_enter_state(iface, NET_DHCPV6_REBINDING); + } + } else { + dhcpv6_enter_state(iface, NET_DHCPV6_SOLICITING); + } + + return iface->config.dhcpv6.timeout; + } + case NET_DHCPV6_SOLICITING: + if (iface->config.dhcpv6.server_preference >= 0) { + dhcpv6_enter_state(iface, NET_DHCPV6_REQUESTING); + return iface->config.dhcpv6.timeout; + } + + iface->config.dhcpv6.retransmissions++; + iface->config.dhcpv6.retransmit_timeout = + dhcpv6_next_retransmit_time( + iface->config.dhcpv6.retransmit_timeout, + DHCPV6_SOL_MAX_RT); + + (void)dhcpv6_send_solicit(iface); + dhcpv6_set_timeout(iface, iface->config.dhcpv6.retransmit_timeout); + + return iface->config.dhcpv6.timeout; + case NET_DHCPV6_REQUESTING: + if (iface->config.dhcpv6.retransmissions >= DHCPV6_REQ_MAX_RC) { + /* Back to soliciting. */ + dhcpv6_enter_state(iface, NET_DHCPV6_SOLICITING); + return iface->config.dhcpv6.timeout; + } + + iface->config.dhcpv6.retransmissions++; + iface->config.dhcpv6.retransmit_timeout = + dhcpv6_next_retransmit_time( + iface->config.dhcpv6.retransmit_timeout, + DHCPV6_REQ_MAX_RT); + + (void)dhcpv6_send_request(iface); + dhcpv6_set_timeout(iface, iface->config.dhcpv6.retransmit_timeout); + + return iface->config.dhcpv6.timeout; + case NET_DHCPV6_CONFIRMING: + if (now >= iface->config.dhcpv6.expire) { + dhcpv6_enter_state(iface, NET_DHCPV6_SOLICITING); + return iface->config.dhcpv6.timeout; + } + + iface->config.dhcpv6.retransmissions++; + iface->config.dhcpv6.retransmit_timeout = + dhcpv6_next_retransmit_time( + iface->config.dhcpv6.retransmit_timeout, + DHCPV6_CNF_MAX_RT); + + (void)dhcpv6_send_confirm(iface); + dhcpv6_set_timeout(iface, iface->config.dhcpv6.retransmit_timeout); + + if (iface->config.dhcpv6.timeout > iface->config.dhcpv6.expire) { + iface->config.dhcpv6.timeout = iface->config.dhcpv6.expire; + } + + return iface->config.dhcpv6.timeout; + case NET_DHCPV6_RENEWING: + if (now >= iface->config.dhcpv6.t2) { + dhcpv6_enter_state(iface, NET_DHCPV6_REBINDING); + return iface->config.dhcpv6.timeout; + } + + iface->config.dhcpv6.retransmissions++; + iface->config.dhcpv6.retransmit_timeout = + dhcpv6_next_retransmit_time( + iface->config.dhcpv6.retransmit_timeout, + DHCPV6_REN_MAX_RT); + + (void)dhcpv6_send_renew(iface); + dhcpv6_set_timeout(iface, iface->config.dhcpv6.retransmit_timeout); + + if (iface->config.dhcpv6.timeout > iface->config.dhcpv6.t2) { + iface->config.dhcpv6.timeout = iface->config.dhcpv6.t2; + } + + return iface->config.dhcpv6.timeout; + case NET_DHCPV6_REBINDING: + if (now >= iface->config.dhcpv6.expire) { + dhcpv6_enter_state(iface, NET_DHCPV6_SOLICITING); + return iface->config.dhcpv6.timeout; + } + + iface->config.dhcpv6.retransmissions++; + iface->config.dhcpv6.retransmit_timeout = + dhcpv6_next_retransmit_time( + iface->config.dhcpv6.retransmit_timeout, + DHCPV6_REB_MAX_RT); + + (void)dhcpv6_send_rebind(iface); + dhcpv6_set_timeout(iface, iface->config.dhcpv6.retransmit_timeout); + + if (iface->config.dhcpv6.timeout > iface->config.dhcpv6.expire) { + iface->config.dhcpv6.timeout = iface->config.dhcpv6.expire; + } + + return iface->config.dhcpv6.timeout; + case NET_DHCPV6_INFO_REQUESTING: + break; + case NET_DHCPV6_BOUND: + dhcpv6_enter_state(iface, NET_DHCPV6_RENEWING); + return iface->config.dhcpv6.timeout; + } + + return UINT64_MAX; +} + +static void dhcpv6_timeout(struct k_work *work) +{ + uint64_t timeout_update = UINT64_MAX; + int64_t now = k_uptime_get(); + struct net_if_dhcpv6 *current, *next; + + ARG_UNUSED(work); + + k_mutex_lock(&lock, K_FOREVER); + + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&dhcpv6_ifaces, current, next, node) { + struct net_if *iface = CONTAINER_OF( + CONTAINER_OF(current, struct net_if_config, dhcpv6), + struct net_if, config); + uint64_t next_timeout; + + next_timeout = dhcpv6_manage_timers(iface, now); + if (next_timeout < timeout_update) { + timeout_update = next_timeout; + } + } + + k_mutex_unlock(&lock); + + if (timeout_update != UINT64_MAX) { + if (now > timeout_update) { + timeout_update = 0ULL; + } else { + timeout_update -= now; + } + + NET_DBG("Waiting for %llums", timeout_update); + k_work_reschedule(&dhcpv6_timeout_work, K_MSEC(timeout_update)); + } +} + +static void dhcpv6_iface_event_handler(struct net_mgmt_event_callback *cb, + uint32_t mgmt_event, struct net_if *iface) +{ + sys_snode_t *node = NULL; + + k_mutex_lock(&lock, K_FOREVER); + + SYS_SLIST_FOR_EACH_NODE(&dhcpv6_ifaces, node) { + if (node == &iface->config.dhcpv6.node) { + break; + } + } + + if (node == NULL) { + goto out; + } + + if (mgmt_event == NET_EVENT_IF_DOWN) { + NET_DBG("Interface %p going down", iface); + dhcpv6_set_timeout(iface, UINT64_MAX); + } else if (mgmt_event == NET_EVENT_IF_UP) { + NET_DBG("Interface %p coming up", iface); + dhcpv6_enter_state(iface, NET_DHCPV6_INIT); + } + + dhcpv6_reschedule(); + +out: + k_mutex_unlock(&lock); +} + +static void dhcpv6_generate_client_duid(struct net_if *iface) +{ + struct net_linkaddr *lladdr = net_if_get_link_addr(iface); + struct net_dhcpv6_duid_storage *clientid = &iface->config.dhcpv6.clientid; + struct dhcpv6_duid_ll *duid_ll = + (struct dhcpv6_duid_ll *)&clientid->duid.buf; + + memset(clientid, 0, sizeof(*clientid)); + + UNALIGNED_PUT(htons(DHCPV6_DUID_TYPE_LL), &clientid->duid.type); + UNALIGNED_PUT(htons(DHCPV6_HARDWARE_ETHERNET_TYPE), &duid_ll->hw_type); + memcpy(duid_ll->ll_addr, lladdr->addr, lladdr->len); + + clientid->length = DHCPV6_DUID_LL_HEADER_SIZE + lladdr->len; +} + +/* DHCPv6 public API */ + +void net_dhcpv6_start(struct net_if *iface, struct net_dhcpv6_params *params) +{ + k_mutex_lock(&lock, K_FOREVER); + + if (iface->config.dhcpv6.state != NET_DHCPV6_DISABLED) { + NET_ERR("DHCPv6 already running on iface %p, state %s", iface, + net_dhcpv6_state_name(iface->config.dhcpv6.state)); + goto out; + } + + if (!params->request_addr && !params->request_prefix) { + NET_ERR("Information Request not supported yet"); + goto out; + } + + net_mgmt_event_notify(NET_EVENT_IPV6_DHCP_START, iface); + + NET_DBG("Starting DHCPv6 on iface %p", iface); + + iface->config.dhcpv6.params = *params; + + if (sys_slist_is_empty(&dhcpv6_ifaces)) { + net_mgmt_add_event_callback(&dhcpv6_mgmt_cb); + } + + sys_slist_append(&dhcpv6_ifaces, &iface->config.dhcpv6.node); + + if (params->request_addr) { + iface->config.dhcpv6.addr_iaid = net_if_get_by_iface(iface); + } + + if (params->request_prefix) { + iface->config.dhcpv6.prefix_iaid = net_if_get_by_iface(iface); + } + + dhcpv6_generate_client_duid(iface); + dhcpv6_enter_state(iface, NET_DHCPV6_INIT); + dhcpv6_reschedule(); + +out: + k_mutex_unlock(&lock); +} + +void net_dhcpv6_stop(struct net_if *iface) +{ + k_mutex_lock(&lock, K_FOREVER); + + switch (iface->config.dhcpv6.state) { + case NET_DHCPV6_DISABLED: + NET_INFO("DHCPv6 already disabled on iface %p", iface); + break; + + case NET_DHCPV6_INIT: + case NET_DHCPV6_SOLICITING: + case NET_DHCPV6_REQUESTING: + case NET_DHCPV6_CONFIRMING: + case NET_DHCPV6_RENEWING: + case NET_DHCPV6_REBINDING: + case NET_DHCPV6_INFO_REQUESTING: + case NET_DHCPV6_BOUND: + NET_DBG("Stopping DHCPv6 on iface %p, state %s", iface, + net_dhcpv6_state_name(iface->config.dhcpv6.state)); + + (void)dhcpv6_enter_state(iface, NET_DHCPV6_DISABLED); + + sys_slist_find_and_remove(&dhcpv6_ifaces, + &iface->config.dhcpv6.node); + + if (sys_slist_is_empty(&dhcpv6_ifaces)) { + (void)k_work_cancel_delayable(&dhcpv6_timeout_work); + net_mgmt_del_event_callback(&dhcpv6_mgmt_cb); + } + + break; + } + + net_mgmt_event_notify(NET_EVENT_IPV6_DHCP_STOP, iface); + + k_mutex_unlock(&lock); +} + +void net_dhcpv6_restart(struct net_if *iface) +{ + struct net_dhcpv6_params params = iface->config.dhcpv6.params; + + net_dhcpv6_stop(iface); + net_dhcpv6_start(iface, ¶ms); +} + +int net_dhcpv6_init(void) +{ + struct sockaddr unspec_addr; + int ret; + + net_ipaddr_copy(&net_sin6(&unspec_addr)->sin6_addr, + net_ipv6_unspecified_address()); + unspec_addr.sa_family = AF_INET6; + + ret = net_udp_register(AF_INET6, NULL, &unspec_addr, + DHCPV6_SERVER_PORT, DHCPV6_CLIENT_PORT, + NULL, dhcpv6_input, NULL, NULL); + if (ret < 0) { + NET_DBG("UDP callback registration failed"); + return ret; + } + + k_work_init_delayable(&dhcpv6_timeout_work, dhcpv6_timeout); + net_mgmt_init_event_callback(&dhcpv6_mgmt_cb, dhcpv6_iface_event_handler, + NET_EVENT_IF_DOWN | NET_EVENT_IF_UP); + + return 0; +} diff --git a/subsys/net/ip/dhcpv6_internal.h b/subsys/net/ip/dhcpv6_internal.h new file mode 100644 index 00000000000..5ec129e601f --- /dev/null +++ b/subsys/net/ip/dhcpv6_internal.h @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** @file + * @brief DHCPv6 internal header + * + * This header should not be included by the application. + */ + +#ifndef DHCPV6_INTERNAL_H_ +#define DHCPV6_INTERNAL_H_ + +#include + +#define DHCPV6_DUID_TYPE_SIZE 2 +#define DHVPV6_DUID_LL_HW_TYPE_SIZE 2 +#define DHCPV6_DUID_LL_HEADER_SIZE (DHCPV6_DUID_TYPE_SIZE + \ + DHVPV6_DUID_LL_HW_TYPE_SIZE) + +#define DHCPV6_MSG_TYPE_SIZE 1 +#define DHCPV6_HEADER_SIZE (DHCPV6_MSG_TYPE_SIZE + DHCPV6_TID_SIZE) + +#define DHCPV6_OPTION_CODE_SIZE 2 +#define DHCPV6_OPTION_LENGTH_SIZE 2 +#define DHCPV6_OPTION_HEADER_SIZE (DHCPV6_OPTION_CODE_SIZE + \ + DHCPV6_OPTION_LENGTH_SIZE) + +#define DHCPV6_OPTION_PREFERENCE_SIZE 1 +#define DHCPV6_OPTION_ELAPSED_TIME_SIZE 2 +#define DHCPV6_OPTION_ELAPSED_TIME_SIZE 2 +#define DHCPV6_OPTION_IA_NA_HEADER_SIZE 12 +#define DHCPV6_OPTION_IAADDR_HEADER_SIZE 24 +#define DHCPV6_OPTION_IA_PD_HEADER_SIZE 12 +#define DHCPV6_OPTION_IAPREFIX_HEADER_SIZE 25 +#define DHCPV6_OPTION_IAADDR_HEADER_SIZE 24 +#define DHCPV6_OPTION_IAPREFIX_HEADER_SIZE 25 +#define DHCPV6_OPTION_STATUS_CODE_HEADER_SIZE 2 + +#define DHCPV6_INFINITY UINT32_MAX +#define DHCPV6_MAX_SERVER_PREFERENCE 255 + +#define DHCPV6_HARDWARE_ETHERNET_TYPE 1 + +#define DHCPV6_CLIENT_PORT 546 +#define DHCPV6_SERVER_PORT 547 + +/* DHCPv6 Transmission/retransmission timeouts */ +#define DHCPV6_SOL_MAX_DELAY 1000 /* Max delay of first Solicit, milliseconds */ +#define DHCPV6_SOL_TIMEOUT 1000 /* Initial Solicit timeout, milliseconds */ +#define DHCPV6_SOL_MAX_RT 3600000 /* Max Solicit timeout value, milliseconds */ +#define DHCPV6_REQ_TIMEOUT 1000 /* Initial Request timeout, milliseconds */ +#define DHCPV6_REQ_MAX_RT 30000 /* Max Request timeout value, milliseconds */ +#define DHCPV6_REQ_MAX_RC 10 /* Max Request retry attempts */ +#define DHCPV6_CNF_MAX_DELAY 1000 /* Max delay of first Confirm, milliseconds */ +#define DHCPV6_CNF_TIMEOUT 1000 /* Initial Confirm timeout, milliseconds */ +#define DHCPV6_CNF_MAX_RT 4000 /* Max Confirm timeout, milliseconds */ +#define DHCPV6_CNF_MAX_RD 10000 /* Max Confirm duration, milliseconds */ +#define DHCPV6_REN_TIMEOUT 10000 /* Initial Renew timeout, milliseconds */ +#define DHCPV6_REN_MAX_RT 600000 /* Max Renew timeout value, milliseconds */ +#define DHCPV6_REB_TIMEOUT 10000 /* Initial Rebind timeout, milliseconds */ +#define DHCPV6_REB_MAX_RT 600000 /* Max Rebind timeout value, milliseconds */ + +/* DUID structures */ +struct dhcpv6_duid_llt { + uint16_t hw_type; + uint32_t time; + uint8_t ll_addr[]; +} __packed; + +struct dhcpv6_duid_en { + uint32_t enterprise_number; + uint8_t identifier[]; +} __packed; + +struct dhcpv6_duid_ll { + uint16_t hw_type; + uint8_t ll_addr[]; +} __packed; + +struct dhcpv6_duid_uuid { + uint8_t uuid[16]; +} __packed; + +struct dhcpv6_msg_hdr { + uint8_t type; /* Message type */ + uint8_t tid[3]; /* Transaction ID */ +} __packed; + +struct dhcpv6_iaaddr { + uint32_t preferred_lifetime; + uint32_t valid_lifetime; + struct in6_addr addr; + uint16_t status; +}; + +struct dhcpv6_ia_na { + uint32_t iaid; + uint32_t t1; + uint32_t t2; + uint16_t status; + struct dhcpv6_iaaddr iaaddr; +}; + +struct dhcpv6_iaprefix { + uint32_t preferred_lifetime; + uint32_t valid_lifetime; + struct in6_addr prefix; + uint8_t prefix_len; + uint16_t status; +}; + +struct dhcpv6_ia_pd { + uint32_t iaid; + uint32_t t1; + uint32_t t2; + uint16_t status; + struct dhcpv6_iaprefix iaprefix; +}; + +/* DHCPv6 message types, RFC8415, ch. 7.3. */ +enum dhcpv6_msg_type { + DHCPV6_MSG_TYPE_SOLICIT = 1, + DHCPV6_MSG_TYPE_ADVERTISE = 2, + DHCPV6_MSG_TYPE_REQUEST = 3, + DHCPV6_MSG_TYPE_CONFIRM = 4, + DHCPV6_MSG_TYPE_RENEW = 5, + DHCPV6_MSG_TYPE_REBIND = 6, + DHCPV6_MSG_TYPE_REPLY = 7, + DHCPV6_MSG_TYPE_RELEASE = 8, + DHCPV6_MSG_TYPE_DECLINE = 9, + DHCPV6_MSG_TYPE_RECONFIGURE = 10, + DHCPV6_MSG_TYPE_INFORMATION_REQUEST = 11, + DHCPV6_MSG_TYPE_RELAY_FORW = 12, + DHCPV6_MSG_TYPE_RELAY_REPL = 13, +}; + +/* DHCPv6 option codes, RFC8415, ch. 21. */ +enum dhcpv6_option_code { + DHCPV6_OPTION_CODE_CLIENTID = 1, + DHCPV6_OPTION_CODE_SERVERID = 2, + DHCPV6_OPTION_CODE_IA_NA = 3, + DHCPV6_OPTION_CODE_IA_TA = 4, + DHCPV6_OPTION_CODE_IAADDR = 5, + DHCPV6_OPTION_CODE_ORO = 6, + DHCPV6_OPTION_CODE_PREFERENCE = 7, + DHCPV6_OPTION_CODE_ELAPSED_TIME = 8, + DHCPV6_OPTION_CODE_RELAY_MSG = 9, + DHCPV6_OPTION_CODE_AUTH = 11, + DHCPV6_OPTION_CODE_UNICAST = 12, + DHCPV6_OPTION_CODE_STATUS_CODE = 13, + DHCPV6_OPTION_CODE_RAPID_COMMIT = 14, + DHCPV6_OPTION_CODE_USER_CLASS = 15, + DHCPV6_OPTION_CODE_VENDOR_CLASS = 16, + DHCPV6_OPTION_CODE_VENDOR_OPTS = 17, + DHCPV6_OPTION_CODE_INTERFACE_ID = 18, + DHCPV6_OPTION_CODE_RECONF_MSG = 19, + DHCPV6_OPTION_CODE_RECONF_ACCEPT = 20, + DHCPV6_OPTION_CODE_IA_PD = 25, + DHCPV6_OPTION_CODE_IAPREFIX = 26, + DHCPV6_OPTION_CODE_INFORMATION_REFRESH_TIME = 32, + DHCPV6_OPTION_CODE_SOL_MAX_RT = 82, + DHCPV6_OPTION_CODE_INF_MAX_RT = 83, +}; + +/* DHCPv6 option codes, RFC8415, ch. 21.13. */ +enum dhcpv6_status_code { + DHCPV6_STATUS_SUCCESS = 0, + DHCPV6_STATUS_UNSPEC_FAIL = 1, + DHCPV6_STATUS_NO_ADDR_AVAIL = 2, + DHCPV6_STATUS_NO_BINDING = 3, + DHCPV6_STATUS_NOT_ON_LINK = 4, + DHCPV6_STATUS_USE_MULTICAST = 5, + DHCPV6_STATUS_NO_PREFIX_AVAIL = 6, +}; + +/* DHCPv6 Unique Identifier types, RFC8415, ch. 11.1. */ +enum dhcpv6_duid_type { + DHCPV6_DUID_TYPE_LLT = 1, /* Based on Link-Layer Address Plus Time */ + DHCPV6_DUID_TYPE_EN = 2, /* Assigned by Vendor Based on Enterprise Number */ + DHCPV6_DUID_TYPE_LL = 3, /* Based on Link-Layer Address */ + DHCPV6_DUID_TYPE_UUID = 4, /* Based on Universally Unique Identifier */ +}; + +#if defined(CONFIG_NET_DHCPV6) +int net_dhcpv6_init(void); +#else +static inline int net_dhcpv6_init(void) +{ + return 0; +} +#endif /* CONFIG_NET_DHCPV6 */ + +#endif /* DHCPV6_INTERNAL_H_ */ diff --git a/subsys/net/ip/icmpv4.c b/subsys/net/ip/icmpv4.c index 5c3d844be8a..ed69903bd8e 100644 --- a/subsys/net/ip/icmpv4.c +++ b/subsys/net/ip/icmpv4.c @@ -507,6 +507,7 @@ int net_icmpv4_send_echo_request(struct net_if *iface, uint16_t identifier, uint16_t sequence, uint8_t tos, + int priority, const void *data, size_t data_size) { @@ -537,8 +538,19 @@ int net_icmpv4_send_echo_request(struct net_if *iface, return -ENOMEM; } - net_pkt_set_ip_dscp(pkt, net_ipv4_get_dscp(tos)); - net_pkt_set_ip_ecn(pkt, net_ipv4_get_ecn(tos)); +#if !defined(CONFIG_NET_ALLOW_ANY_PRIORITY) + if (priority > NET_MAX_PRIORITIES) { + NET_ERR("Priority %d is too large, maximum is %d", + priority, NET_MAX_PRIORITIES); + return -EINVAL; + } +#endif /* !CONFIG_NET_ALLOW_ANY_PRIORITY */ + if (priority < 0) { + net_pkt_set_ip_dscp(pkt, net_ipv4_get_dscp(tos)); + net_pkt_set_ip_ecn(pkt, net_ipv4_get_ecn(tos)); + } else { + net_pkt_set_priority(pkt, priority); + } if (net_ipv4_create(pkt, src, dst) || icmpv4_create(pkt, NET_ICMPV4_ECHO_REQUEST, 0)) { diff --git a/subsys/net/ip/icmpv4.h b/subsys/net/ip/icmpv4.h index bfce2e8d9e6..492fc6e84a0 100644 --- a/subsys/net/ip/icmpv4.h +++ b/subsys/net/ip/icmpv4.h @@ -82,6 +82,7 @@ int net_icmpv4_send_echo_request(struct net_if *iface, uint16_t identifier, uint16_t sequence, uint8_t tos, + int priority, const void *data, size_t data_size); #else @@ -90,6 +91,7 @@ static inline int net_icmpv4_send_echo_request(struct net_if *iface, uint16_t identifier, uint16_t sequence, uint8_t tos, + int priority, const void *data, size_t data_size) { @@ -98,6 +100,7 @@ static inline int net_icmpv4_send_echo_request(struct net_if *iface, ARG_UNUSED(identifier); ARG_UNUSED(sequence); ARG_UNUSED(tos); + ARG_UNUSED(priority); ARG_UNUSED(data); ARG_UNUSED(data_size); diff --git a/subsys/net/ip/icmpv6.c b/subsys/net/ip/icmpv6.c index 95252a7c156..9abc034e1fb 100644 --- a/subsys/net/ip/icmpv6.c +++ b/subsys/net/ip/icmpv6.c @@ -341,6 +341,7 @@ int net_icmpv6_send_echo_request(struct net_if *iface, uint16_t identifier, uint16_t sequence, uint8_t tc, + int priority, const void *data, size_t data_size) { @@ -362,8 +363,20 @@ int net_icmpv6_send_echo_request(struct net_if *iface, return -ENOMEM; } - net_pkt_set_ip_dscp(pkt, net_ipv6_get_dscp(tc)); - net_pkt_set_ip_ecn(pkt, net_ipv6_get_ecn(tc)); +#if !defined(CONFIG_NET_ALLOW_ANY_PRIORITY) + if (priority > NET_MAX_PRIORITIES) { + NET_ERR("Priority %d is too large, maximum is %d", + priority, NET_MAX_PRIORITIES); + return -EINVAL; + } +#endif /* !CONFIG_NET_ALLOW_ANY_PRIORITY */ + + if (priority < 0) { + net_pkt_set_ip_dscp(pkt, net_ipv6_get_dscp(tc)); + net_pkt_set_ip_ecn(pkt, net_ipv6_get_ecn(tc)); + } else { + net_pkt_set_priority(pkt, priority); + } if (net_ipv6_create(pkt, src, dst) || net_icmpv6_create(pkt, NET_ICMPV6_ECHO_REQUEST, 0)) { diff --git a/subsys/net/ip/icmpv6.h b/subsys/net/ip/icmpv6.h index f0c586a7cf8..d71d9959c14 100644 --- a/subsys/net/ip/icmpv6.h +++ b/subsys/net/ip/icmpv6.h @@ -222,6 +222,7 @@ int net_icmpv6_send_echo_request(struct net_if *iface, uint16_t identifier, uint16_t sequence, uint8_t tc, + int priority, const void *data, size_t data_size); #else @@ -230,6 +231,7 @@ static inline int net_icmpv6_send_echo_request(struct net_if *iface, uint16_t identifier, uint16_t sequence, uint8_t tc, + int priority, const void *data, size_t data_size) { @@ -238,6 +240,7 @@ static inline int net_icmpv6_send_echo_request(struct net_if *iface, ARG_UNUSED(identifier); ARG_UNUSED(sequence); ARG_UNUSED(tc); + ARG_UNUSED(priority); ARG_UNUSED(data); ARG_UNUSED(data_size); diff --git a/subsys/net/ip/net_context.c b/subsys/net/ip/net_context.c index 1372398185a..6af67103721 100644 --- a/subsys/net/ip/net_context.c +++ b/subsys/net/ip/net_context.c @@ -1480,7 +1480,7 @@ static int context_sendto(struct net_context *context, { const struct msghdr *msghdr = NULL; struct net_if *iface; - struct net_pkt *pkt; + struct net_pkt *pkt = NULL; size_t tmp_len; int ret; @@ -1689,6 +1689,15 @@ static int context_sendto(struct net_context *context, return -ENETDOWN; } + context->send_cb = cb; + context->user_data = user_data; + + if (IS_ENABLED(CONFIG_NET_TCP) && + net_context_get_proto(context) == IPPROTO_TCP && + !net_if_is_ip_offloaded(net_context_get_iface(context))) { + goto skip_alloc; + } + pkt = context_alloc_pkt(context, len, PKT_WAIT_TIME); if (!pkt) { NET_ERR("Failed to allocate net_pkt"); @@ -1707,9 +1716,6 @@ static int context_sendto(struct net_context *context, len = tmp_len; } - context->send_cb = cb; - context->user_data = user_data; - if (IS_ENABLED(CONFIG_NET_CONTEXT_PRIORITY)) { uint8_t priority; @@ -1731,6 +1737,7 @@ static int context_sendto(struct net_context *context, } } +skip_alloc: if (IS_ENABLED(CONFIG_NET_OFFLOAD) && net_if_is_ip_offloaded(net_context_get_iface(context))) { ret = context_write_data(pkt, buf, len, msghdr); @@ -1762,16 +1769,12 @@ static int context_sendto(struct net_context *context, } else if (IS_ENABLED(CONFIG_NET_TCP) && net_context_get_proto(context) == IPPROTO_TCP) { - ret = context_write_data(pkt, buf, len, msghdr); + ret = net_tcp_queue(context, buf, len, msghdr); if (ret < 0) { goto fail; } - net_pkt_cursor_init(pkt); - ret = net_tcp_queue_data(context, pkt); - if (ret < 0) { - goto fail; - } + len = ret; ret = net_tcp_send_data(context, cb, user_data); } else if (IS_ENABLED(CONFIG_NET_SOCKETS_PACKET) && @@ -1827,7 +1830,9 @@ static int context_sendto(struct net_context *context, return len; fail: - net_pkt_unref(pkt); + if (pkt != NULL) { + net_pkt_unref(pkt); + } return ret; } diff --git a/subsys/net/ip/net_core.c b/subsys/net/ip/net_core.c index 00829dfcf90..1f6dac720af 100644 --- a/subsys/net/ip/net_core.c +++ b/subsys/net/ip/net_core.c @@ -45,6 +45,7 @@ LOG_MODULE_REGISTER(net_core, CONFIG_NET_CORE_LOG_LEVEL); #include "ipv4.h" #include "dhcpv4.h" +#include "dhcpv6_internal.h" #include "route.h" @@ -210,6 +211,15 @@ static void init_rx_queues(void) /* Check if the IPv{4|6} addresses are proper. As this can be expensive, * make this optional. */ + +static inline void copy_ll_addr(struct net_pkt *pkt) +{ + memcpy(net_pkt_lladdr_src(pkt), net_pkt_lladdr_if(pkt), + sizeof(struct net_linkaddr)); + memcpy(net_pkt_lladdr_dst(pkt), net_pkt_lladdr_if(pkt), + sizeof(struct net_linkaddr)); +} + static inline int check_ip_addr(struct net_pkt *pkt) { uint8_t family = net_pkt_family(pkt); @@ -238,6 +248,9 @@ static inline int check_ip_addr(struct net_pkt *pkt) NET_IPV6_HDR(pkt)->dst); net_ipv6_addr_copy_raw(NET_IPV6_HDR(pkt)->dst, (uint8_t *)&addr); + net_pkt_set_ll_proto_type(pkt, ETH_P_IPV6); + copy_ll_addr(pkt); + return 1; } @@ -285,6 +298,9 @@ static inline int check_ip_addr(struct net_pkt *pkt) NET_IPV4_HDR(pkt)->dst); net_ipv4_addr_copy_raw(NET_IPV4_HDR(pkt)->dst, (uint8_t *)&addr); + net_pkt_set_ll_proto_type(pkt, ETH_P_IP); + copy_ll_addr(pkt); + return 1; } @@ -475,6 +491,11 @@ static inline int services_init(void) return status; } + status = net_dhcpv6_init(); + if (status != 0) { + return status; + } + dns_init_resolver(); websocket_init(); diff --git a/subsys/net/ip/net_if.c b/subsys/net/ip/net_if.c index e6e3512e4b2..026eaa88b48 100644 --- a/subsys/net/ip/net_if.c +++ b/subsys/net/ip/net_if.c @@ -265,7 +265,9 @@ static bool net_if_tx(struct net_if *iface, struct net_pkt *pkt) } } + net_if_tx_lock(iface); status = net_if_l2(iface)->send(iface, pkt); + net_if_tx_unlock(iface); if (IS_ENABLED(CONFIG_NET_PKT_TXTIME_STATS)) { uint32_t end_tick = k_cycle_get_32(); @@ -437,6 +439,7 @@ static inline void init_iface(struct net_if *iface) #endif k_mutex_init(&iface->lock); + k_mutex_init(&iface->tx_lock); api->init(iface); } @@ -448,8 +451,6 @@ enum net_verdict net_if_send_data(struct net_if *iface, struct net_pkt *pkt) enum net_verdict verdict = NET_OK; int status = -EIO; - net_if_lock(iface); - if (!net_if_flag_is_set(iface, NET_IF_LOWER_UP) || net_if_flag_is_set(iface, NET_IF_SUSPENDED)) { /* Drop packet if interface is not up */ @@ -532,8 +533,6 @@ enum net_verdict net_if_send_data(struct net_if *iface, struct net_pkt *pkt) net_if_queue_tx(iface, pkt); } - net_if_unlock(iface); - return verdict; } @@ -4747,7 +4746,8 @@ int net_if_get_name(struct net_if *iface, char *buf, int len) return -ERANGE; } - strncpy(buf, net_if_get_config(iface)->name, name_len); + /* Copy string and null terminator */ + memcpy(buf, net_if_get_config(iface)->name, name_len + 1); return name_len; #else @@ -4769,7 +4769,8 @@ int net_if_set_name(struct net_if *iface, const char *buf) return -ENAMETOOLONG; } - strncpy(net_if_get_config(iface)->name, buf, name_len); + /* Copy string and null terminator */ + memcpy(net_if_get_config(iface)->name, buf, name_len + 1); return 0; #else diff --git a/subsys/net/ip/net_pkt.c b/subsys/net/ip/net_pkt.c index 57ba0f3bb0d..fb44490dd2b 100644 --- a/subsys/net/ip/net_pkt.c +++ b/subsys/net/ip/net_pkt.c @@ -1197,6 +1197,67 @@ int net_pkt_alloc_buffer(struct net_pkt *pkt, return 0; } + +#if NET_LOG_LEVEL >= LOG_LEVEL_DBG +int net_pkt_alloc_buffer_raw_debug(struct net_pkt *pkt, size_t size, + k_timeout_t timeout, const char *caller, + int line) +#else +int net_pkt_alloc_buffer_raw(struct net_pkt *pkt, size_t size, + k_timeout_t timeout) +#endif +{ + struct net_buf_pool *pool = NULL; + struct net_buf *buf; + + if (size == 0) { + return 0; + } + + if (k_is_in_isr()) { + timeout = K_NO_WAIT; + } + + NET_DBG("Data allocation size %zu", size); + + if (pkt->context) { + pool = get_data_pool(pkt->context); + } + + if (!pool) { + pool = pkt->slab == &tx_pkts ? &tx_bufs : &rx_bufs; + } + +#if NET_LOG_LEVEL >= LOG_LEVEL_DBG + buf = pkt_alloc_buffer(pool, size, timeout, caller, line); +#else + buf = pkt_alloc_buffer(pool, size, timeout); +#endif + + if (!buf) { +#if NET_LOG_LEVEL >= LOG_LEVEL_DBG + NET_ERR("Data buffer (%zd) allocation failed (%s:%d)", + size, caller, line); +#else + NET_ERR("Data buffer (%zd) allocation failed.", size); +#endif + return -ENOMEM; + } + + net_pkt_append_buffer(pkt, buf); + +#if IS_ENABLED(CONFIG_NET_BUF_FIXED_DATA_SIZE) + /* net_buf allocators shrink the buffer size to the requested size. + * We don't want this behavior here, so restore the real size of the + * last fragment. + */ + buf = net_buf_frag_last(buf); + buf->size = CONFIG_NET_BUF_DATA_SIZE; +#endif + + return 0; +} + #if NET_LOG_LEVEL >= LOG_LEVEL_DBG static struct net_pkt *pkt_alloc(struct k_mem_slab *slab, k_timeout_t timeout, const char *caller, int line) diff --git a/subsys/net/ip/net_private.h b/subsys/net/ip/net_private.h index be0466fc98b..c2b80aa4f05 100644 --- a/subsys/net/ip/net_private.h +++ b/subsys/net/ip/net_private.h @@ -30,6 +30,9 @@ union net_mgmt_events { #if defined(CONFIG_NET_DHCPV4) struct net_if_dhcpv4 dhcpv4; #endif /* CONFIG_NET_DHCPV4 */ +#if defined(CONFIG_NET_DHCPV6) + struct net_if_dhcpv6 dhcpv6; +#endif /* CONFIG_NET_DHCPV6 */ #if defined(CONFIG_NET_L2_WIFI_MGMT) union wifi_mgmt_events wifi; #endif /* CONFIG_NET_L2_WIFI_MGMT */ diff --git a/subsys/net/ip/net_shell.c b/subsys/net/ip/net_shell.c index 286d0eca8a9..87995ccabb3 100644 --- a/subsys/net/ip/net_shell.c +++ b/subsys/net/ip/net_shell.c @@ -719,6 +719,19 @@ static void iface_cb(struct net_if *iface, void *user_data) iface->config.dhcpv4.attempts); #endif /* CONFIG_NET_DHCPV4 */ +#if defined(CONFIG_NET_DHCPV6) + PR("DHCPv6 address requested : %s\n", + iface->config.dhcpv6.params.request_addr ? + net_sprint_ipv6_addr(&iface->config.dhcpv6.addr) : "none"); + PR("DHCPv6 prefix requested : %s\n", + iface->config.dhcpv6.params.request_prefix ? + net_sprint_ipv6_addr(&iface->config.dhcpv6.prefix) : "none"); + PR("DHCPv6 state : %s\n", + net_dhcpv6_state_name(iface->config.dhcpv6.state)); + PR("DHCPv6 attempts : %d\n", + iface->config.dhcpv6.retransmissions + 1); +#endif /* CONFIG_NET_DHCPV6 */ + #else ARG_UNUSED(iface); ARG_UNUSED(user_data); @@ -2513,6 +2526,29 @@ static char *get_l3_desc(struct event_msg *msg, info = net_addr_ntop(AF_INET6, msg->data, extra_info, extra_info_len); break; + case NET_EVENT_IPV6_DHCP_START: + *desc = "DHCPv6"; + *desc2 = "start"; + break; + case NET_EVENT_IPV6_DHCP_BOUND: + *desc = "DHCPv6"; + *desc2 = "bound"; +#if defined(CONFIG_NET_DHCPV6) + struct net_if_dhcpv6 *data = (struct net_if_dhcpv6 *)msg->data; + + if (data->params.request_addr) { + info = net_addr_ntop(AF_INET6, &data->addr, extra_info, + extra_info_len); + } else if (data->params.request_prefix) { + info = net_addr_ntop(AF_INET6, &data->prefix, extra_info, + extra_info_len); + } +#endif + break; + case NET_EVENT_IPV6_DHCP_STOP: + *desc = "DHCPv6"; + *desc2 = "stop"; + break; case NET_EVENT_IPV4_ADDR_ADD: *desc = "IPv4 address"; *desc2 = "add"; @@ -4279,6 +4315,7 @@ static struct ping_context { uint32_t sequence; uint16_t payload_size; uint8_t tos; + int priority; } ping_ctx; static void ping_done(struct ping_context *ctx); @@ -4498,12 +4535,19 @@ static void ping_work(struct k_work *work) return; } + if (ctx->sequence < ctx->count) { + k_work_reschedule(&ctx->work, K_MSEC(ctx->interval)); + } else { + k_work_reschedule(&ctx->work, K_SECONDS(2)); + } + if (ctx->addr.family == AF_INET6) { ret = net_icmpv6_send_echo_request(ctx->iface, &ctx->addr.in6_addr, sys_rand32_get(), ctx->sequence, ctx->tos, + ctx->priority, NULL, ctx->payload_size); } else { @@ -4512,6 +4556,7 @@ static void ping_work(struct k_work *work) sys_rand32_get(), ctx->sequence, ctx->tos, + ctx->priority, NULL, ctx->payload_size); } @@ -4521,12 +4566,6 @@ static void ping_work(struct k_work *work) ping_done(ctx); return; } - - if (ctx->sequence < ctx->count) { - k_work_reschedule(&ctx->work, K_MSEC(ctx->interval)); - } else { - k_work_reschedule(&ctx->work, K_SECONDS(2)); - } } #define ASCII_CTRL_C 0x03 @@ -4612,6 +4651,7 @@ static int cmd_net_ping(const struct shell *sh, size_t argc, char *argv[]) int iface_idx = -1; int tos = 0; int payload_size = 4; + int priority = -1; for (size_t i = 1; i < argc; ++i) { @@ -4647,6 +4687,14 @@ static int cmd_net_ping(const struct shell *sh, size_t argc, char *argv[]) } break; + case 'p': + priority = parse_arg(&i, argc, argv); + if (priority < 0 || priority > UINT8_MAX) { + PR_WARNING("Parse error: %s\n", argv[i]); + return -ENOEXEC; + } + break; + case 'Q': tos = parse_arg(&i, argc, argv); if (tos < 0 || tos > UINT8_MAX) { @@ -4683,6 +4731,7 @@ static int cmd_net_ping(const struct shell *sh, size_t argc, char *argv[]) ping_ctx.sh = sh; ping_ctx.count = count; ping_ctx.interval = interval; + ping_ctx.priority = priority; ping_ctx.tos = tos; ping_ctx.payload_size = payload_size; @@ -6584,7 +6633,7 @@ SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_vlan, SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_ping, SHELL_CMD(--help, NULL, "'net ping [-c count] [-i interval ms] [-I ] " - "[-Q tos] [-s payload size] ' " + "[-Q tos] [-s payload size] [-p priority] ' " "Send ICMPv4 or ICMPv6 Echo-Request to a network host.", cmd_net_ping), SHELL_SUBCMD_SET_END diff --git a/subsys/net/ip/tcp.c b/subsys/net/ip/tcp.c index c90f7b92c20..680fdcecb85 100644 --- a/subsys/net/ip/tcp.c +++ b/subsys/net/ip/tcp.c @@ -621,6 +621,8 @@ static int tcp_conn_close(struct tcp *conn, int status) status, conn->recv_user_data); } + k_sem_give(&conn->tx_sem); + return tcp_conn_unref(conn); } @@ -1252,6 +1254,49 @@ static int tcp_pkt_peek(struct net_pkt *to, struct net_pkt *from, size_t pos, return net_pkt_copy(to, from, len); } +static int tcp_pkt_append(struct net_pkt *pkt, const uint8_t *data, size_t len) +{ + size_t alloc_len = len; + struct net_buf *buf = NULL; + int ret = 0; + + if (pkt->buffer) { + buf = net_buf_frag_last(pkt->buffer); + + if (len > net_buf_tailroom(buf)) { + alloc_len -= net_buf_tailroom(buf); + } else { + alloc_len = 0; + } + } + + if (alloc_len > 0) { + ret = net_pkt_alloc_buffer_raw(pkt, alloc_len, + TCP_PKT_ALLOC_TIMEOUT); + if (ret < 0) { + return -ENOBUFS; + } + } + + if (buf == NULL) { + buf = pkt->buffer; + } + + while (buf != NULL && len > 0) { + size_t write_len = MIN(len, net_buf_tailroom(buf)); + + net_buf_add_mem(buf, data, write_len); + + data += write_len; + len -= write_len; + buf = buf->frags; + } + + NET_ASSERT(len == 0, "Not all bytes written"); + + return ret; +} + static bool tcp_window_full(struct tcp *conn) { bool window_full = (conn->send_data_total >= conn->send_win); @@ -2276,7 +2321,7 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt) tcp_out(conn, RST); do_close = true; close_status = -ECONNRESET; - goto next_state; + goto out; } if (FL(&fl, &, RST)) { @@ -2292,7 +2337,7 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt) net_stats_update_tcp_seg_rst(net_pkt_iface(pkt)); do_close = true; close_status = -ECONNRESET; - goto next_state; + goto out; } if (tcp_options_len && !tcp_options_check(&conn->recv_options, pkt, @@ -2301,7 +2346,7 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt) tcp_out(conn, RST); do_close = true; close_status = -ECONNRESET; - goto next_state; + goto out; } if (th && (conn->state != TCP_LISTEN) && (conn->state != TCP_SYN_SENT) && @@ -2316,7 +2361,7 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt) tcp_out(conn, RST); do_close = true; close_status = -ECONNRESET; - goto next_state; + goto out; } if (th) { @@ -2904,6 +2949,7 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt) tcp_state_to_str(conn->state, true)); } +out: if (pkt) { if (verdict == NET_OK) { net_pkt_unref(pkt); @@ -3054,13 +3100,12 @@ int net_tcp_update_recv_wnd(struct net_context *context, int32_t delta) return ret; } -/* net_context queues the outgoing data for the TCP connection */ -int net_tcp_queue_data(struct net_context *context, struct net_pkt *pkt) +int net_tcp_queue(struct net_context *context, const void *data, size_t len, + const struct msghdr *msg) { struct tcp *conn = context->tcp; - struct net_buf *orig_buf = NULL; + size_t queued_len = 0; int ret = 0; - size_t len; if (!conn || conn->state != TCP_ESTABLISHED) { return -ENOTCONN; @@ -3077,72 +3122,69 @@ int net_tcp_queue_data(struct net_context *context, struct net_pkt *pkt) goto out; } - len = net_pkt_get_len(pkt); + if (msg) { + len = 0; - if (conn->send_data->buffer) { - orig_buf = net_buf_frag_last(conn->send_data->buffer); + for (int i = 0; i < msg->msg_iovlen; i++) { + len += msg->msg_iov[i].iov_len; + } } - net_pkt_append_buffer(conn->send_data, pkt->buffer); - conn->send_data_total += len; - NET_DBG("conn: %p Queued %zu bytes (total %zu)", conn, len, - conn->send_data_total); - pkt->buffer = NULL; + /* Queue no more than TX window permits. It's guaranteed at this point + * that conn->send_data_total is less than conn->send_win, as it was + * verified in tcp_window_full() check above. As the connection mutex + * is held, their values shall not change since. + */ + len = MIN(conn->send_win - conn->send_data_total, len); + + if (msg) { + for (int i = 0; i < msg->msg_iovlen; i++) { + int iovlen = MIN(msg->msg_iov[i].iov_len, len); + + ret = tcp_pkt_append(conn->send_data, + msg->msg_iov[i].iov_base, + iovlen); + if (ret < 0) { + if (queued_len == 0) { + goto out; + } else { + break; + } + } + + queued_len += iovlen; + len -= iovlen; + + if (len == 0) { + break; + } + } + } else { + ret = tcp_pkt_append(conn->send_data, data, len); + if (ret < 0) { + goto out; + } + + queued_len = len; + } + conn->send_data_total += queued_len; + + /* Successfully queued data for transmission. Even if there's a transmit + * failure now (out-of-buf case), it can be ignored for now, retransmit + * timer will take care of queued data retransmission. + */ ret = tcp_send_queued_data(conn); if (ret < 0 && ret != -ENOBUFS) { tcp_conn_close(conn, ret); goto out; } - if ((ret == -ENOBUFS) && - (conn->send_data_total < (conn->unacked_len + len))) { - /* Some of the data has been sent, we cannot remove the - * whole chunk, the remainder portion is already - * in the send_data and will be transmitted upon a - * received ack or the next send call - * - * Set the return code back to 0 to pretend we just - * transmitted the chunk - */ - ret = 0; + if (tcp_window_full(conn)) { + (void)k_sem_take(&conn->tx_sem, K_NO_WAIT); } - if (ret == -ENOBUFS) { - /* Restore the original data so that we do not resend the pkt - * data multiple times. - */ - conn->send_data_total -= len; - - if (orig_buf) { - pkt->buffer = orig_buf->frags; - orig_buf->frags = NULL; - } else { - pkt->buffer = conn->send_data->buffer; - conn->send_data->buffer = NULL; - } - - /* If we have out-of-bufs case, and the send_data buffer has - * become empty, till the retransmit timer, as there is no - * data to retransmit. - * The socket layer will catch this and resend data if needed. - * Only perform this when it is just the newly added packet, - * otherwise it can disrupt any pending transmission - */ - if (conn->send_data_total == 0) { - NET_DBG("No bufs, cancelling retransmit timer"); - k_work_cancel_delayable(&conn->send_data_timer); - } - } else { - if (tcp_window_full(conn)) { - (void)k_sem_take(&conn->tx_sem, K_NO_WAIT); - } - - /* We should not free the pkt if there was an error. It will be - * freed in net_context.c:context_sendto() - */ - tcp_pkt_unref(pkt); - } + ret = queued_len; out: k_mutex_unlock(&conn->lock); @@ -3497,7 +3539,9 @@ static size_t tp_tcp_recv_cb(struct tcp *conn, struct net_pkt *pkt) net_pkt_pull(up, net_pkt_get_len(up) - len); - net_tcp_queue_data(conn->context, up); + for (struct net_buf *buf = pkt->buffer; buf != NULL; buf = buf->frags) { + net_tcp_queue(conn->context, buf->data, buf->len); + } return len; } @@ -3640,12 +3684,7 @@ enum net_verdict tp_input(struct net_conn *net_conn, responded = true; NET_DBG("tcp_send(\"%s\")", tp->data); { - struct net_pkt *data_pkt; - - data_pkt = tcp_pkt_alloc(conn, len); - net_pkt_write(data_pkt, buf, len); - net_pkt_cursor_init(data_pkt); - net_tcp_queue_data(conn->context, data_pkt); + net_tcp_queue(conn->context, buf, len); } } break; diff --git a/subsys/net/ip/tcp.h b/subsys/net/ip/tcp.h index f6f481d5443..162407710eb 100644 --- a/subsys/net/ip/tcp.h +++ b/subsys/net/ip/tcp.h @@ -31,6 +31,7 @@ extern "C" { #endif +#include #include /** @@ -72,18 +73,7 @@ int net_tcp_listen(struct net_context *context); */ int net_tcp_accept(struct net_context *context, net_tcp_accept_cb_t cb, void *user_data); -/** - * @brief Enqueue data for transmission - * - * @param context Network context - * @param buf Pointer to the data - * @param len Number of bytes - * @param msghdr Data for a vector array operation - * - * @return 0 if ok, < 0 if error - */ -int net_tcp_queue(struct net_context *context, const void *buf, size_t len, - const struct msghdr *msghdr); + /* TODO: split into 2 functions, conn -> context, queue -> send? */ /* The following functions are provided solely for the compatibility @@ -112,7 +102,6 @@ void net_tcp_init(void); #define net_tcp_init(...) #endif int net_tcp_update_recv_wnd(struct net_context *context, int32_t delta); -int net_tcp_queue_data(struct net_context *context, struct net_pkt *pkt); int net_tcp_finalize(struct net_pkt *pkt); #if defined(CONFIG_NET_TEST_PROTOCOL) diff --git a/subsys/net/ip/tcp_internal.h b/subsys/net/ip/tcp_internal.h index e76ba859e49..17d4581aa3d 100644 --- a/subsys/net/ip/tcp_internal.h +++ b/subsys/net/ip/tcp_internal.h @@ -282,21 +282,27 @@ struct net_tcp_hdr *net_tcp_input(struct net_pkt *pkt, #endif /** - * @brief Enqueue a single packet for transmission + * @brief Enqueue data for transmission * - * @param context TCP context - * @param pkt Packet + * @param context Network context + * @param data Pointer to the data + * @param len Number of bytes + * @param msg Data for a vector array operation * * @return 0 if ok, < 0 if error */ #if defined(CONFIG_NET_NATIVE_TCP) -int net_tcp_queue_data(struct net_context *context, struct net_pkt *pkt); +int net_tcp_queue(struct net_context *context, const void *data, size_t len, + const struct msghdr *msg); #else -static inline int net_tcp_queue_data(struct net_context *context, - struct net_pkt *pkt) +static inline int net_tcp_queue(struct net_context *context, const void *data, + size_t len, const struct msghdr *msg) { ARG_UNUSED(context); - ARG_UNUSED(pkt); + ARG_UNUSED(data); + ARG_UNUSED(len); + ARG_UNUSED(msg); + return -EPROTONOSUPPORT; } #endif diff --git a/subsys/net/l2/ethernet/arp.c b/subsys/net/l2/ethernet/arp.c index f26c2f7ba96..39a06ed64a0 100644 --- a/subsys/net/l2/ethernet/arp.c +++ b/subsys/net/l2/ethernet/arp.c @@ -514,6 +514,8 @@ static void arp_update(struct net_if *iface, sys_slist_prepend(&arp_table, &entry->node); while (!k_fifo_is_empty(&entry->pending_queue)) { + int ret; + pkt = k_fifo_get(&entry->pending_queue, K_FOREVER); /* Set the dst in the pending packet */ @@ -525,7 +527,19 @@ static void arp_update(struct net_if *iface, net_sprint_ipv4_addr(&entry->ip), pkt, pkt->frags); - net_if_queue_tx(iface, pkt); + /* We directly send the packet without first queueing it. + * The pkt has already been queued for sending, once by + * net_if and second time in the ARP queue. We must not + * queue it twice in net_if so that the statistics of + * the pkt are not counted twice and the packet filter + * callbacks are only called once. + */ + net_if_tx_lock(iface); + ret = net_if_l2(iface)->send(iface, pkt); + net_if_tx_unlock(iface); + if (ret < 0) { + net_pkt_unref(pkt); + } } k_mutex_unlock(&arp_mutex); diff --git a/subsys/net/l2/wifi/Kconfig b/subsys/net/l2/wifi/Kconfig index e034a745b5e..6623456d84b 100644 --- a/subsys/net/l2/wifi/Kconfig +++ b/subsys/net/l2/wifi/Kconfig @@ -76,6 +76,7 @@ config WIFI_MGMT_SCAN_DWELL_TIME_PASSIVE config WIFI_MGMT_SCAN_SSID_FILT_MAX int "Maximum number of SSIDs that can be specified for SSID filtering" default 1 + range 1 4 help Maximum number of SSIDs that can be specified for SSID filtering. This can be set based on the underlying chipsets limitations. diff --git a/subsys/net/l2/wifi/wifi_mgmt.c b/subsys/net/l2/wifi/wifi_mgmt.c index 22d54b926ab..0751f5d2b8c 100644 --- a/subsys/net/l2/wifi/wifi_mgmt.c +++ b/subsys/net/l2/wifi/wifi_mgmt.c @@ -18,6 +18,224 @@ LOG_MODULE_REGISTER(net_wifi_mgmt, CONFIG_NET_L2_WIFI_MGMT_LOG_LEVEL); #include #endif /* CONFIG_WIFI_NM */ +const char * const wifi_security_txt(enum wifi_security_type security) +{ + switch (security) { + case WIFI_SECURITY_TYPE_NONE: + return "OPEN"; + case WIFI_SECURITY_TYPE_WEP: + return "WEP"; + case WIFI_SECURITY_TYPE_WPA_PSK: + return "WPA-PSK"; + case WIFI_SECURITY_TYPE_PSK: + return "WPA2-PSK"; + case WIFI_SECURITY_TYPE_PSK_SHA256: + return "WPA2-PSK-SHA256"; + case WIFI_SECURITY_TYPE_SAE: + return "WPA3-SAE"; + case WIFI_SECURITY_TYPE_WAPI: + return "WAPI"; + case WIFI_SECURITY_TYPE_EAP: + return "EAP"; + case WIFI_SECURITY_TYPE_UNKNOWN: + default: + return "UNKNOWN"; + } +} + +const char * const wifi_mfp_txt(enum wifi_mfp_options mfp) +{ + switch (mfp) { + case WIFI_MFP_DISABLE: + return "Disable"; + case WIFI_MFP_OPTIONAL: + return "Optional"; + case WIFI_MFP_REQUIRED: + return "Required"; + case WIFI_MFP_UNKNOWN: + default: + return "UNKNOWN"; + } +} + +const char * const wifi_band_txt(enum wifi_frequency_bands band) +{ + switch (band) { + case WIFI_FREQ_BAND_2_4_GHZ: + return "2.4GHz"; + case WIFI_FREQ_BAND_5_GHZ: + return "5GHz"; + case WIFI_FREQ_BAND_6_GHZ: + return "6GHz"; + case WIFI_FREQ_BAND_UNKNOWN: + default: + return "UNKNOWN"; + } +} + +const char * const wifi_state_txt(enum wifi_iface_state state) +{ + switch (state) { + case WIFI_STATE_DISCONNECTED: + return "DISCONNECTED"; + case WIFI_STATE_INACTIVE: + return "INACTIVE"; + case WIFI_STATE_INTERFACE_DISABLED: + return "INTERFACE_DISABLED"; + case WIFI_STATE_SCANNING: + return "SCANNING"; + case WIFI_STATE_AUTHENTICATING: + return "AUTHENTICATING"; + case WIFI_STATE_ASSOCIATING: + return "ASSOCIATING"; + case WIFI_STATE_ASSOCIATED: + return "ASSOCIATED"; + case WIFI_STATE_4WAY_HANDSHAKE: + return "4WAY_HANDSHAKE"; + case WIFI_STATE_GROUP_HANDSHAKE: + return "GROUP_HANDSHAKE"; + case WIFI_STATE_COMPLETED: + return "COMPLETED"; + case WIFI_STATE_UNKNOWN: + default: + return "UNKNOWN"; + } +} + +const char * const wifi_mode_txt(enum wifi_iface_mode mode) +{ + switch (mode) { + case WIFI_MODE_INFRA: + return "STATION"; + case WIFI_MODE_IBSS: + return "ADHOC"; + case WIFI_MODE_AP: + return "ACCESS POINT"; + case WIFI_MODE_P2P_GO: + return "P2P GROUP OWNER"; + case WIFI_MODE_P2P_GROUP_FORMATION: + return "P2P GROUP FORMATION"; + case WIFI_MODE_MESH: + return "MESH"; + case WIFI_MODE_UNKNOWN: + default: + return "UNKNOWN"; + } +} + +const char * const wifi_link_mode_txt(enum wifi_link_mode link_mode) +{ + switch (link_mode) { + case WIFI_0: + return "WIFI 0 (802.11)"; + case WIFI_1: + return "WIFI 1 (802.11b)"; + case WIFI_2: + return "WIFI 2 (802.11a)"; + case WIFI_3: + return "WIFI 3 (802.11g)"; + case WIFI_4: + return "WIFI 4 (802.11n/HT)"; + case WIFI_5: + return "WIFI 5 (802.11ac/VHT)"; + case WIFI_6: + return "WIFI 6 (802.11ax/HE)"; + case WIFI_6E: + return "WIFI 6E (802.11ax 6GHz/HE)"; + case WIFI_7: + return "WIFI 7 (802.11be/EHT)"; + case WIFI_LINK_MODE_UNKNOWN: + default: + return "UNKNOWN"; + } +} + +const char * const wifi_ps_txt(enum wifi_ps ps_name) +{ + switch (ps_name) { + case WIFI_PS_DISABLED: + return "Power save disabled"; + case WIFI_PS_ENABLED: + return "Power save enabled"; + default: + return "UNKNOWN"; + } +} + +const char * const wifi_ps_mode_txt(enum wifi_ps_mode ps_mode) +{ + switch (ps_mode) { + case WIFI_PS_MODE_LEGACY: + return "Legacy power save"; + case WIFI_PS_MODE_WMM: + return "WMM power save"; + default: + return "UNKNOWN"; + } +} + +const char * const wifi_twt_operation_txt(enum wifi_twt_operation twt_operation) +{ + switch (twt_operation) { + case WIFI_TWT_SETUP: + return "TWT setup"; + case WIFI_TWT_TEARDOWN: + return "TWT teardown"; + default: + return "UNKNOWN"; + } +} + +const char * const wifi_twt_negotiation_type_txt(enum wifi_twt_negotiation_type twt_negotiation) +{ + switch (twt_negotiation) { + case WIFI_TWT_INDIVIDUAL: + return "TWT individual negotiation"; + case WIFI_TWT_BROADCAST: + return "TWT broadcast negotiation"; + case WIFI_TWT_WAKE_TBTT: + return "TWT wake TBTT negotiation"; + default: + return "UNKNOWN"; + } +} + +const char * const wifi_twt_setup_cmd_txt(enum wifi_twt_setup_cmd twt_setup) +{ + switch (twt_setup) { + case WIFI_TWT_SETUP_CMD_REQUEST: + return "TWT request"; + case WIFI_TWT_SETUP_CMD_SUGGEST: + return "TWT suggest"; + case WIFI_TWT_SETUP_CMD_DEMAND: + return "TWT demand"; + case WIFI_TWT_SETUP_CMD_GROUPING: + return "TWT grouping"; + case WIFI_TWT_SETUP_CMD_ACCEPT: + return "TWT accept"; + case WIFI_TWT_SETUP_CMD_ALTERNATE: + return "TWT alternate"; + case WIFI_TWT_SETUP_CMD_DICTATE: + return "TWT dictate"; + case WIFI_TWT_SETUP_CMD_REJECT: + return "TWT reject"; + default: + return "UNKNOWN"; + } +} + +const char * const wifi_ps_wakeup_mode_txt(enum wifi_ps_wakeup_mode ps_wakeup_mode) +{ + switch (ps_wakeup_mode) { + case WIFI_PS_WAKEUP_MODE_DTIM: + return "PS wakeup mode DTIM"; + case WIFI_PS_WAKEUP_MODE_LISTEN_INTERVAL: + return "PS wakeup mode listen interval"; + default: + return "UNKNOWN"; + } +} + static const struct wifi_mgmt_ops *const get_wifi_api(struct net_if *iface) { const struct device *dev = net_if_get_device(iface); @@ -57,6 +275,7 @@ static int wifi_connect(uint32_t mgmt_request, struct net_if *iface, (params->ssid_length > WIFI_SSID_MAX_LEN) || (params->ssid_length == 0U) || ((params->security == WIFI_SECURITY_TYPE_PSK || + params->security == WIFI_SECURITY_TYPE_WPA_PSK || params->security == WIFI_SECURITY_TYPE_PSK_SHA256) && ((params->psk_length < 8) || (params->psk_length > 64) || (params->psk_length == 0U) || !params->psk)) || @@ -446,6 +665,66 @@ void wifi_mgmt_raise_twt_sleep_state(struct net_if *iface, sizeof(twt_sleep_state)); } +static int wifi_mode(uint32_t mgmt_request, struct net_if *iface, + void *data, size_t len) +{ + const struct device *dev = net_if_get_device(iface); + const struct wifi_mgmt_ops *const wifi_mgmt_api = get_wifi_api(iface); + struct wifi_mode_info *mode_info = data; + + if (dev == NULL) { + return -ENODEV; + } + + if (wifi_mgmt_api == NULL || wifi_mgmt_api->mode == NULL) { + return -ENOTSUP; + } + + return wifi_mgmt_api->mode(dev, mode_info); +} + +NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_WIFI_MODE, wifi_mode); + +static int wifi_packet_filter(uint32_t mgmt_request, struct net_if *iface, + void *data, size_t len) +{ + const struct device *dev = net_if_get_device(iface); + const struct wifi_mgmt_ops *const wifi_mgmt_api = get_wifi_api(iface); + struct wifi_filter_info *filter_info = data; + + if (dev == NULL) { + return -ENODEV; + } + + if (wifi_mgmt_api == NULL || wifi_mgmt_api->filter == NULL) { + return -ENOTSUP; + } + + return wifi_mgmt_api->filter(dev, filter_info); +} + +NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_WIFI_PACKET_FILTER, wifi_packet_filter); + +static int wifi_channel(uint32_t mgmt_request, struct net_if *iface, + void *data, size_t len) +{ + const struct device *dev = net_if_get_device(iface); + const struct wifi_mgmt_ops *const wifi_mgmt_api = get_wifi_api(iface); + struct wifi_channel_info *channel_info = data; + + if (dev == NULL) { + return -ENODEV; + } + + if (wifi_mgmt_api == NULL || wifi_mgmt_api->channel == NULL) { + return -ENOTSUP; + } + + return wifi_mgmt_api->channel(dev, channel_info); +} + +NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_WIFI_CHANNEL, wifi_channel); + #ifdef CONFIG_WIFI_MGMT_RAW_SCAN_RESULTS void wifi_mgmt_raise_raw_scan_result_event(struct net_if *iface, struct wifi_raw_scan_result *raw_scan_result) diff --git a/subsys/net/l2/wifi/wifi_shell.c b/subsys/net/l2/wifi/wifi_shell.c index 0b8c6a74983..6893e20353e 100644 --- a/subsys/net/l2/wifi/wifi_shell.c +++ b/subsys/net/l2/wifi/wifi_shell.c @@ -75,12 +75,20 @@ static bool parse_number(const struct shell *sh, long *param, char *str, long mi { char *endptr; char *str_tmp = str; - long num = strtol(str_tmp, &endptr, 10); + long num = 0; + + if ((str_tmp[0] == '0') && (str_tmp[1] == 'x')) { + /* Hexadecimal numbers take base 0 in strtol */ + num = strtol(str_tmp, &endptr, 0); + } else { + num = strtol(str_tmp, &endptr, 10); + } if (*endptr != '\0') { print(sh, SHELL_ERROR, "Invalid number: %s", str_tmp); return false; } + if ((num) < (min) || (num) > (max)) { print(sh, SHELL_WARNING, "Value out of range: %s, (%ld-%ld)", str_tmp, min, max); return false; @@ -249,7 +257,7 @@ static void print_twt_params(uint8_t dialog_token, uint8_t flow_id, print(context.sh, SHELL_NORMAL, "TWT flow ID: %d\n", flow_id); print(context.sh, SHELL_NORMAL, "TWT negotiation type: %s\n", - wifi_twt_negotiation_type2str[negotiation_type]); + wifi_twt_negotiation_type_txt(negotiation_type)); print(context.sh, SHELL_NORMAL, "TWT responder: %s\n", responder ? "true" : "false"); print(context.sh, SHELL_NORMAL, "TWT implicit: %s\n", @@ -278,7 +286,7 @@ static void handle_wifi_twt_event(struct net_mgmt_event_callback *cb) if (resp->resp_status == WIFI_TWT_RESP_RECEIVED) { print(context.sh, SHELL_NORMAL, "TWT response: %s\n", - wifi_twt_setup_cmd2str[resp->setup_cmd]); + wifi_twt_setup_cmd_txt(resp->setup_cmd)); print(context.sh, SHELL_NORMAL, "== TWT negotiated parameters ==\n"); print_twt_params(resp->dialog_token, resp->flow_id, @@ -333,6 +341,11 @@ static int __wifi_args_to_params(size_t argc, char *argv[], return -EINVAL; } + /* Defaults */ + params->band = WIFI_FREQ_BAND_UNKNOWN; + params->channel = WIFI_CHANNEL_ANY; + params->security = WIFI_SECURITY_TYPE_NONE; + /* SSID */ params->ssid = argv[0]; params->ssid_length = strlen(params->ssid); @@ -352,8 +365,6 @@ static int __wifi_args_to_params(size_t argc, char *argv[], } idx++; - } else { - params->channel = WIFI_CHANNEL_ANY; } /* PSK (optional) */ @@ -392,8 +403,6 @@ static int __wifi_args_to_params(size_t argc, char *argv[], params->psk_length > WIFI_SAE_PSWD_MAX_LEN)) { return -EINVAL; } - } else { - params->security = WIFI_SECURITY_TYPE_NONE; } @@ -634,7 +643,7 @@ static int cmd_wifi_status(const struct shell *sh, size_t argc, char *argv[]) wifi_mode_txt(status.iface_mode)); shell_fprintf(sh, SHELL_NORMAL, "Link Mode: %s\n", wifi_link_mode_txt(status.link_mode)); - shell_fprintf(sh, SHELL_NORMAL, "SSID: %-32s\n", status.ssid); + shell_fprintf(sh, SHELL_NORMAL, "SSID: %.32s\n", status.ssid); shell_fprintf(sh, SHELL_NORMAL, "BSSID: %s\n", net_sprint_ll_addr_buf(status.bssid, WIFI_MAC_ADDR_LEN, mac_string_buf, @@ -647,7 +656,9 @@ static int cmd_wifi_status(const struct shell *sh, size_t argc, char *argv[]) wifi_security_txt(status.security)); shell_fprintf(sh, SHELL_NORMAL, "MFP: %s\n", wifi_mfp_txt(status.mfp)); - shell_fprintf(sh, SHELL_NORMAL, "RSSI: %d\n", status.rssi); + if (status.iface_mode == WIFI_MODE_INFRA) { + shell_fprintf(sh, SHELL_NORMAL, "RSSI: %d\n", status.rssi); + } shell_fprintf(sh, SHELL_NORMAL, "Beacon Interval: %d\n", status.beacon_interval); shell_fprintf(sh, SHELL_NORMAL, "DTIM: %d\n", status.dtim_period); shell_fprintf(sh, SHELL_NORMAL, "TWT: %s\n", @@ -728,10 +739,10 @@ static int cmd_wifi_ps(const struct shell *sh, size_t argc, char *argv[]) } shell_fprintf(sh, SHELL_NORMAL, "PS status: %s\n", - wifi_ps2str[config.ps_params.enabled]); + wifi_ps_txt(config.ps_params.enabled)); if (config.ps_params.enabled) { shell_fprintf(sh, SHELL_NORMAL, "PS mode: %s\n", - wifi_ps_mode2str[config.ps_params.mode]); + wifi_ps_mode_txt(config.ps_params.mode)); } shell_fprintf(sh, SHELL_NORMAL, "PS listen_interval: %d\n", @@ -786,7 +797,7 @@ static int cmd_wifi_ps(const struct shell *sh, size_t argc, char *argv[]) return -ENOEXEC; } - shell_fprintf(sh, SHELL_NORMAL, "%s\n", wifi_ps2str[params.enabled]); + shell_fprintf(sh, SHELL_NORMAL, "%s\n", wifi_ps_txt(params.enabled)); return 0; } @@ -811,12 +822,12 @@ static int cmd_wifi_ps_mode(const struct shell *sh, size_t argc, char *argv[]) if (net_mgmt(NET_REQUEST_WIFI_PS, iface, ¶ms, sizeof(params))) { shell_fprintf(sh, SHELL_WARNING, "%s failed Reason : %s\n", - wifi_ps_mode2str[params.mode], + wifi_ps_mode_txt(params.mode), wifi_ps_get_config_err_code_str(params.fail_reason)); return -ENOEXEC; } - shell_fprintf(sh, SHELL_NORMAL, "%s\n", wifi_ps_mode2str[params.mode]); + shell_fprintf(sh, SHELL_NORMAL, "%s\n", wifi_ps_mode_txt(params.mode)); return 0; } @@ -895,15 +906,15 @@ static int cmd_wifi_twt_setup_quick(const struct shell *sh, size_t argc, if (net_mgmt(NET_REQUEST_WIFI_TWT, iface, ¶ms, sizeof(params))) { shell_fprintf(sh, SHELL_WARNING, "%s with %s failed, reason : %s\n", - wifi_twt_operation2str[params.operation], - wifi_twt_negotiation_type2str[params.negotiation_type], + wifi_twt_operation_txt(params.operation), + wifi_twt_negotiation_type_txt(params.negotiation_type), wifi_twt_get_err_code_str(params.fail_reason)); return -ENOEXEC; } shell_fprintf(sh, SHELL_NORMAL, "TWT operation %s with dg: %d, flow_id: %d requested\n", - wifi_twt_operation2str[params.operation], + wifi_twt_operation_txt(params.operation), params.dialog_token, params.flow_id); return 0; @@ -982,15 +993,15 @@ static int cmd_wifi_twt_setup(const struct shell *sh, size_t argc, if (net_mgmt(NET_REQUEST_WIFI_TWT, iface, ¶ms, sizeof(params))) { shell_fprintf(sh, SHELL_WARNING, "%s with %s failed. reason : %s\n", - wifi_twt_operation2str[params.operation], - wifi_twt_negotiation_type2str[params.negotiation_type], + wifi_twt_operation_txt(params.operation), + wifi_twt_negotiation_type_txt(params.negotiation_type), wifi_twt_get_err_code_str(params.fail_reason)); return -ENOEXEC; } shell_fprintf(sh, SHELL_NORMAL, "TWT operation %s with dg: %d, flow_id: %d requested\n", - wifi_twt_operation2str[params.operation], + wifi_twt_operation_txt(params.operation), params.dialog_token, params.flow_id); return 0; @@ -1038,15 +1049,15 @@ static int cmd_wifi_twt_teardown(const struct shell *sh, size_t argc, if (net_mgmt(NET_REQUEST_WIFI_TWT, iface, ¶ms, sizeof(params))) { shell_fprintf(sh, SHELL_WARNING, "%s with %s failed, reason : %s\n", - wifi_twt_operation2str[params.operation], - wifi_twt_negotiation_type2str[params.negotiation_type], + wifi_twt_operation_txt(params.operation), + wifi_twt_negotiation_type_txt(params.negotiation_type), wifi_twt_get_err_code_str(params.fail_reason)); return -ENOEXEC; } shell_fprintf(sh, SHELL_NORMAL, "TWT operation %s with dg: %d, flow_id: %d success\n", - wifi_twt_operation2str[params.operation], + wifi_twt_operation_txt(params.operation), params.dialog_token, params.flow_id); return 0; @@ -1065,15 +1076,15 @@ static int cmd_wifi_twt_teardown_all(const struct shell *sh, size_t argc, if (net_mgmt(NET_REQUEST_WIFI_TWT, iface, ¶ms, sizeof(params))) { shell_fprintf(sh, SHELL_WARNING, "%s with %s failed, reason : %s\n", - wifi_twt_operation2str[params.operation], - wifi_twt_negotiation_type2str[params.negotiation_type], + wifi_twt_operation_txt(params.operation), + wifi_twt_negotiation_type_txt(params.negotiation_type), wifi_twt_get_err_code_str(params.fail_reason)); return -ENOEXEC; } shell_fprintf(sh, SHELL_NORMAL, "TWT operation %s all flows success\n", - wifi_twt_operation2str[params.operation]); + wifi_twt_operation_txt(params.operation)); return 0; } @@ -1250,64 +1261,408 @@ static int cmd_wifi_ps_wakeup_mode(const struct shell *sh, size_t argc, char *ar } shell_fprintf(sh, SHELL_NORMAL, "%s\n", - wifi_ps_wakeup_mode2str[params.wakeup_mode]); + wifi_ps_wakeup_mode_txt(params.wakeup_mode)); + + return 0; +} + +void parse_mode_args_to_params(const struct shell *sh, int argc, + char *argv[], struct wifi_mode_info *mode, + bool *do_mode_oper) +{ + int opt; + int option_index = 0; + + static struct option long_options[] = {{"if-index", optional_argument, 0, 'i'}, + {"sta", no_argument, 0, 's'}, + {"monitor", no_argument, 0, 'm'}, + {"tx-injection", no_argument, 0, 't'}, + {"promiscuous", no_argument, 0, 'p'}, + {"ap", no_argument, 0, 'a'}, + {"softap", no_argument, 0, 'k'}, + {"get", no_argument, 0, 'g'}, + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0}}; + + while ((opt = getopt_long(argc, argv, "i:smtpakgh", long_options, &option_index)) != -1) { + switch (opt) { + case 's': + mode->mode |= WIFI_STA_MODE; + break; + case 'm': + mode->mode |= WIFI_MONITOR_MODE; + break; + case 't': + mode->mode |= WIFI_TX_INJECTION_MODE; + break; + case 'p': + mode->mode |= WIFI_PROMISCUOUS_MODE; + break; + case 'a': + mode->mode |= WIFI_AP_MODE; + break; + case 'k': + mode->mode |= WIFI_SOFTAP_MODE; + break; + case 'g': + mode->oper = WIFI_MGMT_GET; + break; + case 'i': + mode->if_index = (uint8_t)atoi(optarg); + break; + case 'h': + shell_help(sh); + *do_mode_oper = false; + break; + case '?': + default: + break; + } + } +} + +static int cmd_wifi_mode(const struct shell *sh, size_t argc, char *argv[]) +{ + struct net_if *iface; + struct wifi_mode_info mode_info = {0}; + int ret; + bool do_mode_oper = true; + + if (argc > 1) { + mode_info.oper = WIFI_MGMT_SET; + parse_mode_args_to_params(sh, argc, argv, &mode_info, &do_mode_oper); + } else { + shell_fprintf(sh, SHELL_ERROR, "Invalid number of arguments\n"); + return -EINVAL; + } + if (do_mode_oper) { + /* Check interface index value. Mode validation must be performed by + * lower layer + */ + if (mode_info.if_index == 0) { + iface = net_if_get_first_wifi(); + if (iface == NULL) { + shell_fprintf(sh, SHELL_ERROR, + "Cannot find the default wifi interface\n"); + return -ENOEXEC; + } + mode_info.if_index = net_if_get_by_iface(iface); + } else { + iface = net_if_get_by_index(mode_info.if_index); + if (iface == NULL) { + shell_fprintf(sh, SHELL_ERROR, + "Cannot find interface for if_index %d\n", + mode_info.if_index); + return -ENOEXEC; + } + } + + ret = net_mgmt(NET_REQUEST_WIFI_MODE, iface, &mode_info, sizeof(mode_info)); + + if (ret) { + shell_fprintf(sh, SHELL_ERROR, "mode %s operation failed with reason %d\n", + mode_info.oper == WIFI_MGMT_GET ? "get" : "set", ret); + return -ENOEXEC; + } + + if (mode_info.oper == WIFI_MGMT_GET) { + shell_fprintf(sh, SHELL_NORMAL, "Wi-Fi current mode is %x\n", + mode_info.mode); + } else { + shell_fprintf(sh, SHELL_NORMAL, "Wi-Fi mode set to %x\n", mode_info.mode); + } + } + return 0; +} + +void parse_channel_args_to_params(const struct shell *sh, int argc, + char *argv[], struct wifi_channel_info *channel, + bool *do_channel_oper) +{ + int opt; + int option_index = 0; + + static struct option long_options[] = {{"if-index", optional_argument, 0, 'i'}, + {"channel", required_argument, 0, 'c'}, + {"get", no_argument, 0, 'g'}, + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0}}; + + while ((opt = getopt_long(argc, argv, "i:c:gh", long_options, &option_index)) != -1) { + switch (opt) { + case 'c': + channel->channel = (uint16_t)atoi(optarg); + break; + case 'i': + channel->if_index = (uint8_t)atoi(optarg); + break; + case 'g': + channel->oper = WIFI_MGMT_GET; + break; + case 'h': + shell_help(sh); + *do_channel_oper = false; + break; + case '?': + default: + break; + } + } +} + +static int cmd_wifi_channel(const struct shell *sh, size_t argc, char *argv[]) +{ + struct net_if *iface; + struct wifi_channel_info channel_info = {0}; + int ret; + bool do_channel_oper = true; + + if (argc > 1) { + channel_info.oper = WIFI_MGMT_SET; + parse_channel_args_to_params(sh, argc, argv, &channel_info, &do_channel_oper); + } else { + shell_fprintf(sh, SHELL_ERROR, "Invalid number of arguments\n"); + return -EINVAL; + } + + if (do_channel_oper) { + /* + * Validate parameters before sending to lower layer. + * Do it here instead of parse_channel_args_to_params + * as this is right before sending the parameters to + * the lower layer. + */ + + if (channel_info.if_index == 0) { + iface = net_if_get_first_wifi(); + if (iface == NULL) { + shell_fprintf(sh, SHELL_ERROR, + "Cannot find the default wifi interface\n"); + return -ENOEXEC; + } + channel_info.if_index = net_if_get_by_iface(iface); + } else { + iface = net_if_get_by_index(channel_info.if_index); + if (iface == NULL) { + shell_fprintf(sh, SHELL_ERROR, + "Cannot find interface for if_index %d\n", + channel_info.if_index); + return -ENOEXEC; + } + } + + if (channel_info.oper == WIFI_MGMT_SET) { + if ((channel_info.channel < WIFI_CHANNEL_MIN) || + (channel_info.channel > WIFI_CHANNEL_MAX)) { + shell_fprintf(sh, SHELL_ERROR, + "Invalid channel number. Range is (1-233)\n"); + return -ENOEXEC; + } + } + + ret = net_mgmt(NET_REQUEST_WIFI_CHANNEL, iface, + &channel_info, sizeof(channel_info)); + + if (ret) { + shell_fprintf(sh, SHELL_ERROR, + "channel %s operation failed with reason %d\n", + channel_info.oper == WIFI_MGMT_GET ? "get" : "set", ret); + return -ENOEXEC; + } + + if (channel_info.oper == WIFI_MGMT_GET) { + shell_fprintf(sh, SHELL_NORMAL, "Wi-Fi current channel is: %d\n", + channel_info.channel); + } else { + shell_fprintf(sh, SHELL_NORMAL, "Wi-Fi channel set to %d\n", + channel_info.channel); + } + } + return 0; +} + +void parse_filter_args_to_params(const struct shell *sh, int argc, + char *argv[], struct wifi_filter_info *filter, + bool *do_filter_oper) +{ + int opt; + int option_index = 0; + + static struct option long_options[] = {{"if-index", optional_argument, 0, 'i'}, + {"capture-len", optional_argument, 0, 'b'}, + {"all", no_argument, 0, 'a'}, + {"mgmt", no_argument, 0, 'm'}, + {"ctrl", no_argument, 0, 'c'}, + {"data", no_argument, 0, 'd'}, + {"get", no_argument, 0, 'g'}, + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0}}; + + while ((opt = getopt_long(argc, argv, "i:b:amcdgh", long_options, &option_index)) != -1) { + switch (opt) { + case 'a': + filter->filter |= WIFI_PACKET_FILTER_ALL; + break; + case 'm': + filter->filter |= WIFI_PACKET_FILTER_MGMT; + break; + case 'c': + filter->filter |= WIFI_PACKET_FILTER_DATA; + break; + case 'd': + filter->filter |= WIFI_PACKET_FILTER_CTRL; + break; + case 'i': + filter->if_index = (uint8_t)atoi(optarg); + break; + case 'b': + filter->buffer_size = (uint16_t)atoi(optarg); + break; + case 'h': + shell_help(sh); + *do_filter_oper = false; + break; + case 'g': + filter->oper = WIFI_MGMT_GET; + break; + case '?': + default: + break; + } + } +} + +static int cmd_wifi_packet_filter(const struct shell *sh, size_t argc, char *argv[]) +{ + struct net_if *iface; + struct wifi_filter_info packet_filter = {0}; + int ret; + bool do_filter_oper = true; + + if (argc > 1) { + packet_filter.oper = WIFI_MGMT_SET; + parse_filter_args_to_params(sh, argc, argv, &packet_filter, &do_filter_oper); + } else { + shell_fprintf(sh, SHELL_ERROR, "Invalid number of arguments\n"); + return -EINVAL; + } + + if (do_filter_oper) { + /* + * Validate parameters before sending to lower layer. + * Do it here instead of parse_filter_args_to_params + * as this is right before sending the parameters to + * the lower layer. filter and packet capture length + * value to be verified by the lower layer. + */ + if (packet_filter.if_index == 0) { + iface = net_if_get_first_wifi(); + if (iface == NULL) { + shell_fprintf(sh, SHELL_ERROR, + "Cannot find the default wifi interface\n"); + return -ENOEXEC; + } + packet_filter.if_index = net_if_get_by_iface(iface); + } else { + iface = net_if_get_by_index(packet_filter.if_index); + if (iface == NULL) { + shell_fprintf(sh, SHELL_ERROR, + "Cannot find interface for if_index %d\n", + packet_filter.if_index); + return -ENOEXEC; + } + } + + ret = net_mgmt(NET_REQUEST_WIFI_PACKET_FILTER, iface, + &packet_filter, sizeof(packet_filter)); + + if (ret) { + shell_fprintf(sh, SHELL_ERROR, + "Wi-Fi packet filter %s operation failed with reason %d\n", + packet_filter.oper == WIFI_MGMT_GET ? "get" : "set", ret); + return -ENOEXEC; + } + + if (packet_filter.oper == WIFI_MGMT_GET) { + shell_fprintf(sh, SHELL_NORMAL, "Wi-Fi current mode packet filter is %d\n", + packet_filter.filter); + } else { + shell_fprintf(sh, SHELL_NORMAL, "Wi-Fi mode packet filter set to %d\n", + packet_filter.filter); + } + } return 0; } SHELL_STATIC_SUBCMD_SET_CREATE(wifi_cmd_ap, - SHELL_CMD(disable, NULL, + SHELL_CMD_ARG(disable, NULL, "Disable Access Point mode", - cmd_wifi_ap_disable), - SHELL_CMD(enable, NULL, " [channel] [PSK]", - cmd_wifi_ap_enable), + cmd_wifi_ap_disable, + 1, 0), + SHELL_CMD_ARG(enable, NULL, + "\"\"\n" + "[channel number: 0 means all]\n" + "[PSK: valid only for secure SSIDs]\n" + "[Security type: valid only for secure SSIDs]\n" + "0:None, 1:WPA2-PSK, 2:WPA2-PSK-256, 3:SAE, 4:WAPI, 5:EAP, 6:WEP, 7: WPA-PSK\n" + "[MFP (optional: needs security type to be specified)]\n" + ": 0:Disable, 1:Optional, 2:Required", + cmd_wifi_ap_enable, + 2, 4), SHELL_SUBCMD_SET_END ); SHELL_STATIC_SUBCMD_SET_CREATE(wifi_twt_ops, - SHELL_CMD(quick_setup, NULL, " Start a TWT flow with defaults:\n" + SHELL_CMD_ARG(quick_setup, NULL, " Start a TWT flow with defaults:\n" " \n", - cmd_wifi_twt_setup_quick), - SHELL_CMD(setup, NULL, " Start a TWT flow:\n" + cmd_wifi_twt_setup_quick, + 3, 0), + SHELL_CMD_ARG(setup, NULL, " Start a TWT flow:\n" "\n" "\n" " " " \n", - cmd_wifi_twt_setup), - SHELL_CMD(teardown, NULL, " Teardown a TWT flow:\n" + cmd_wifi_twt_setup, + 11, 0), + SHELL_CMD_ARG(teardown, NULL, " Teardown a TWT flow:\n" "\n" "\n" " \n", - cmd_wifi_twt_teardown), - SHELL_CMD(teardown_all, NULL, " Teardown all TWT flows\n", - cmd_wifi_twt_teardown_all), + cmd_wifi_twt_teardown, + 5, 0), + SHELL_CMD_ARG(teardown_all, NULL, " Teardown all TWT flows\n", + cmd_wifi_twt_teardown_all, + 1, 0), SHELL_SUBCMD_SET_END ); SHELL_STATIC_SUBCMD_SET_CREATE(wifi_commands, SHELL_CMD(ap, &wifi_cmd_ap, "Access Point mode commands", NULL), - SHELL_CMD(connect, NULL, + SHELL_CMD_ARG(connect, NULL, "Connect to a Wi-Fi AP\n" "\"\"\n" - "\n" - "\n" - "\n" - "0:None, 1:PSK, 2:PSK-256, 3:SAE\n" - "\n" + "[channel number: 0 means all]\n" + "[PSK: valid only for secure SSIDs]\n" + "[Security type: valid only for secure SSIDs]\n" + "0:None, 1:WPA2-PSK, 2:WPA2-PSK-256, 3:SAE, 4:WAPI, 5:EAP, 6:WEP, 7: WPA-PSK\n" + "[MFP (optional: needs security type to be specified)]\n" ": 0:Disable, 1:Optional, 2:Required", - cmd_wifi_connect), - SHELL_CMD(disconnect, NULL, "Disconnect from the Wi-Fi AP", - cmd_wifi_disconnect), - SHELL_CMD(ps, NULL, "Configure Wi-F PS on/off, no arguments will dump config", - cmd_wifi_ps), + cmd_wifi_connect, + 2, 5), + SHELL_CMD_ARG(disconnect, NULL, "Disconnect from the Wi-Fi AP", + cmd_wifi_disconnect, + 1, 0), + SHELL_CMD_ARG(ps, NULL, "Configure Wi-F PS on/off, no arguments will dump config", + cmd_wifi_ps, + 1, 1), SHELL_CMD_ARG(ps_mode, NULL, - "\n" - "", + "\n", cmd_wifi_ps_mode, 2, 0), - SHELL_CMD(scan, NULL, + SHELL_CMD_ARG(scan, NULL, "Scan for Wi-Fi APs\n" "OPTIONAL PARAMETERS:\n" "[-t, --type ] : Preferred mode of scan. The actual mode of scan can depend on factors such as the Wi-Fi chip implementation, regulatory domain restrictions. Default type is active.\n" @@ -1318,16 +1673,70 @@ SHELL_STATIC_SUBCMD_SET_CREATE(wifi_commands, "[-m, --max_bss ] : Maximum BSSes to scan for. Range 1 - 65535.\n" "[-c, --chans ] : Channels to be scanned. The channels must be specified in the form band1:chan1,chan2_band2:chan3,..etc. band1, band2 must be valid band values and chan1, chan2, chan3 must be specified as a list of comma separated values where each value is either a single channel or a channel range specified as chan_start-chan_end. Each band channel set has to be separated by a _. For example, a valid channel specification can be 2:1,6-11,14_5:36,149-165,44\n" "[-h, --help] : Print out the help for the scan command.", - cmd_wifi_scan), - SHELL_CMD(statistics, NULL, "Wi-Fi interface statistics", cmd_wifi_stats), - SHELL_CMD(status, NULL, "Status of the Wi-Fi interface", cmd_wifi_status), + cmd_wifi_scan, + 1, 8), + SHELL_CMD_ARG(statistics, NULL, "Wi-Fi interface statistics", cmd_wifi_stats, 1, 0), + SHELL_CMD_ARG(status, NULL, "Status of the Wi-Fi interface", cmd_wifi_status, 1, 0), SHELL_CMD(twt, &wifi_twt_ops, "Manage TWT flows", NULL), - SHELL_CMD(reg_domain, NULL, + SHELL_CMD_ARG(reg_domain, NULL, "Set or Get Wi-Fi regulatory domain\n" "Usage: wifi reg_domain [ISO/IEC 3166-1 alpha2] [-f]\n" - "-f: Force to use this regulatory hint over any other regulatory hints\n" + "[-f]: Force to use this regulatory hint over any other regulatory hints\n" "Note: This may cause regulatory compliance issues, use it at your own risk.", - cmd_wifi_reg_domain), + cmd_wifi_reg_domain, + 1, 1), + SHELL_CMD_ARG(mode, NULL, "mode operational setting\n" + "This command may be used to set the Wi-Fi device into a specific mode of operation\n" + "parameters:" + "[-i, --if-index ] : Interface index.\n" + "[-s, --sta] : Station mode.\n" + "[-m, --monitor] : Monitor mode.\n" + "[-p, --promiscuous] : Promiscuous mode.\n" + "[-t, --tx-injection] : TX-Injection mode.\n" + "[-a, --ap] : AP mode.\n" + "[-k, --softap] : Softap mode.\n" + "[-h, --help] : Help.\n" + "[-g, --get] : Get current mode for a specific interface index.\n" + "Usage: Get operation example for interface index 1\n" + "wifi mode -g -i1\n" + "Set operation example for interface index 1 - set station+promiscuous\n" + "wifi mode -i1 -sp\n", + cmd_wifi_mode, + 1, 9), + SHELL_CMD_ARG(packet_filter, NULL, "mode filter setting\n" + "This command is used to set packet filter setting when\n" + "monitor, TX-Injection and promiscuous mode is enabled.\n" + "The different packet filter modes are control, management, data and enable all filters\n" + "parameters:" + "[-i, --if-index ] : Interface index.\n" + "[-a, --all] : Enable all packet filter modes\n" + "[-m, --mgmt] : Enable management packets to allowed up the stack.\n" + "[-c, --ctrl] : Enable control packets to be allowed up the stack.\n" + "[-d, --data] : Enable Data packets to be allowed up the stack.\n" + "[-g, --get] : Get current filter settings for a specific interface index.\n" + "[-b, --capture-len ] : Capture length buffer size for each packet to be captured\n" + "[-h, --help] : Help.\n" + "Usage: Get operation example for interface index 1\n" + "wifi packet_filter -g -i1\n" + "Set operation example for interface index 1 - set data+management frame filter\n" + "wifi packet_filter -i1 -md\n", + cmd_wifi_packet_filter, + 1, 8), + SHELL_CMD_ARG(channel, NULL, "wifi channel setting\n" + "This command is used to set the channel when\n" + "monitor or TX-Injection mode is enabled.\n" + "Currently 20 MHz is only supported and no BW parameter is provided\n" + "parameters:" + "[-i, --if-index ] : Interface index.\n" + "[-c, --channel ] : Set a specific channel number to the lower layer.\n" + "[-g, --get] : Get current set channel number from the lower layer.\n" + "[-h, --help] : Help.\n" + "Usage: Get operation example for interface index 1\n" + "wifi channel -g -i1\n" + "Set operation example for interface index 1 (setting channel 5)\n" + "wifi -i1 -c5\n", + cmd_wifi_channel, + 1, 4), SHELL_CMD_ARG(ps_timeout, NULL, " - PS inactivity timer(in ms)", @@ -1342,8 +1751,7 @@ SHELL_STATIC_SUBCMD_SET_CREATE(wifi_commands, 0), SHELL_CMD_ARG(ps_wakeup_mode, NULL, - " : Set PS wake up mode to DTIM interval\n" - " : Set PS wake up mode to listen interval", + "\n", cmd_wifi_ps_wakeup_mode, 2, 0), diff --git a/subsys/net/l2/wifi/wifi_utils.c b/subsys/net/l2/wifi/wifi_utils.c index f7e9d9af4dc..ea0e42def47 100644 --- a/subsys/net/l2/wifi/wifi_utils.c +++ b/subsys/net/l2/wifi/wifi_utils.c @@ -215,15 +215,30 @@ static int wifi_utils_validate_chan_str(char *chan_str) int wifi_utils_parse_scan_bands(char *scan_bands_str, uint8_t *band_map) { + char parse_str[WIFI_MGMT_BAND_STR_SIZE_MAX + 1]; char *band_str = NULL; char *ctx = NULL; enum wifi_frequency_bands band = WIFI_FREQ_BAND_UNKNOWN; + int len; if (!scan_bands_str) { return -EINVAL; } - band_str = strtok_r(scan_bands_str, ",", &ctx); + len = strlen(scan_bands_str); + + if (len > WIFI_MGMT_BAND_STR_SIZE_MAX) { + NET_ERR("Band string (%s) size (%d) exceeds maximum allowed value (%d)", + scan_bands_str, + len, + WIFI_MGMT_BAND_STR_SIZE_MAX); + return -EINVAL; + } + + strncpy(parse_str, scan_bands_str, len); + parse_str[len] = '\0'; + + band_str = strtok_r(parse_str, ",", &ctx); while (band_str) { band = wifi_utils_map_band_str_to_idx(band_str); @@ -244,15 +259,30 @@ int wifi_utils_parse_scan_bands(char *scan_bands_str, uint8_t *band_map) int wifi_utils_parse_scan_ssids(char *scan_ssids_str, char ssids[][WIFI_SSID_MAX_LEN + 1]) { + char parse_str[(WIFI_MGMT_SCAN_SSID_FILT_MAX * (WIFI_SSID_MAX_LEN + 1)) + 1]; char *ssid = NULL; char *ctx = NULL; uint8_t i = 0; + int len; if (!scan_ssids_str) { return -EINVAL; } - ssid = strtok_r(scan_ssids_str, ",", &ctx); + len = strlen(scan_ssids_str); + + if (len > (WIFI_MGMT_SCAN_SSID_FILT_MAX * (WIFI_SSID_MAX_LEN + 1))) { + NET_ERR("SSID string (%s) size (%d) exceeds maximum allowed value (%d)", + scan_ssids_str, + len, + (WIFI_MGMT_SCAN_SSID_FILT_MAX * (WIFI_SSID_MAX_LEN + 1))); + return -EINVAL; + } + + strncpy(parse_str, scan_ssids_str, len); + parse_str[len] = '\0'; + + ssid = strtok_r(parse_str, ",", &ctx); while (ssid) { if (strlen(ssid) > WIFI_SSID_MAX_LEN) { diff --git a/subsys/net/lib/coap/coap.c b/subsys/net/lib/coap/coap.c index a5c300bf0e2..e3427844bb5 100644 --- a/subsys/net/lib/coap/coap.c +++ b/subsys/net/lib/coap/coap.c @@ -1303,6 +1303,19 @@ int coap_get_block1_option(const struct coap_packet *cpkt, bool *has_more, uint8 return ret; } +int coap_get_block2_option(const struct coap_packet *cpkt, uint8_t *block_number) +{ + int ret = coap_get_option_int(cpkt, COAP_OPTION_BLOCK2); + + if (ret < 0) { + return ret; + } + + *block_number = GET_NUM(ret); + ret = 1 << (GET_BLOCK_SIZE(ret) + 4); + return ret; +} + int insert_option(struct coap_packet *cpkt, uint16_t code, const uint8_t *value, uint16_t len) { uint16_t offset = cpkt->hdr_len; diff --git a/subsys/net/lib/coap/coap_client.c b/subsys/net/lib/coap/coap_client.c index f8ba4283648..e78aafd6829 100644 --- a/subsys/net/lib/coap/coap_client.c +++ b/subsys/net/lib/coap/coap_client.c @@ -308,12 +308,6 @@ int coap_client_req(struct coap_client *client, int sock, const struct sockaddr reset_internal_request(internal_req); - if (retries == -1) { - internal_req->retry_count = DEFAULT_RETRY_AMOUNT; - } else { - internal_req->retry_count = retries; - } - if (k_mutex_lock(&client->send_mutex, K_NO_WAIT)) { return -EAGAIN; } @@ -332,16 +326,25 @@ int coap_client_req(struct coap_client *client, int sock, const struct sockaddr goto out; } - ret = coap_pending_init(&internal_req->pending, &internal_req->request, &client->address, - internal_req->retry_count); + /* only TYPE_CON messages need pending tracking */ + if (coap_header_get_type(&internal_req->request) == COAP_TYPE_CON) { + if (retries == -1) { + internal_req->retry_count = DEFAULT_RETRY_AMOUNT; + } else { + internal_req->retry_count = retries; + } - if (ret < 0) { - LOG_ERR("Failed to initialize pending struct"); - k_mutex_unlock(&client->send_mutex); - goto out; - } + ret = coap_pending_init(&internal_req->pending, &internal_req->request, + &client->address, internal_req->retry_count); + + if (ret < 0) { + LOG_ERR("Failed to initialize pending struct"); + k_mutex_unlock(&client->send_mutex); + goto out; + } - coap_pending_cycle(&internal_req->pending); + coap_pending_cycle(&internal_req->pending); + } ret = send_request(sock, internal_req->request.data, internal_req->request.offset, 0, &client->address, client->socklen); @@ -368,8 +371,12 @@ static void report_callback_error(struct coap_client_internal_request *internal_ static bool timeout_expired(struct coap_client_internal_request *internal_req) { + if (internal_req->pending.timeout == 0) { + return false; + } + return (internal_req->request_ongoing && - internal_req->pending.timeout <= k_uptime_get_32()); + internal_req->pending.timeout <= (k_uptime_get() - internal_req->pending.t0)); } static int resend_request(struct coap_client *client, @@ -547,35 +554,6 @@ static int send_ack(struct coap_client *client, const struct coap_packet *req, return 0; } -static int send_reset(struct coap_client *client, const struct coap_packet *req, - uint8_t response_code) -{ - int ret; - uint16_t id; - uint8_t token[COAP_TOKEN_MAX_LEN]; - uint8_t tkl; - struct coap_packet reset; - - id = coap_header_get_id(req); - tkl = response_code ? coap_header_get_token(req, token) : 0; - ret = coap_packet_init(&reset, client->send_buf, MAX_COAP_MSG_LEN, COAP_VERSION, - COAP_TYPE_RESET, tkl, token, response_code, id); - - if (ret < 0) { - LOG_ERR("Error creating CoAP reset message"); - return ret; - } - - ret = send_request(client->fd, reset.data, reset.offset, 0, - &client->address, client->socklen); - if (ret < 0) { - LOG_ERR("Error sending CoAP reset message"); - return ret; - } - - return 0; -} - struct coap_client_internal_request *get_request_with_id(struct coap_client *client, uint16_t message_id) { @@ -631,7 +609,7 @@ static int handle_response(struct coap_client *client, const struct coap_packet */ response_type = coap_header_get_type(response); - internal_req = get_request_with_id(client, coap_header_get_id(response)); + internal_req = get_request_with_token(client, response); /* Reset and Ack need to match the message ID with request */ if ((response_type == COAP_TYPE_ACK || response_type == COAP_TYPE_RESET) && internal_req == NULL) { @@ -649,23 +627,14 @@ static int handle_response(struct coap_client *client, const struct coap_packet /* Separate response coming */ if (payload_len == 0 && response_type == COAP_TYPE_ACK && response_code == COAP_CODE_EMPTY) { - internal_req->pending.t0 = k_uptime_get_32(); + internal_req->pending.t0 = k_uptime_get(); internal_req->pending.timeout = internal_req->pending.t0 + COAP_SEPARATE_TIMEOUT; internal_req->pending.retries = 0; return 1; } - /* Check for tokens - * Separate response doesn't match with message ID, - * check if there is a separate request waiting with matching token - */ - if (internal_req == NULL) { - internal_req = get_request_with_token(client, response); - } - if (internal_req == NULL || !token_compare(internal_req, response)) { - LOG_ERR("Not matching tokens, respond with reset"); - ret = send_reset(client, response, COAP_RESPONSE_CODE_NOT_FOUND); + LOG_WRN("Not matching tokens"); return 1; } @@ -795,7 +764,7 @@ void coap_client_recv(void *coap_cl, void *a, void *b) ret = handle_response(clients[i], &response); if (ret < 0) { - LOG_ERR("Error handling respnse"); + LOG_ERR("Error handling response"); } clients[i]->response_ready = false; diff --git a/subsys/net/lib/config/Kconfig b/subsys/net/lib/config/Kconfig index 0f1e3c50544..5ca788cebf1 100644 --- a/subsys/net/lib/config/Kconfig +++ b/subsys/net/lib/config/Kconfig @@ -188,6 +188,23 @@ config NET_CONFIG_BT_NODE endif # NET_CONFIG_SETTINGS +if NET_DHCPV6 + +config NET_CONFIG_DHCPV6_REQUEST_ADDR + bool "Request IPv6 address when configuring DHCPv6 client" + default y + help + When DHCPv6 is enabled this will configure the DHCPv6 client to + request IPv6 address from the DHCPv6 server. + +config NET_CONFIG_DHCPV6_REQUEST_PREFIX + bool "Request IPv6 prefix when configuring DHCPv6 client" + help + When DHCPv6 is enabled this will configure the DHCPv6 client to + request IPv6 prefix from the DHCPv6 server. + +endif # NET_DHCPV6 + config NET_CONFIG_CLOCK_SNTP_INIT bool "Initialize system clock using SNTP on application startup" depends on SNTP && POSIX_CLOCK diff --git a/subsys/net/lib/config/init.c b/subsys/net/lib/config/init.c index cd2bf711a7d..098003b79b3 100644 --- a/subsys/net/lib/config/init.c +++ b/subsys/net/lib/config/init.c @@ -21,6 +21,7 @@ LOG_MODULE_REGISTER(net_config, CONFIG_NET_CONFIG_LOG_LEVEL); #include #include #include +#include #include #include @@ -203,8 +204,26 @@ static void setup_ipv4(struct net_if *iface) #endif /* CONFIG_NET_IPV4 && !CONFIG_NET_DHCPV4 */ #if defined(CONFIG_NET_NATIVE_IPV6) -#if !defined(CONFIG_NET_CONFIG_MY_IPV6_ADDR) -#error "You need to define an IPv6 address!" + +#if defined(CONFIG_NET_DHCPV6) +static void setup_dhcpv6(struct net_if *iface) +{ + struct net_dhcpv6_params params = { + .request_addr = IS_ENABLED(CONFIG_NET_CONFIG_DHCPV6_REQUEST_ADDR), + .request_prefix = IS_ENABLED(CONFIG_NET_CONFIG_DHCPV6_REQUEST_PREFIX), + }; + + NET_INFO("Running dhcpv6 client..."); + + net_dhcpv6_start(iface, ¶ms); +} +#else /* CONFIG_NET_DHCPV6 */ +#define setup_dhcpv6(...) +#endif /* CONFIG_NET_DHCPV6 */ + +#if !defined(CONFIG_NET_CONFIG_DHCPV6_REQUEST_ADDR) && \ + !defined(CONFIG_NET_CONFIG_MY_IPV6_ADDR) +#error "You need to define an IPv6 address or enable DHCPv6!" #endif static struct net_mgmt_event_callback mgmt6_cb; @@ -249,6 +268,21 @@ static void ipv6_event_handler(struct net_mgmt_event_callback *cb, #if CONFIG_NET_CONFIG_LOG_LEVEL >= LOG_LEVEL_INF NET_INFO("IPv6 address: %s", net_addr_ntop(AF_INET6, &laddr, hr_addr, NET_IPV6_ADDR_LEN)); + + if (ifaddr->addr_type == NET_ADDR_DHCP) { + char remaining_str[] = "infinite"; + uint32_t remaining; + + remaining = net_timeout_remaining(&ifaddr->lifetime, + k_uptime_get_32()); + + if (!ifaddr->is_infinite) { + snprintk(remaining_str, sizeof(remaining_str), + "%u", remaining); + } + + NET_INFO("Lifetime: %s seconds", remaining_str); + } #endif services_notify_ready(NET_CONFIG_NEED_IPV6); @@ -264,6 +298,9 @@ static void setup_ipv6(struct net_if *iface, uint32_t flags) struct net_if_addr *ifaddr; uint32_t mask = NET_EVENT_IPV6_DAD_SUCCEED; + net_mgmt_init_event_callback(&mgmt6_cb, ipv6_event_handler, mask); + net_mgmt_add_event_callback(&mgmt6_cb); + if (sizeof(CONFIG_NET_CONFIG_MY_IPV6_ADDR) == 1) { /* Empty address, skip setting ANY address in this case */ goto exit; @@ -279,9 +316,6 @@ static void setup_ipv6(struct net_if *iface, uint32_t flags) mask |= NET_EVENT_IPV6_ROUTER_ADD; } - net_mgmt_init_event_callback(&mgmt6_cb, ipv6_event_handler, mask); - net_mgmt_add_event_callback(&mgmt6_cb); - /* * check for CMD_ADDR_ADD bit here, NET_EVENT_IPV6_ADDR_ADD is * a combination of _NET_EVENT_IPV6_BASE | NET_EVENT_IPV6_CMD_ADDR_ADD @@ -310,6 +344,7 @@ static void setup_ipv6(struct net_if *iface, uint32_t flags) #else #define setup_ipv6(...) +#define setup_dhcpv6(...) #endif /* CONFIG_NET_IPV6 */ #if defined(CONFIG_NET_NATIVE) @@ -400,6 +435,7 @@ int net_config_init_by_iface(struct net_if *iface, const char *app_info, setup_ipv4(iface); setup_dhcpv4(iface); setup_ipv6(iface, flags); + setup_dhcpv6(iface); /* Network interface did not come up. */ if (timeout > 0 && count < 0) { diff --git a/subsys/net/lib/lwm2m/CMakeLists.txt b/subsys/net/lib/lwm2m/CMakeLists.txt index 5cb0ab158d5..2e895ec381b 100644 --- a/subsys/net/lib/lwm2m/CMakeLists.txt +++ b/subsys/net/lib/lwm2m/CMakeLists.txt @@ -14,6 +14,7 @@ zephyr_library_sources( lwm2m_obj_device.c lwm2m_rw_link_format.c lwm2m_rw_plain_text.c + lwm2m_rw_opaque.c lwm2m_util.c lwm2m_rd_client.c ) diff --git a/subsys/net/lib/lwm2m/Kconfig b/subsys/net/lib/lwm2m/Kconfig index 41ac5bcf1ce..1f5b83f47d0 100644 --- a/subsys/net/lib/lwm2m/Kconfig +++ b/subsys/net/lib/lwm2m/Kconfig @@ -83,6 +83,8 @@ config LWM2M_COAP_BLOCK_TRANSFER help LwM2M messages with a big body that exceed the block size will be split into blocks for sending. + To append CoAP ETag option into outgoing block transfers, CONFIG_SYS_HASH_FUNC32 should + be enabled. config LWM2M_CANCEL_OBSERVE_BY_PATH bool "Use path matching as fallback for cancel-observe" @@ -469,9 +471,19 @@ config LWM2M_ENGINE_DEFAULT_LIFETIME This is also a minimum lifetime that client accepts. If server sets lifetime less than this value, client automatically raises it. +config LWM2M_UPDATE_PERIOD + int "LwM2M engine update period" + default 0 + range 0 4294967295 + help + Time period after the previous update or registration when to send the next update message. + This allows modifying lifetime without affecting the update period. + Set to zero, to allow LWM2M_SECONDS_TO_UPDATE_EARLY and lifetime to control when to + send the update. When both values are set, smallest period will be used. + config LWM2M_SECONDS_TO_UPDATE_EARLY int "LWM2M Registration Update transmission time before timeout" - default 6 + default 10 range 1 4294967295 help Time in seconds before the registration timeout, when the LWM2M diff --git a/subsys/net/lib/lwm2m/lwm2m_engine.c b/subsys/net/lib/lwm2m/lwm2m_engine.c index 91d8c3bd605..651baa30ff6 100644 --- a/subsys/net/lib/lwm2m/lwm2m_engine.c +++ b/subsys/net/lib/lwm2m/lwm2m_engine.c @@ -642,7 +642,7 @@ static int socket_recv_message(struct lwm2m_ctx *client_ctx) } in_buf[len] = 0U; - lwm2m_udp_receive(client_ctx, in_buf, len, &from_addr, handle_request); + lwm2m_udp_receive(client_ctx, in_buf, len, &from_addr); return 0; } @@ -675,7 +675,9 @@ static int socket_send_message(struct lwm2m_ctx *client_ctx) } if (msg->type != COAP_TYPE_CON) { - lwm2m_reset_message(msg, true); + if (!lwm2m_outgoing_is_part_of_blockwise(msg)) { + lwm2m_reset_message(msg, true); + } } return rc; diff --git a/subsys/net/lib/lwm2m/lwm2m_message_handling.c b/subsys/net/lib/lwm2m/lwm2m_message_handling.c index 952a3d9c130..678f358d1ee 100644 --- a/subsys/net/lib/lwm2m/lwm2m_message_handling.c +++ b/subsys/net/lib/lwm2m/lwm2m_message_handling.c @@ -33,6 +33,7 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME); #include #include #include +#include #if defined(CONFIG_LWM2M_DTLS_SUPPORT) #include @@ -47,6 +48,7 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME); #include "lwm2m_rw_link_format.h" #include "lwm2m_rw_oma_tlv.h" #include "lwm2m_rw_plain_text.h" +#include "lwm2m_rw_opaque.h" #include "lwm2m_util.h" #include "lwm2m_rd_client.h" #if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT) @@ -80,6 +82,7 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME); /* Shared set of in-flight LwM2M messages */ static struct lwm2m_message messages[CONFIG_LWM2M_ENGINE_MAX_MESSAGES]; static struct lwm2m_block_context block1_contexts[NUM_BLOCK1_CONTEXT]; +static struct lwm2m_message *ongoing_block2_tx; #if defined(CONFIG_LWM2M_COAP_BLOCK_TRANSFER) /* we need 1 more buffer as the payload is encoded in that buffer first even if @@ -96,7 +99,9 @@ sys_slist_t *lwm2m_engine_obj_list(void); sys_slist_t *lwm2m_engine_obj_inst_list(void); +static int handle_request(struct coap_packet *request, struct lwm2m_message *msg); #if defined(CONFIG_LWM2M_COAP_BLOCK_TRANSFER) +STATIC int build_msg_block_for_send(struct lwm2m_message *msg, uint16_t block_num); struct coap_block_context *lwm2m_output_block_context(void); #endif @@ -299,10 +304,17 @@ STATIC int build_msg_block_for_send(struct lwm2m_message *msg, uint16_t block_nu } msg->cpkt.hdr_len = msg->body_encode_buffer.hdr_len; } else { - /* reuse message for next block */ - tkl = coap_header_get_token(&msg->cpkt, token); + /* reuse message for next block. Copy token from the new query to allow + * CoAP clients to use new token for every query of ongoing transaction + */ lwm2m_reset_message(msg, false); - msg->mid = coap_next_id(); + if (msg->type == COAP_TYPE_ACK) { + msg->mid = coap_header_get_id(msg->in.in_cpkt); + tkl = coap_header_get_token(msg->in.in_cpkt, token); + } else { + msg->mid = coap_next_id(); + tkl = LWM2M_MSG_TOKEN_GENERATE_NEW; + } msg->token = token; msg->tkl = tkl; ret = lwm2m_init_message(msg); @@ -331,10 +343,14 @@ STATIC int build_msg_block_for_send(struct lwm2m_message *msg, uint16_t block_nu return ret; } ret = coap_block_transfer_init(msg->out.block_ctx, lwm2m_default_block_size(), - msg->body_encode_buffer.offset); + complete_payload_len); if (ret < 0) { return ret; } + if (msg->type == COAP_TYPE_ACK) { + ongoing_block2_tx = msg; + } + msg->block_send = true; } else { /* update block context */ msg->out.block_ctx->current = block_num * block_size_bytes; @@ -364,6 +380,7 @@ STATIC int prepare_msg_for_send(struct lwm2m_message *msg) { int ret; uint16_t len; + const uint8_t *payload; /* save the big buffer for later use (splitting blocks) */ msg->body_encode_buffer = msg->cpkt; @@ -373,7 +390,7 @@ STATIC int prepare_msg_for_send(struct lwm2m_message *msg) msg->cpkt.offset = 0; msg->cpkt.max_len = MAX_PACKET_SIZE; - coap_packet_get_payload(&msg->body_encode_buffer, &len); + payload = coap_packet_get_payload(&msg->body_encode_buffer, &len); if (len <= CONFIG_LWM2M_COAP_MAX_MSG_SIZE) { /* copy the packet */ @@ -392,6 +409,16 @@ STATIC int prepare_msg_for_send(struct lwm2m_message *msg) NET_ASSERT(msg->out.block_ctx == NULL, "Expecting to have no context to release"); } else { + /* Before splitting the content, append Etag option to protect the integrity of + * the payload. + */ + if (IS_ENABLED(CONFIG_SYS_HASH_FUNC32)) { + uint32_t hash = sys_hash32(payload, len); + + coap_packet_append_option(&msg->body_encode_buffer, COAP_OPTION_ETAG, + (const uint8_t *)&hash, sizeof(hash)); + } + ret = build_msg_block_for_send(msg, 0); if (ret != 0) { return ret; @@ -400,6 +427,7 @@ STATIC int prepare_msg_for_send(struct lwm2m_message *msg) return 0; } + #endif void lwm2m_engine_context_close(struct lwm2m_ctx *client_ctx) @@ -807,6 +835,10 @@ static int select_writer(struct lwm2m_output_context *out, uint16_t accept) out->writer = &link_format_writer; break; + case LWM2M_FORMAT_APP_OCTET_STREAM: + out->writer = &opaque_writer; + break; + case LWM2M_FORMAT_PLAIN_TEXT: case LWM2M_FORMAT_OMA_PLAIN_TEXT: out->writer = &plain_text_writer; @@ -857,6 +889,9 @@ static int select_reader(struct lwm2m_input_context *in, uint16_t format) switch (format) { case LWM2M_FORMAT_APP_OCTET_STREAM: + in->reader = &opaque_reader; + break; + case LWM2M_FORMAT_PLAIN_TEXT: case LWM2M_FORMAT_OMA_PLAIN_TEXT: in->reader = &plain_text_reader; @@ -1537,6 +1572,8 @@ static int do_read_op(struct lwm2m_message *msg, uint16_t content_format) switch (content_format) { case LWM2M_FORMAT_APP_OCTET_STREAM: + return do_read_op_opaque(msg, content_format); + case LWM2M_FORMAT_PLAIN_TEXT: case LWM2M_FORMAT_OMA_PLAIN_TEXT: return do_read_op_plain_text(msg, content_format); @@ -1916,48 +1953,179 @@ static int do_discover_op(struct lwm2m_message *msg, uint16_t content_format) static int do_write_op(struct lwm2m_message *msg, uint16_t format) { + int r; + switch (format) { case LWM2M_FORMAT_APP_OCTET_STREAM: + r = do_write_op_opaque(msg); + break; + case LWM2M_FORMAT_PLAIN_TEXT: case LWM2M_FORMAT_OMA_PLAIN_TEXT: - return do_write_op_plain_text(msg); + r = do_write_op_plain_text(msg); + break; #ifdef CONFIG_LWM2M_RW_OMA_TLV_SUPPORT case LWM2M_FORMAT_OMA_TLV: case LWM2M_FORMAT_OMA_OLD_TLV: - return do_write_op_tlv(msg); + r = do_write_op_tlv(msg); + break; #endif #ifdef CONFIG_LWM2M_RW_JSON_SUPPORT case LWM2M_FORMAT_OMA_JSON: case LWM2M_FORMAT_OMA_OLD_JSON: - return do_write_op_json(msg); + r = do_write_op_json(msg); + break; #endif #if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT) case LWM2M_FORMAT_APP_SEML_JSON: - return do_write_op_senml_json(msg); + r = do_write_op_senml_json(msg); + break; #endif #ifdef CONFIG_LWM2M_RW_CBOR_SUPPORT case LWM2M_FORMAT_APP_CBOR: - return do_write_op_cbor(msg); + r = do_write_op_cbor(msg); + break; #endif #ifdef CONFIG_LWM2M_RW_SENML_CBOR_SUPPORT case LWM2M_FORMAT_APP_SENML_CBOR: - return do_write_op_senml_cbor(msg); + r = do_write_op_senml_cbor(msg); + break; #endif default: LOG_ERR("Unsupported format: %u", format); - return -ENOMSG; + r = -ENOMSG; + break; } + + return r; +} + +static int parse_write_op(struct lwm2m_message *msg, uint16_t format) +{ + int block_opt, block_num; + struct lwm2m_block_context *block_ctx = NULL; + enum coap_block_size block_size; + bool last_block = false; + int r; + uint16_t payload_len = 0U; + const uint8_t *payload_start; + + /* setup incoming data */ + payload_start = coap_packet_get_payload(msg->in.in_cpkt, &payload_len); + if (payload_len > 0) { + msg->in.offset = payload_start - msg->in.in_cpkt->data; + } else { + msg->in.offset = msg->in.in_cpkt->offset; + } + + /* Check for block transfer */ + block_opt = coap_get_option_int(msg->in.in_cpkt, COAP_OPTION_BLOCK1); + if (block_opt > 0) { + last_block = !GET_MORE(block_opt); + + /* RFC7252: 4.6. Message Size */ + block_size = GET_BLOCK_SIZE(block_opt); + if (!last_block && coap_block_size_to_bytes(block_size) > payload_len) { + LOG_DBG("Trailing payload is discarded!"); + return -EFBIG; + } + + block_num = GET_BLOCK_NUM(block_opt); + + /* Try to retrieve existing block context. If one not exists, + * and we've received first block, allocate new context. + */ + r = get_block_ctx(&msg->path, &block_ctx); + if (r < 0 && block_num == 0) { + r = init_block_ctx(&msg->path, &block_ctx); + } + + if (r < 0) { + LOG_ERR("Cannot find block context"); + return r; + } + + msg->in.block_ctx = block_ctx; + + if (block_num < block_ctx->expected) { + LOG_WRN("Block already handled %d, expected %d", block_num, + block_ctx->expected); + return 0; + } + if (block_num > block_ctx->expected) { + LOG_WRN("Block out of order %d, expected %d", block_num, + block_ctx->expected); + r = -EFAULT; + return r; + } + r = coap_update_from_block(msg->in.in_cpkt, &block_ctx->ctx); + if (r < 0) { + LOG_ERR("Error from block update: %d", r); + return r; + } + + block_ctx->last_block = last_block; + + /* Initial block sent by the server might be larger than + * our block size therefore it is needed to take this + * into account when calculating next expected block + * number. + */ + block_ctx->expected += GET_BLOCK_SIZE(block_opt) - block_ctx->ctx.block_size + 1; + + /* Handle blockwise 1 (Part 1): Set response code */ + if (!last_block) { + msg->code = COAP_RESPONSE_CODE_CONTINUE; + } + } + + r = do_write_op(msg, format); + + /* Handle blockwise 1 (Part 2): Append BLOCK1 option / free context */ + if (block_ctx) { + if (r >= 0 && !last_block) { + /* More to come, ack with correspond block # */ + r = coap_append_block1_option(msg->out.out_cpkt, &block_ctx->ctx); + if (r < 0) { + /* report as internal server error */ + LOG_ERR("Fail adding block1 option: %d", r); + r = -EINVAL; + } + } + if (r < 0 || last_block) { + /* Free context when finished or when there is error */ + free_block_ctx(block_ctx); + + } + } + + return r; } static int do_composite_write_op(struct lwm2m_message *msg, uint16_t format) { + uint16_t payload_len = 0U; + const uint8_t *payload_start; + + /* setup incoming data */ + payload_start = coap_packet_get_payload(msg->in.in_cpkt, &payload_len); + if (payload_len > 0) { + msg->in.offset = payload_start - msg->in.in_cpkt->data; + } else { + msg->in.offset = msg->in.in_cpkt->offset; + } + + if (coap_get_option_int(msg->in.in_cpkt, COAP_OPTION_BLOCK1) >= 0) { + return -ENOTSUP; + } + switch (format) { #if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT) case LWM2M_FORMAT_APP_SEML_JSON: @@ -2052,7 +2220,7 @@ static int lwm2m_exec_handler(struct lwm2m_message *msg) return -ENOENT; } -int handle_request(struct coap_packet *request, struct lwm2m_message *msg) +static int handle_request(struct coap_packet *request, struct lwm2m_message *msg) { int r; uint8_t code; @@ -2062,13 +2230,6 @@ int handle_request(struct coap_packet *request, struct lwm2m_message *msg) uint8_t tkl = 0U; uint16_t format = LWM2M_FORMAT_NONE, accept; int observe = -1; /* default to -1, 0 = ENABLE, 1 = DISABLE */ - int block_opt, block_num; - struct lwm2m_block_context *block_ctx = NULL; - enum coap_block_size block_size; - uint16_t payload_len = 0U; - bool last_block = false; - bool ignore = false; - const uint8_t *payload_start; /* set CoAP request / message */ msg->in.in_cpkt = request; @@ -2240,197 +2401,111 @@ int handle_request(struct coap_packet *request, struct lwm2m_message *msg) break; } - /* setup incoming data */ - payload_start = coap_packet_get_payload(msg->in.in_cpkt, &payload_len); - if (payload_len > 0) { - msg->in.offset = payload_start - msg->in.in_cpkt->data; - } else { - msg->in.offset = msg->in.in_cpkt->offset; - } - - /* Check for block transfer */ - - block_opt = coap_get_option_int(msg->in.in_cpkt, COAP_OPTION_BLOCK1); - if (block_opt > 0) { - last_block = !GET_MORE(block_opt); - - /* RFC7252: 4.6. Message Size */ - block_size = GET_BLOCK_SIZE(block_opt); - if (!last_block && coap_block_size_to_bytes(block_size) > payload_len) { - LOG_DBG("Trailing payload is discarded!"); - r = -EFBIG; - goto error; - } - - block_num = GET_BLOCK_NUM(block_opt); - - /* Try to retrieve existing block context. If one not exists, - * and we've received first block, allocate new context. - */ - r = get_block_ctx(&msg->path, &block_ctx); - if (r < 0 && block_num == 0) { - r = init_block_ctx(&msg->path, &block_ctx); - } - - if (r < 0) { - LOG_ERR("Cannot find block context"); - goto error; - } - - msg->in.block_ctx = block_ctx; - - if (block_num < block_ctx->expected) { - LOG_WRN("Block already handled %d, expected %d", block_num, - block_ctx->expected); - ignore = true; - } else if (block_num > block_ctx->expected) { - LOG_WRN("Block out of order %d, expected %d", block_num, - block_ctx->expected); - r = -EFAULT; - goto error; - } else { - r = coap_update_from_block(msg->in.in_cpkt, &block_ctx->ctx); - if (r < 0) { - LOG_ERR("Error from block update: %d", r); - goto error; - } - - block_ctx->last_block = last_block; - - /* Initial block sent by the server might be larger than - * our block size therefore it is needed to take this - * into account when calculating next expected block - * number. - */ - block_ctx->expected += - GET_BLOCK_SIZE(block_opt) - block_ctx->ctx.block_size + 1; - } - - /* Handle blockwise 1 (Part 1): Set response code */ - if (!last_block) { - msg->code = COAP_RESPONSE_CODE_CONTINUE; - } - } - /* render CoAP packet header */ r = lwm2m_init_message(msg); if (r < 0) { goto error; } - if (!ignore) { #if defined(CONFIG_LWM2M_ACCESS_CONTROL_ENABLE) - r = access_control_check_access(msg->path.obj_id, msg->path.obj_inst_id, - msg->ctx->srv_obj_inst, msg->operation, - msg->ctx->bootstrap_mode); - if (r < 0) { - LOG_ERR("Access denied - Server obj %u does not have proper access to " - "resource", - msg->ctx->srv_obj_inst); - goto error; - } + r = access_control_check_access(msg->path.obj_id, msg->path.obj_inst_id, + msg->ctx->srv_obj_inst, msg->operation, + msg->ctx->bootstrap_mode); + if (r < 0) { + LOG_ERR("Access denied - Server obj %u does not have proper access to " + "resource", + msg->ctx->srv_obj_inst); + goto error; + } #endif - switch (msg->operation) { + if (msg->path.obj_id == LWM2M_OBJECT_SECURITY_ID && !msg->ctx->bootstrap_mode) { + r = -EACCES; + goto error; + } - case LWM2M_OP_READ: - if (observe >= 0) { - /* Validate That Token is valid for Observation */ - if (!msg->token) { - LOG_ERR("OBSERVE request missing token"); - r = -EINVAL; - goto error; - } + switch (msg->operation) { - if ((code & COAP_REQUEST_MASK) == COAP_METHOD_GET) { - /* Normal Observation Request or Cancel */ - r = lwm2m_engine_observation_handler(msg, observe, accept, - false); - if (r < 0) { - goto error; - } + case LWM2M_OP_READ: + if (observe >= 0) { + /* Validate That Token is valid for Observation */ + if (!msg->token) { + LOG_ERR("OBSERVE request missing token"); + r = -EINVAL; + goto error; + } - r = do_read_op(msg, accept); - } else { - /* Composite Observation request & cancel handler */ - r = lwm2m_engine_observation_handler(msg, observe, accept, - true); - if (r < 0) { - goto error; - } + if ((code & COAP_REQUEST_MASK) == COAP_METHOD_GET) { + /* Normal Observation Request or Cancel */ + r = lwm2m_engine_observation_handler(msg, observe, accept, + false); + if (r < 0) { + goto error; } + + r = do_read_op(msg, accept); } else { - if ((code & COAP_REQUEST_MASK) == COAP_METHOD_GET) { - r = do_read_op(msg, accept); - } else { - r = do_composite_read_op(msg, accept); + /* Composite Observation request & cancel handler */ + r = lwm2m_engine_observation_handler(msg, observe, accept, + true); + if (r < 0) { + goto error; } } - break; - - case LWM2M_OP_DISCOVER: - r = do_discover_op(msg, accept); - break; - - case LWM2M_OP_WRITE: - case LWM2M_OP_CREATE: - if ((code & COAP_REQUEST_MASK) == COAP_METHOD_IPATCH) { - /* iPATCH is for Composite purpose */ - r = do_composite_write_op(msg, format); + } else { + if ((code & COAP_REQUEST_MASK) == COAP_METHOD_GET) { + r = do_read_op(msg, accept); } else { - /* Single resource write Operation */ - r = do_write_op(msg, format); + r = do_composite_read_op(msg, accept); } + } + break; + + case LWM2M_OP_DISCOVER: + r = do_discover_op(msg, accept); + break; + + case LWM2M_OP_WRITE: + case LWM2M_OP_CREATE: + if ((code & COAP_REQUEST_MASK) == COAP_METHOD_IPATCH) { + /* iPATCH is for Composite purpose */ + r = do_composite_write_op(msg, format); + } else { + /* Single resource write Operation */ + r = parse_write_op(msg, format); + } #if defined(CONFIG_LWM2M_ACCESS_CONTROL_ENABLE) - if (msg->operation == LWM2M_OP_CREATE && r >= 0) { - access_control_add(msg->path.obj_id, msg->path.obj_inst_id, - msg->ctx->srv_obj_inst); - } + if (msg->operation == LWM2M_OP_CREATE && r >= 0) { + access_control_add(msg->path.obj_id, msg->path.obj_inst_id, + msg->ctx->srv_obj_inst); + } #endif - break; + break; - case LWM2M_OP_WRITE_ATTR: - r = lwm2m_write_attr_handler(obj, msg); - break; + case LWM2M_OP_WRITE_ATTR: + r = lwm2m_write_attr_handler(obj, msg); + break; - case LWM2M_OP_EXECUTE: - r = lwm2m_exec_handler(msg); - break; + case LWM2M_OP_EXECUTE: + r = lwm2m_exec_handler(msg); + break; - case LWM2M_OP_DELETE: + case LWM2M_OP_DELETE: #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP) - if (msg->ctx->bootstrap_mode) { - r = bootstrap_delete(msg); - break; - } -#endif - r = lwm2m_delete_handler(msg); + if (msg->ctx->bootstrap_mode) { + r = bootstrap_delete(msg); break; - - default: - LOG_ERR("Unknown operation: %u", msg->operation); - r = -EINVAL; } +#endif + r = lwm2m_delete_handler(msg); + break; - if (r < 0) { - goto error; - } + default: + LOG_ERR("Unknown operation: %u", msg->operation); + r = -EINVAL; } - /* Handle blockwise 1 (Part 2): Append BLOCK1 option / free context */ - if (block_ctx) { - if (!last_block) { - /* More to come, ack with correspond block # */ - r = coap_append_block1_option(msg->out.out_cpkt, &block_ctx->ctx); - if (r < 0) { - /* report as internal server error */ - LOG_ERR("Fail adding block1 option: %d", r); - r = -EINVAL; - goto error; - } - } else { - /* Free context when finished */ - free_block_ctx(block_ctx); - } + if (r < 0) { + goto error; } return 0; @@ -2465,9 +2540,6 @@ int handle_request(struct coap_packet *request, struct lwm2m_message *msg) LOG_ERR("Error recreating message: %d", r); } - /* Free block context when error happened */ - free_block_ctx(block_ctx); - return 0; } @@ -2511,20 +2583,70 @@ static int lwm2m_response_promote_to_con(struct lwm2m_message *msg) return ret; } +static struct lwm2m_message *find_ongoing_block2_tx(void) +{ + /* TODO: I could try to check if there is Request-Tags attached, and then match queries + * for those, but currently popular LwM2M servers don't attach those tags, so in reality + * I have no way of properly matching query with BLOCK2 option to a previous query. + * Therefore we can only support one ongoing BLOCK2 transfer and assume all BLOCK2 requests + * are part of currently ongoing one. + */ + return ongoing_block2_tx; +} + +static void clear_ongoing_block2_tx(void) +{ + if (ongoing_block2_tx) { + LOG_DBG("clear"); + lwm2m_reset_message(ongoing_block2_tx, true); + ongoing_block2_tx = NULL; + } +} + +static void handle_ongoing_block2_tx(struct lwm2m_message *msg, struct coap_packet *cpkt) +{ +#if defined(CONFIG_LWM2M_COAP_BLOCK_TRANSFER) + int r; + uint8_t block; + + r = coap_get_block2_option(cpkt, &block); + if (r < 0) { + LOG_ERR("Failed to parse BLOCK2"); + return; + } + + msg->in.in_cpkt = cpkt; + + r = build_msg_block_for_send(msg, block); + if (r < 0) { + clear_ongoing_block2_tx(); + LOG_ERR("Unable to build next block of lwm2m message! r=%d", r); + return; + } + + r = lwm2m_send_message_async(msg); + if (r < 0) { + clear_ongoing_block2_tx(); + LOG_ERR("Unable to send next block of lwm2m message!"); + return; + } +#endif +} + void lwm2m_udp_receive(struct lwm2m_ctx *client_ctx, uint8_t *buf, uint16_t buf_len, - struct sockaddr *from_addr, udp_request_handler_cb_t udp_request_handler) + struct sockaddr *from_addr) { struct lwm2m_message *msg = NULL; struct coap_pending *pending; struct coap_reply *reply; struct coap_packet response; int r; - uint8_t token[8]; #if defined(CONFIG_LWM2M_COAP_BLOCK_TRANSFER) bool more_blocks = false; uint8_t block_num; uint8_t last_block_num; #endif + bool has_block2; r = coap_packet_parse(&response, buf, buf_len, NULL, 0); if (r < 0) { @@ -2532,7 +2654,7 @@ void lwm2m_udp_receive(struct lwm2m_ctx *client_ctx, uint8_t *buf, uint16_t buf_ return; } - (void)coap_header_get_token(&response, token); + has_block2 = coap_get_option_int(&response, COAP_OPTION_BLOCK2) > 0 ? true : false; pending = coap_pending_received(&response, client_ctx->pendings, ARRAY_SIZE(client_ctx->pendings)); if (pending && coap_header_get_type(&response) == COAP_TYPE_ACK) { @@ -2639,12 +2761,18 @@ void lwm2m_udp_receive(struct lwm2m_ctx *client_ctx, uint8_t *buf, uint16_t buf_ return; } - /* - * If no normal response handler is found, then this is - * a new request coming from the server. Let's look - * at registered objects to find a handler. - */ - if (udp_request_handler && coap_header_get_type(&response) == COAP_TYPE_CON) { + if (coap_header_get_type(&response) == COAP_TYPE_CON) { + if (has_block2 && IS_ENABLED(CONFIG_LWM2M_COAP_BLOCK_TRANSFER)) { + msg = find_ongoing_block2_tx(); + if (msg) { + return handle_ongoing_block2_tx(msg, &response); + } + return; + } + + /* Clear out existing Block2 transfers when new requests come */ + clear_ongoing_block2_tx(); + msg = lwm2m_get_message(client_ctx); if (!msg) { LOG_ERR("Unable to get a lwm2m message!"); @@ -2662,7 +2790,7 @@ void lwm2m_udp_receive(struct lwm2m_ctx *client_ctx, uint8_t *buf, uint16_t buf_ lwm2m_registry_lock(); /* process the response to this request */ - r = udp_request_handler(&response, msg); + r = handle_request(&response, msg); lwm2m_registry_unlock(); if (r < 0) { return; diff --git a/subsys/net/lib/lwm2m/lwm2m_message_handling.h b/subsys/net/lib/lwm2m/lwm2m_message_handling.h index 12ba9e762fd..c41fbbac0ee 100644 --- a/subsys/net/lib/lwm2m/lwm2m_message_handling.h +++ b/subsys/net/lib/lwm2m/lwm2m_message_handling.h @@ -39,8 +39,6 @@ #define NUM_OUTPUT_BLOCK_CONTEXT CONFIG_LWM2M_NUM_OUTPUT_BLOCK_CONTEXT #endif -/* Establish a request handler callback type */ -typedef int (*udp_request_handler_cb_t)(struct coap_packet *request, struct lwm2m_message *msg); /* LwM2M message functions */ struct lwm2m_message *lwm2m_get_message(struct lwm2m_ctx *client_ctx); struct lwm2m_message *find_msg(struct coap_pending *pending, struct coap_reply *reply); @@ -49,10 +47,8 @@ void lm2m_message_clear_allocations(struct lwm2m_message *msg); int lwm2m_init_message(struct lwm2m_message *msg); int lwm2m_send_message_async(struct lwm2m_message *msg); -int handle_request(struct coap_packet *request, struct lwm2m_message *msg); - void lwm2m_udp_receive(struct lwm2m_ctx *client_ctx, uint8_t *buf, uint16_t buf_len, - struct sockaddr *from_addr, udp_request_handler_cb_t udp_request_handler); + struct sockaddr *from_addr); int generate_notify_message(struct lwm2m_ctx *ctx, struct observe_node *obs, void *user_data); /* Notification and Send operation */ @@ -80,4 +76,9 @@ enum coap_block_size lwm2m_default_block_size(void); int lwm2m_parse_peerinfo(char *url, struct lwm2m_ctx *client_ctx, bool is_firmware_uri); void lwm2m_clear_block_contexts(void); +static inline bool lwm2m_outgoing_is_part_of_blockwise(struct lwm2m_message *msg) +{ + return msg->block_send; +} + #endif /* LWM2M_MESSAGE_HANDLING_H */ diff --git a/subsys/net/lib/lwm2m/lwm2m_obj_access_control.c b/subsys/net/lib/lwm2m/lwm2m_obj_access_control.c index 1a83ef5ad29..383dbe9b539 100644 --- a/subsys/net/lib/lwm2m/lwm2m_obj_access_control.c +++ b/subsys/net/lib/lwm2m/lwm2m_obj_access_control.c @@ -432,10 +432,10 @@ static int ac_control_init(void) ac_obj.create_cb = ac_create; lwm2m_register_obj(&ac_obj); -#if !IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP) - /* add the objects/object instances that were created before the ac control object */ - add_existing_objects(); -#endif /* CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP */ + if (!IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)) { + /* add the objects/object instances that were created before the ac control */ + add_existing_objects(); + } return 0; } diff --git a/subsys/net/lib/lwm2m/lwm2m_object.h b/subsys/net/lib/lwm2m/lwm2m_object.h index ca8fdf12fd0..170a99c023d 100644 --- a/subsys/net/lib/lwm2m/lwm2m_object.h +++ b/subsys/net/lib/lwm2m/lwm2m_object.h @@ -521,8 +521,11 @@ struct lwm2m_message { /** Incoming message action */ uint8_t operation; - /* Information whether the message was acknowledged. */ + /** Information whether the message was acknowledged. */ bool acknowledged : 1; + + /** Indicate that this is part of outgoing block transfer. */ + bool block_send : 1; }; /* LWM2M format writer for the various formats supported */ @@ -705,71 +708,95 @@ static inline int engine_put_end_ri(struct lwm2m_output_context *out, return 0; } -static inline int engine_put_s8(struct lwm2m_output_context *out, - struct lwm2m_obj_path *path, int8_t value) +static inline int engine_put_s8(struct lwm2m_output_context *out, struct lwm2m_obj_path *path, + int8_t value) { - return out->writer->put_s8(out, path, value); + if (out->writer->put_s8) { + return out->writer->put_s8(out, path, value); + } + return -ENOTSUP; } -static inline int engine_put_s16(struct lwm2m_output_context *out, - struct lwm2m_obj_path *path, int16_t value) +static inline int engine_put_s16(struct lwm2m_output_context *out, struct lwm2m_obj_path *path, + int16_t value) { - return out->writer->put_s16(out, path, value); + if (out->writer->put_s16) { + return out->writer->put_s16(out, path, value); + } + return -ENOTSUP; } -static inline int engine_put_s32(struct lwm2m_output_context *out, - struct lwm2m_obj_path *path, int32_t value) +static inline int engine_put_s32(struct lwm2m_output_context *out, struct lwm2m_obj_path *path, + int32_t value) { - return out->writer->put_s32(out, path, value); + if (out->writer->put_s32) { + return out->writer->put_s32(out, path, value); + } + return -ENOTSUP; } -static inline int engine_put_s64(struct lwm2m_output_context *out, - struct lwm2m_obj_path *path, int64_t value) +static inline int engine_put_s64(struct lwm2m_output_context *out, struct lwm2m_obj_path *path, + int64_t value) { - return out->writer->put_s64(out, path, value); + if (out->writer->put_s64) { + return out->writer->put_s64(out, path, value); + } + return -ENOTSUP; } -static inline int engine_put_string(struct lwm2m_output_context *out, - struct lwm2m_obj_path *path, char *buf, - size_t buflen) +static inline int engine_put_string(struct lwm2m_output_context *out, struct lwm2m_obj_path *path, + char *buf, size_t buflen) { - return out->writer->put_string(out, path, buf, buflen); + if (out->writer->put_string) { + return out->writer->put_string(out, path, buf, buflen); + } + return -ENOTSUP; } -static inline int engine_put_float(struct lwm2m_output_context *out, - struct lwm2m_obj_path *path, double *value) +static inline int engine_put_float(struct lwm2m_output_context *out, struct lwm2m_obj_path *path, + double *value) { - return out->writer->put_float(out, path, value); + if (out->writer->put_float) { + return out->writer->put_float(out, path, value); + } + return -ENOTSUP; } -static inline int engine_put_time(struct lwm2m_output_context *out, - struct lwm2m_obj_path *path, time_t value) +static inline int engine_put_time(struct lwm2m_output_context *out, struct lwm2m_obj_path *path, + time_t value) { - return out->writer->put_time(out, path, value); + if (out->writer->put_time) { + return out->writer->put_time(out, path, value); + } + return -ENOTSUP; } -static inline int engine_put_bool(struct lwm2m_output_context *out, - struct lwm2m_obj_path *path, bool value) +static inline int engine_put_bool(struct lwm2m_output_context *out, struct lwm2m_obj_path *path, + bool value) { - return out->writer->put_bool(out, path, value); + if (out->writer->put_bool) { + return out->writer->put_bool(out, path, value); + } + return -ENOTSUP; } -static inline int engine_put_opaque(struct lwm2m_output_context *out, - struct lwm2m_obj_path *path, char *buf, - size_t buflen) +static inline int engine_put_opaque(struct lwm2m_output_context *out, struct lwm2m_obj_path *path, + char *buf, size_t buflen) { if (out->writer->put_opaque) { return out->writer->put_opaque(out, path, buf, buflen); } - return 0; + return -ENOTSUP; } -static inline int engine_put_objlnk(struct lwm2m_output_context *out, - struct lwm2m_obj_path *path, +static inline int engine_put_objlnk(struct lwm2m_output_context *out, struct lwm2m_obj_path *path, struct lwm2m_objlnk *value) { - return out->writer->put_objlnk(out, path, value); + if (out->writer->put_objlnk) { + return out->writer->put_objlnk(out, path, value); + } + return -ENOTSUP; } static inline int engine_put_corelink(struct lwm2m_output_context *out, @@ -793,53 +820,68 @@ static inline int engine_put_timestamp(struct lwm2m_output_context *out, time_t static inline int engine_get_s32(struct lwm2m_input_context *in, int32_t *value) { - return in->reader->get_s32(in, value); + if (in->reader->get_s32) { + return in->reader->get_s32(in, value); + } + return -ENOTSUP; } static inline int engine_get_s64(struct lwm2m_input_context *in, int64_t *value) { - return in->reader->get_s64(in, value); + if (in->reader->get_s64) { + return in->reader->get_s64(in, value); + } + return -ENOTSUP; } -static inline int engine_get_string(struct lwm2m_input_context *in, - uint8_t *buf, size_t buflen) +static inline int engine_get_string(struct lwm2m_input_context *in, uint8_t *buf, size_t buflen) { - return in->reader->get_string(in, buf, buflen); + if (in->reader->get_string) { + return in->reader->get_string(in, buf, buflen); + } + return -ENOTSUP; } static inline int engine_get_time(struct lwm2m_input_context *in, time_t *value) { - return in->reader->get_time(in, value); + if (in->reader->get_time) { + return in->reader->get_time(in, value); + } + return -ENOTSUP; } -static inline int engine_get_float(struct lwm2m_input_context *in, - double *value) +static inline int engine_get_float(struct lwm2m_input_context *in, double *value) { - return in->reader->get_float(in, value); + if (in->reader->get_float) { + return in->reader->get_float(in, value); + } + return -ENOTSUP; } static inline int engine_get_bool(struct lwm2m_input_context *in, bool *value) { - return in->reader->get_bool(in, value); + if (in->reader->get_bool) { + return in->reader->get_bool(in, value); + } + return -ENOTSUP; } -static inline int engine_get_opaque(struct lwm2m_input_context *in, - uint8_t *buf, size_t buflen, - struct lwm2m_opaque_context *opaque, - bool *last_block) +static inline int engine_get_opaque(struct lwm2m_input_context *in, uint8_t *buf, size_t buflen, + struct lwm2m_opaque_context *opaque, bool *last_block) { if (in->reader->get_opaque) { - return in->reader->get_opaque(in, buf, buflen, - opaque, last_block); + return in->reader->get_opaque(in, buf, buflen, opaque, last_block); } - return 0; + return -ENOTSUP; } -static inline int engine_get_objlnk(struct lwm2m_input_context *in, - struct lwm2m_objlnk *value) +static inline int engine_get_objlnk(struct lwm2m_input_context *in, struct lwm2m_objlnk *value) { - return in->reader->get_objlnk(in, value); + if (in->reader->get_objlnk) { + return in->reader->get_objlnk(in, value); + } + return -ENOTSUP; } #endif /* LWM2M_OBJECT_H_ */ diff --git a/subsys/net/lib/lwm2m/lwm2m_rd_client.c b/subsys/net/lib/lwm2m/lwm2m_rd_client.c index 0f4405bfc92..ca539fcded6 100644 --- a/subsys/net/lib/lwm2m/lwm2m_rd_client.c +++ b/subsys/net/lib/lwm2m/lwm2m_rd_client.c @@ -64,12 +64,13 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME); #include "lwm2m_util.h" #define LWM2M_RD_CLIENT_URI "rd" -#define SECONDS_TO_UPDATE_EARLY CONFIG_LWM2M_SECONDS_TO_UPDATE_EARLY #define CLIENT_EP_LEN CONFIG_LWM2M_RD_CLIENT_ENDPOINT_NAME_MAX_LENGTH #define CLIENT_BINDING_LEN sizeof("UQ") #define CLIENT_QUEUE_LEN sizeof("Q") #define DELAY_BEFORE_CLOSING (1 * MSEC_PER_SEC) #define DELAY_FOR_ACK 100U +#define EXCHANGE_LIFETIME 247U +#define MINIMUM_PERIOD 15 static void sm_handle_registration_update_failure(void); static int sm_send_registration_msg(void); @@ -88,12 +89,10 @@ static void set_sm_state(uint8_t sm_state); enum sm_engine_state { ENGINE_IDLE, ENGINE_INIT, -#if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP) ENGINE_DO_BOOTSTRAP_REG, ENGINE_BOOTSTRAP_REG_SENT, ENGINE_BOOTSTRAP_REG_DONE, ENGINE_BOOTSTRAP_TRANS_DONE, -#endif ENGINE_DO_REGISTRATION, ENGINE_SEND_REGISTRATION, ENGINE_REGISTRATION_SENT, @@ -120,6 +119,7 @@ struct lwm2m_rd_client_info { int64_t last_update; int64_t last_tx; int64_t next_event; + int64_t last_state_change; char ep_name[CLIENT_EP_LEN]; char server_ep[CLIENT_EP_LEN]; @@ -172,6 +172,7 @@ void engine_update_tx_time(void) static void next_event_at(int64_t timestamp) { + client.next_event = timestamp; (void)lwm2m_engine_call_at(lwm2m_rd_client_service, timestamp); } @@ -214,6 +215,8 @@ static void set_sm_state_delayed(uint8_t sm_state, int64_t delay_ms) } } else if (sm_state == ENGINE_UPDATE_REGISTRATION) { event = LWM2M_RD_CLIENT_EVENT_REG_UPDATE; + } else if (sm_state == ENGINE_DEREGISTER) { + event = LWM2M_RD_CLIENT_EVENT_DEREGISTER; } if (sm_is_suspended()) { @@ -236,6 +239,7 @@ static void set_sm_state_delayed(uint8_t sm_state, int64_t delay_ms) lwm2m_close_socket(client.ctx); } } + client.last_state_change = k_uptime_get(); next_event_at(k_uptime_get() + delay_ms); k_mutex_unlock(&client.mutex); } @@ -351,14 +355,20 @@ static void sm_handle_failure_state(enum sm_engine_state sm_state) static void socket_fault_cb(int error) { LOG_ERR("RD Client socket error: %d", error); + lwm2m_socket_close(client.ctx); - if (sm_is_bootstrap()) { + if (IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP) && sm_is_bootstrap()) { client.ctx->sec_obj_inst = -1; /* force full registration */ client.last_update = 0; - } - lwm2m_socket_close(client.ctx); + if (get_sm_state() == ENGINE_BOOTSTRAP_TRANS_DONE) { + /* Ignore the error, some servers close the connection immediately + * after receiving Ack to Bootstrap-Finish command. + */ + return; + } + } /* Network error state causes engine to re-register, * so only trigger that state if we are not stopping the @@ -1094,11 +1104,21 @@ static int sm_do_registration(void) static int64_t next_update(void) { - /* - * check for lifetime seconds - SECONDS_TO_UPDATE_EARLY - * so that we can update early and avoid lifetime timeout - */ - return client.last_update + (client.lifetime - SECONDS_TO_UPDATE_EARLY) * MSEC_PER_SEC; + int64_t next; + int64_t period = CONFIG_LWM2M_UPDATE_PERIOD; + int64_t early = CONFIG_LWM2M_SECONDS_TO_UPDATE_EARLY; + + if (period == 0) { + period = client.lifetime; + } + if (early > client.lifetime) { + early = client.lifetime; + } + + next = MIN(period, client.lifetime - early); + next = MAX(next, MINIMUM_PERIOD); + + return client.last_update + next * MSEC_PER_SEC; } static int64_t next_rx_off(void) @@ -1288,8 +1308,11 @@ static void lwm2m_rd_client_service(struct k_work *work) { k_mutex_lock(&client.mutex, K_FOREVER); + int64_t timeout = 0; + if (client.ctx) { LOG_DBG("State: %d", get_sm_state()); + client.next_event = INT64_MAX; switch (get_sm_state()) { case ENGINE_IDLE: if (client.ctx->sock_fd > -1) { @@ -1312,10 +1335,12 @@ static void lwm2m_rd_client_service(struct k_work *work) case ENGINE_BOOTSTRAP_REG_SENT: /* wait for bootstrap registration done */ + timeout = EXCHANGE_LIFETIME; break; case ENGINE_BOOTSTRAP_REG_DONE: /* wait for transfer done */ + timeout = EXCHANGE_LIFETIME; break; case ENGINE_BOOTSTRAP_TRANS_DONE: @@ -1333,6 +1358,7 @@ static void lwm2m_rd_client_service(struct k_work *work) case ENGINE_REGISTRATION_SENT: /* wait registration to be done or timeout */ + timeout = EXCHANGE_LIFETIME; break; case ENGINE_REGISTRATION_DONE: @@ -1346,6 +1372,7 @@ static void lwm2m_rd_client_service(struct k_work *work) case ENGINE_UPDATE_SENT: /* wait update to be done or abort */ + timeout = EXCHANGE_LIFETIME; break; case ENGINE_DEREGISTER: @@ -1354,6 +1381,7 @@ static void lwm2m_rd_client_service(struct k_work *work) case ENGINE_DEREGISTER_SENT: /* wait for deregister to be done or reset */ + timeout = EXCHANGE_LIFETIME; break; case ENGINE_DEREGISTERED: @@ -1369,6 +1397,17 @@ static void lwm2m_rd_client_service(struct k_work *work) LOG_ERR("Unhandled state: %d", get_sm_state()); } + + if (timeout) { + int64_t end = client.last_state_change + timeout * MSEC_PER_SEC; + + if (end < k_uptime_get()) { + LOG_DBG("State machine have timed out"); + sm_handle_timeout_state(NULL, ENGINE_INIT); + } else if (client.next_event > end) { + next_event_at(end); + } + } } k_mutex_unlock(&client.mutex); diff --git a/subsys/net/lib/lwm2m/lwm2m_rw_opaque.c b/subsys/net/lib/lwm2m/lwm2m_rw_opaque.c new file mode 100644 index 00000000000..90cb4619d9a --- /dev/null +++ b/subsys/net/lib/lwm2m/lwm2m_rw_opaque.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * Copyright (c) 2015, Yanzi Networks AB. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_MODULE_NAME net_lwm2m_opaque +#define LOG_LEVEL CONFIG_LWM2M_LOG_LEVEL + +#include +LOG_MODULE_REGISTER(LOG_MODULE_NAME); + +#include +#include +#include +#include +#include + +#include "lwm2m_object.h" +#include "lwm2m_rw_opaque.h" +#include "lwm2m_engine.h" +#include "lwm2m_util.h" + +static int get_opaque(struct lwm2m_input_context *in, uint8_t *value, + size_t buflen, struct lwm2m_opaque_context *opaque, + bool *last_block) +{ + uint16_t in_len; + + if (opaque->remaining == 0) { + coap_packet_get_payload(in->in_cpkt, &in_len); + + if (in_len == 0) { + return -ENODATA; + } + + if (in->block_ctx != NULL) { + uint32_t block_num = + in->block_ctx->ctx.current / + coap_block_size_to_bytes( + in->block_ctx->ctx.block_size); + + if (block_num == 0) { + opaque->len = in->block_ctx->ctx.total_size; + } + + if (opaque->len == 0) { + /* No size1 option provided, use current + * payload size. This will reset on next packet + * received. + */ + opaque->remaining = in_len; + } else { + opaque->remaining = opaque->len; + } + + } else { + opaque->len = in_len; + opaque->remaining = in_len; + } + } + + return lwm2m_engine_get_opaque_more(in, value, buflen, + opaque, last_block); +} + +static int put_opaque(struct lwm2m_output_context *out, + struct lwm2m_obj_path *path, char *buf, + size_t buflen) +{ + int ret; + + ret = buf_append(CPKT_BUF_WRITE(out->out_cpkt), buf, buflen); + if (ret < 0) { + return ret; + } + + return buflen; +} + + +const struct lwm2m_writer opaque_writer = { + .put_opaque = put_opaque, +}; + +const struct lwm2m_reader opaque_reader = { + .get_opaque = get_opaque, +}; + +int do_read_op_opaque(struct lwm2m_message *msg, int content_format) +{ + /* Opaque can only return single resource (instance) */ + if (msg->path.level < LWM2M_PATH_LEVEL_RESOURCE) { + return -EPERM; + } else if (msg->path.level > LWM2M_PATH_LEVEL_RESOURCE) { + if (!IS_ENABLED(CONFIG_LWM2M_VERSION_1_1)) { + return -ENOENT; + } else if (msg->path.level > LWM2M_PATH_LEVEL_RESOURCE_INST) { + return -ENOENT; + } + } + + return lwm2m_perform_read_op(msg, content_format); +} + +int do_write_op_opaque(struct lwm2m_message *msg) +{ + struct lwm2m_engine_obj_inst *obj_inst = NULL; + struct lwm2m_engine_obj_field *obj_field; + struct lwm2m_engine_res *res = NULL; + struct lwm2m_engine_res_inst *res_inst = NULL; + int ret; + uint8_t created = 0U; + + ret = lwm2m_get_or_create_engine_obj(msg, &obj_inst, &created); + if (ret < 0) { + return ret; + } + + ret = lwm2m_engine_validate_write_access(msg, obj_inst, &obj_field); + if (ret < 0) { + return ret; + } + + ret = lwm2m_engine_get_create_res_inst(&msg->path, &res, &res_inst); + if (ret < 0) { + return -ENOENT; + } + + if (msg->path.level < LWM2M_PATH_LEVEL_RESOURCE) { + msg->path.level = LWM2M_PATH_LEVEL_RESOURCE; + } + + return lwm2m_write_handler(obj_inst, res, res_inst, obj_field, msg); +} diff --git a/subsys/net/lib/lwm2m/lwm2m_rw_opaque.h b/subsys/net/lib/lwm2m/lwm2m_rw_opaque.h new file mode 100644 index 00000000000..4118049efe8 --- /dev/null +++ b/subsys/net/lib/lwm2m/lwm2m_rw_opaque.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef LWM2M_RW_OPAQUE_H_ +#define LWM2M_RW_OPAQUE_H_ + +#include "lwm2m_object.h" + +extern const struct lwm2m_writer opaque_writer; +extern const struct lwm2m_reader opaque_reader; + +int do_read_op_opaque(struct lwm2m_message *msg, int content_format); +int do_write_op_opaque(struct lwm2m_message *msg); + +#endif /* LWM2M_RW_OPAQUE_H_ */ diff --git a/subsys/net/lib/lwm2m/lwm2m_rw_plain_text.c b/subsys/net/lib/lwm2m/lwm2m_rw_plain_text.c index 9650b33fc29..3ec69e8db82 100644 --- a/subsys/net/lib/lwm2m/lwm2m_rw_plain_text.c +++ b/subsys/net/lib/lwm2m/lwm2m_rw_plain_text.c @@ -341,49 +341,6 @@ static int get_bool(struct lwm2m_input_context *in, bool *value) return sizeof(uint8_t); } -static int get_opaque(struct lwm2m_input_context *in, uint8_t *value, - size_t buflen, struct lwm2m_opaque_context *opaque, - bool *last_block) -{ - uint16_t in_len; - - if (opaque->remaining == 0) { - coap_packet_get_payload(in->in_cpkt, &in_len); - - if (in_len == 0) { - return -ENODATA; - } - - if (in->block_ctx != NULL) { - uint32_t block_num = - in->block_ctx->ctx.current / - coap_block_size_to_bytes( - in->block_ctx->ctx.block_size); - - if (block_num == 0) { - opaque->len = in->block_ctx->ctx.total_size; - } - - if (opaque->len == 0) { - /* No size1 option provided, use current - * payload size. This will reset on next packet - * received. - */ - opaque->remaining = in_len; - } else { - opaque->remaining = opaque->len; - } - - } else { - opaque->len = in_len; - opaque->remaining = in_len; - } - } - - return lwm2m_engine_get_opaque_more(in, value, buflen, - opaque, last_block); -} - static int get_objlnk(struct lwm2m_input_context *in, struct lwm2m_objlnk *value) { @@ -432,7 +389,6 @@ const struct lwm2m_reader plain_text_reader = { .get_time = get_time, .get_float = get_float, .get_bool = get_bool, - .get_opaque = get_opaque, .get_objlnk = get_objlnk, }; diff --git a/subsys/net/lib/lwm2m/lwm2m_rw_senml_cbor.c b/subsys/net/lib/lwm2m/lwm2m_rw_senml_cbor.c index d62af88200b..15dc9a9451e 100644 --- a/subsys/net/lib/lwm2m/lwm2m_rw_senml_cbor.c +++ b/subsys/net/lib/lwm2m/lwm2m_rw_senml_cbor.c @@ -836,12 +836,15 @@ static uint8_t parse_composite_read_paths(struct lwm2m_message *msg, uint_fast8_t dret; int len; int ret; + char *payload; + uint16_t in_len; setup_in_fmt_data(msg); fd = engine_get_in_user_data(&msg->in); + payload = (char *)coap_packet_get_payload(msg->in.in_cpkt, &in_len); - dret = cbor_decode_lwm2m_senml(ICTX_BUF_R_REGION(&msg->in), &fd->dcd, &isize); + dret = cbor_decode_lwm2m_senml(payload, in_len, &fd->dcd, &isize); if (dret != ZCBOR_SUCCESS) { __ASSERT_NO_MSG(false); diff --git a/subsys/net/lib/lwm2m/lwm2m_shell.c b/subsys/net/lib/lwm2m/lwm2m_shell.c index f8374eb34f2..335004ec5d5 100644 --- a/subsys/net/lib/lwm2m/lwm2m_shell.c +++ b/subsys/net/lib/lwm2m/lwm2m_shell.c @@ -22,10 +22,9 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME); #include #define LWM2M_HELP_CMD "LwM2M commands" -#define LWM2M_HELP_SEND "LwM2M SEND operation\nsend [OPTION]... [PATH]...\n" \ - "Root-level operation is unsupported" -#define LWM2M_HELP_EXEC "Execute a resource\nexec PATH [PARAM]\n" -#define LWM2M_HELP_READ "Read value from LwM2M resource\nread PATH [OPTIONS]\n" \ +#define LWM2M_HELP_SEND "send PATHS\nLwM2M SEND operation\n" +#define LWM2M_HELP_EXEC "exec PATH [PARAM]\nExecute a resource\n" +#define LWM2M_HELP_READ "read PATH [OPTIONS]\nRead value from LwM2M resource\n" \ "-x \tRead value as hex stream (default)\n" \ "-s \tRead value as string\n" \ "-b \tRead value as bool (1/0)\n" \ @@ -33,25 +32,25 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME); "-sX\tRead value as intX_t\n" \ "-f \tRead value as float\n" \ "-t \tRead value as time_t\n" -#define LWM2M_HELP_WRITE "Write into LwM2M resource\nwrite PATH [OPTIONS] VALUE\n" \ +#define LWM2M_HELP_WRITE "write PATH [OPTIONS] VALUE\nWrite into LwM2M resource\n" \ "-s \tWrite value as string (default)\n" \ "-b \tWrite value as bool\n" \ "-uX\tWrite value as uintX_t\n" \ "-sX\tWrite value as intX_t\n" \ "-f \tWrite value as float\n" \ "-t \tWrite value as time_t\n" -#define LWM2M_HELP_START "Start the LwM2M RD (Registration / Discovery) Client\n" \ - "start EP_NAME [BOOTSTRAP FLAG]\n" \ +#define LWM2M_HELP_CREATE "create PATH\nCreate object instance\n" +#define LWM2M_HELP_START "start EP_NAME [BOOTSTRAP FLAG]\n" \ + "Start the LwM2M RD (Registration / Discovery) Client\n" \ "-b \tSet the bootstrap flag (default 0)\n" -#define LWM2M_HELP_STOP "Stop the LwM2M RD (De-register) Client\nstop [OPTIONS]\n" \ +#define LWM2M_HELP_STOP "stop [OPTIONS]\nStop the LwM2M RD (De-register) Client\n" \ "-f \tForce close the connection\n" #define LWM2M_HELP_UPDATE "Trigger Registration Update of the LwM2M RD Client\n" #define LWM2M_HELP_PAUSE "LwM2M engine thread pause" #define LWM2M_HELP_RESUME "LwM2M engine thread resume" #define LWM2M_HELP_LOCK "Lock the LwM2M registry" #define LWM2M_HELP_UNLOCK "Unlock the LwM2M registry" -#define LWM2M_HELP_CACHE "Enable data cache for resource\n" \ - "cache PATH NUM\n" \ +#define LWM2M_HELP_CACHE "cache PATH NUM\nEnable data cache for resource\n" \ "PATH is LwM2M path\n" \ "NUM how many elements to cache\n" \ @@ -372,6 +371,40 @@ static int cmd_write(const struct shell *sh, size_t argc, char **argv) return 0; } +static int cmd_create(const struct shell *sh, size_t argc, char **argv) +{ + struct lwm2m_obj_path path; + struct lwm2m_engine_obj_inst *obj_inst; + int ret; + + if (argc < 2) { + shell_error(sh, "No object ID given\n"); + shell_help(sh); + return -EINVAL; + } + + ret = lwm2m_string_to_path(argv[1], &path, '/'); + if (ret < 0) { + shell_error(sh, "failed to read path (%d)\n", ret); + return -ENOEXEC; + } + + if (path.level != LWM2M_PATH_LEVEL_OBJECT_INST) { + shell_error(sh, "path is not an object instance\n"); + shell_help(sh); + return -EINVAL; + } + + ret = lwm2m_create_obj_inst(path.obj_id, path.obj_inst_id, &obj_inst); + if (ret < 0) { + shell_error(sh, "Failed to create object instance %d/%d, ret=%d", path.obj_id, + path.obj_inst_id, ret); + return -ENOEXEC; + } + + return 0; +} + static int cmd_start(const struct shell *sh, size_t argc, char **argv) { struct lwm2m_ctx *ctx = lwm2m_rd_client_ctx(); @@ -561,6 +594,8 @@ SHELL_STATIC_SUBCMD_SET_CREATE( SHELL_CMD_ARG(exec, NULL, LWM2M_HELP_EXEC, cmd_exec, 2, 1), SHELL_CMD_ARG(read, NULL, LWM2M_HELP_READ, cmd_read, 2, 1), SHELL_CMD_ARG(write, NULL, LWM2M_HELP_WRITE, cmd_write, 3, 1), + SHELL_CMD_ARG(create, NULL, LWM2M_HELP_CREATE, cmd_create, 2, 0), + SHELL_CMD_ARG(cache, NULL, LWM2M_HELP_CACHE, cmd_cache, 3, 0), SHELL_CMD_ARG(start, NULL, LWM2M_HELP_START, cmd_start, 2, 2), SHELL_CMD_ARG(stop, NULL, LWM2M_HELP_STOP, cmd_stop, 1, 1), SHELL_CMD_ARG(update, NULL, LWM2M_HELP_UPDATE, cmd_update, 1, 0), @@ -568,7 +603,6 @@ SHELL_STATIC_SUBCMD_SET_CREATE( SHELL_CMD_ARG(resume, NULL, LWM2M_HELP_RESUME, cmd_resume, 1, 0), SHELL_CMD_ARG(lock, NULL, LWM2M_HELP_LOCK, cmd_lock, 1, 0), SHELL_CMD_ARG(unlock, NULL, LWM2M_HELP_UNLOCK, cmd_unlock, 1, 0), - SHELL_CMD_ARG(cache, NULL, LWM2M_HELP_CACHE, cmd_cache, 3, 0), SHELL_SUBCMD_SET_END); SHELL_COND_CMD_ARG_REGISTER(CONFIG_LWM2M_SHELL, lwm2m, &sub_lwm2m, diff --git a/subsys/net/lib/mqtt/mqtt_transport_socket_tls.c b/subsys/net/lib/mqtt/mqtt_transport_socket_tls.c index c835656b6cf..8e9cc87239a 100644 --- a/subsys/net/lib/mqtt/mqtt_transport_socket_tls.c +++ b/subsys/net/lib/mqtt/mqtt_transport_socket_tls.c @@ -22,10 +22,15 @@ int mqtt_client_tls_connect(struct mqtt_client *client) { const struct sockaddr *broker = client->broker; struct mqtt_sec_config *tls_config = &client->transport.tls.config; + int type = SOCK_STREAM; int ret; + if (tls_config->set_native_tls) { + type |= SOCK_NATIVE_TLS; + } + client->transport.tls.sock = zsock_socket(broker->sa_family, - SOCK_STREAM, IPPROTO_TLS_1_2); + type, IPPROTO_TLS_1_2); if (client->transport.tls.sock < 0) { return -errno; } @@ -78,6 +83,16 @@ int mqtt_client_tls_connect(struct mqtt_client *client) } } + if (tls_config->session_cache == TLS_SESSION_CACHE_ENABLED) { + ret = zsock_setsockopt(client->transport.tls.sock, SOL_TLS, + TLS_SESSION_CACHE, + &tls_config->session_cache, + sizeof(tls_config->session_cache)); + if (ret < 0) { + goto error; + } + } + if (tls_config->cert_nocopy != TLS_CERT_NOCOPY_NONE) { ret = zsock_setsockopt(client->transport.tls.sock, SOL_TLS, TLS_CERT_NOCOPY, &tls_config->cert_nocopy, diff --git a/subsys/net/lib/sockets/socketpair.c b/subsys/net/lib/sockets/socketpair.c index f3bbb46f6c8..9207f4b659e 100644 --- a/subsys/net/lib/sockets/socketpair.c +++ b/subsys/net/lib/sockets/socketpair.c @@ -191,6 +191,10 @@ static void spair_delete(struct spair *spair) res = k_poll_signal_raise(&spair->writeable, SPAIR_SIG_CANCEL); __ASSERT(res == 0, "k_poll_signal_raise() failed: %d", res); + if (remote != NULL && have_remote_sem) { + k_sem_give(&remote->sem); + } + /* ensure no private information is released to the memory pool */ memset(spair, 0, sizeof(*spair)); #ifdef CONFIG_NET_SOCKETPAIR_STATIC @@ -200,10 +204,6 @@ static void spair_delete(struct spair *spair) #else k_free(spair); #endif - - if (remote != NULL && have_remote_sem) { - k_sem_give(&remote->sem); - } } /** diff --git a/subsys/net/lib/sockets/sockets_tls.c b/subsys/net/lib/sockets/sockets_tls.c index 20fdc3617af..f77bf51d668 100644 --- a/subsys/net/lib/sockets/sockets_tls.c +++ b/subsys/net/lib/sockets/sockets_tls.c @@ -2230,10 +2230,9 @@ static ssize_t send_tls(struct tls_context *ctx, const void *buf, timeout_ms = timeout_to_ms(&timeout); ret = wait_for_reason(ctx->sock, timeout_ms, ret); if (ret != 0) { - /* Retry. */ + errno = -ret; break; } - } else { (void)tls_mbedtls_reset(ctx); errno = EIO; diff --git a/subsys/net/lib/zperf/zperf_common.c b/subsys/net/lib/zperf/zperf_common.c index aa29bc99043..29568eeef5b 100644 --- a/subsys/net/lib/zperf/zperf_common.c +++ b/subsys/net/lib/zperf/zperf_common.c @@ -109,29 +109,8 @@ int zperf_get_ipv4_addr(char *host, struct in_addr *addr) return 0; } - -const struct in_addr *zperf_get_default_if_in4_addr(void) -{ -#if CONFIG_NET_IPV4 - return net_if_ipv4_select_src_addr(NULL, - net_ipv4_unspecified_address()); -#else - return NULL; -#endif -} - -const struct in6_addr *zperf_get_default_if_in6_addr(void) -{ -#if CONFIG_NET_IPV6 - return net_if_ipv6_select_src_addr(NULL, - net_ipv6_unspecified_address()); -#else - return NULL; -#endif -} - int zperf_prepare_upload_sock(const struct sockaddr *peer_addr, int tos, - int proto) + int priority, int proto) { socklen_t addrlen = peer_addr->sa_family == AF_INET6 ? sizeof(struct sockaddr_in6) : @@ -192,6 +171,24 @@ int zperf_prepare_upload_sock(const struct sockaddr *peer_addr, int tos, return -EINVAL; } + if (IS_ENABLED(CONFIG_NET_CONTEXT_PRIORITY) && priority >= 0) { + uint8_t prio = priority; + + if (!IS_ENABLED(CONFIG_NET_ALLOW_ANY_PRIORITY) && + (prio > NET_MAX_PRIORITIES)) { + NET_ERR("Priority %d is too large, maximum is %d", + priority, NET_MAX_PRIORITIES); + return -EINVAL; + } + + if (zsock_setsockopt(sock, SOL_SOCKET, SO_PRIORITY, + &prio, + sizeof(prio)) != 0) { + NET_WARN("Failed to set SOL_SOCKET - SO_PRIORITY socket option."); + return -EINVAL; + } + } + ret = zsock_connect(sock, peer_addr, addrlen); if (ret < 0) { NET_ERR("Connect failed (%d)", errno); diff --git a/subsys/net/lib/zperf/zperf_internal.h b/subsys/net/lib/zperf/zperf_internal.h index 5bef0331f6f..592424a9446 100644 --- a/subsys/net/lib/zperf/zperf_internal.h +++ b/subsys/net/lib/zperf/zperf_internal.h @@ -95,11 +95,8 @@ struct sockaddr_in *zperf_get_sin(void); extern void connect_ap(char *ssid); -const struct in_addr *zperf_get_default_if_in4_addr(void); -const struct in6_addr *zperf_get_default_if_in6_addr(void); - int zperf_prepare_upload_sock(const struct sockaddr *peer_addr, int tos, - int proto); + int priority, int proto); uint32_t zperf_packet_duration(uint32_t packet_size, uint32_t rate_in_kbps); diff --git a/subsys/net/lib/zperf/zperf_shell.c b/subsys/net/lib/zperf/zperf_shell.c index a30caf08074..b61501d0e9e 100644 --- a/subsys/net/lib/zperf/zperf_shell.c +++ b/subsys/net/lib/zperf/zperf_shell.c @@ -178,6 +178,39 @@ static int parse_ipv4_addr(const struct shell *sh, char *host, char *port, return 0; } +static int zperf_bind_host(const struct shell *sh, + size_t argc, char *argv[], + struct zperf_download_params *param) +{ + int ret; + + /* Parse options */ + if (argc >= 2) { + param->port = strtoul(argv[1], NULL, 10); + } else { + param->port = DEF_PORT; + } + + if (argc >= 3) { + char *addr_str = argv[2]; + struct sockaddr addr; + + memset(&addr, 0, sizeof(addr)); + + ret = net_ipaddr_parse(addr_str, strlen(addr_str), &addr); + if (ret < 0) { + shell_fprintf(sh, SHELL_WARNING, + "Cannot parse address \"%s\"\n", + addr_str); + return ret; + } + + memcpy(¶m->addr, &addr, sizeof(struct sockaddr)); + } + + return 0; +} + static int cmd_setip(const struct shell *sh, size_t argc, char *argv[]) { int start = 0; @@ -191,7 +224,7 @@ static int cmd_setip(const struct shell *sh, size_t argc, char *argv[]) if (zperf_get_ipv6_addr(argv[start + 1], argv[start + 2], &shell_ipv6) < 0) { shell_fprintf(sh, SHELL_WARNING, - "Unable to set IP\n"); + "Unable to set %s address (%s disabled)\n", "IPv6", "IPv4"); return 0; } @@ -208,7 +241,7 @@ static int cmd_setip(const struct shell *sh, size_t argc, char *argv[]) if (zperf_get_ipv4_addr(argv[start + 1], &shell_ipv4) < 0) { shell_fprintf(sh, SHELL_WARNING, - "Unable to set IP\n"); + "Unable to set %s address (%s disabled)\n", "IPv4", "IPv6"); return -ENOEXEC; } @@ -227,7 +260,7 @@ static int cmd_setip(const struct shell *sh, size_t argc, char *argv[]) if (zperf_get_ipv4_addr(argv[start + 1], &shell_ipv4) < 0) { shell_fprintf(sh, SHELL_WARNING, - "Unable to set IP\n"); + "Unable to set %s address\n", "IPv4"); return -ENOEXEC; } @@ -243,7 +276,7 @@ static int cmd_setip(const struct shell *sh, size_t argc, char *argv[]) if (zperf_get_ipv6_addr(argv[start + 1], argv[start + 2], &shell_ipv6) < 0) { shell_fprintf(sh, SHELL_WARNING, - "Unable to set IP\n"); + "Unable to set %s address\n", "IPv6"); return -ENOEXEC; } @@ -333,10 +366,12 @@ static int cmd_udp_download(const struct shell *sh, size_t argc, struct zperf_download_params param = { 0 }; int ret; - if (argc >= 2) { - param.port = strtoul(argv[1], NULL, 10); - } else { - param.port = DEF_PORT; + ret = zperf_bind_host(sh, argc, argv, ¶m); + if (ret < 0) { + shell_fprintf(sh, SHELL_WARNING, + "Unable to bind host.\n"); + shell_help(sh); + return -ENOEXEC; } ret = zperf_udp_download(¶m, udp_session_cb, (void *)sh); @@ -526,7 +561,7 @@ static int execute_upload(const struct shell *sh, * some time and start the test after that. */ net_icmpv6_send_echo_request(net_if_get_default(), - &ipv6->sin6_addr, 0, 0, 0, NULL, 0); + &ipv6->sin6_addr, 0, 0, 0, -1, NULL, 0); k_sleep(K_SECONDS(1)); } @@ -642,6 +677,7 @@ static int shell_cmd_upload(const struct shell *sh, size_t argc, int start = 0; size_t opt_cnt = 0; + param.options.priority = -1; is_udp = proto == IPPROTO_UDP; /* Parse options */ @@ -680,6 +716,19 @@ static int shell_cmd_upload(const struct shell *sh, size_t argc, opt_cnt += 1; break; +#ifdef CONFIG_NET_CONTEXT_PRIORITY + case 'p': + param.options.priority = parse_arg(&i, argc, argv); + if (param.options.priority < 0 || + param.options.priority > UINT8_MAX) { + shell_fprintf(sh, SHELL_WARNING, + "Parse error: %s\n", argv[i]); + return -ENOEXEC; + } + opt_cnt += 2; + break; +#endif /* CONFIG_NET_CONTEXT_PRIORITY */ + default: shell_fprintf(sh, SHELL_WARNING, "Unrecognized argument: %s\n", argv[i]); @@ -853,6 +902,19 @@ static int shell_cmd_upload2(const struct shell *sh, size_t argc, opt_cnt += 1; break; +#ifdef CONFIG_NET_CONTEXT_PRIORITY + case 'p': + param.options.priority = parse_arg(&i, argc, argv); + if (param.options.priority == -1 || + param.options.priority > UINT8_MAX) { + shell_fprintf(sh, SHELL_WARNING, + "Parse error: %s\n", argv[i]); + return -ENOEXEC; + } + opt_cnt += 2; + break; +#endif /* CONFIG_NET_CONTEXT_PRIORITY */ + default: shell_fprintf(sh, SHELL_WARNING, "Unrecognized argument: %s\n", argv[i]); @@ -1045,10 +1107,12 @@ static int cmd_tcp_download(const struct shell *sh, size_t argc, struct zperf_download_params param = { 0 }; int ret; - if (argc >= 2) { - param.port = strtoul(argv[1], NULL, 10); - } else { - param.port = DEF_PORT; + ret = zperf_bind_host(sh, argc, argv, ¶m); + if (ret < 0) { + shell_fprintf(sh, SHELL_WARNING, + "Unable to bind host.\n"); + shell_help(sh); + return -ENOEXEC; } ret = zperf_tcp_download(¶m, tcp_session_cb, (void *)sh); @@ -1083,11 +1147,11 @@ void zperf_shell_init(void) { int ret; - if (IS_ENABLED(CONFIG_NET_IPV6) && MY_IP6ADDR) { + if (IS_ENABLED(MY_IP6ADDR_SET) && MY_IP6ADDR) { ret = net_addr_pton(AF_INET6, MY_IP6ADDR, &in6_addr_my.sin6_addr); if (ret < 0) { - NET_WARN("Unable to set IP"); + NET_WARN("Unable to set %s address\n", "IPv6"); } else { NET_INFO("Setting IP address %s", net_sprint_ipv6_addr(&in6_addr_my.sin6_addr)); @@ -1096,20 +1160,21 @@ void zperf_shell_init(void) ret = net_addr_pton(AF_INET6, DST_IP6ADDR, &in6_addr_dst.sin6_addr); if (ret < 0) { - NET_WARN("Unable to set IP %s", + NET_WARN("Unable to set destination %s address %s", + "IPv6", DST_IP6ADDR ? DST_IP6ADDR - : "(Default IPv6 destination address not set)"); + : "(not set)"); } else { NET_INFO("Setting destination IP address %s", net_sprint_ipv6_addr(&in6_addr_dst.sin6_addr)); } } - if (IS_ENABLED(CONFIG_NET_IPV4) && MY_IP4ADDR) { + if (IS_ENABLED(MY_IP4ADDR_SET) && MY_IP4ADDR) { ret = net_addr_pton(AF_INET, MY_IP4ADDR, &in4_addr_my.sin_addr); if (ret < 0) { - NET_WARN("Unable to set IP"); + NET_WARN("Unable to set %s address\n", "IPv4"); } else { NET_INFO("Setting IP address %s", net_sprint_ipv4_addr(&in4_addr_my.sin_addr)); @@ -1118,9 +1183,10 @@ void zperf_shell_init(void) ret = net_addr_pton(AF_INET, DST_IP4ADDR, &in4_addr_dst.sin_addr); if (ret < 0) { - NET_WARN("Unable to set IP %s", + NET_WARN("Unable to set destination %s address %s", + "IPv4", DST_IP4ADDR ? DST_IP4ADDR - : "(Default IPv4 destination address not set)"); + : "(not set)"); } else { NET_INFO("Setting destination IP address %s", net_sprint_ipv4_addr(&in4_addr_dst.sin_addr)); @@ -1146,6 +1212,9 @@ SHELL_STATIC_SUBCMD_SET_CREATE(zperf_cmd_tcp, "-S tos: Specify IPv4/6 type of service\n" "-a: Asynchronous call (shell will not block for the upload)\n" "-n: Disable Nagle's algorithm\n" +#ifdef CONFIG_NET_CONTEXT_PRIORITY + "-p: Specify custom packet priority\n" +#endif /* CONFIG_NET_CONTEXT_PRIORITY */ "Example: tcp upload 192.0.2.2 1111 1 1K\n" "Example: tcp upload 2001:db8::2\n", cmd_tcp_upload), @@ -1159,6 +1228,9 @@ SHELL_STATIC_SUBCMD_SET_CREATE(zperf_cmd_tcp, "Available options:\n" "-S tos: Specify IPv4/6 type of service\n" "-a: Asynchronous call (shell will not block for the upload)\n" +#ifdef CONFIG_NET_CONTEXT_PRIORITY + "-p: Specify custom packet priority\n" +#endif /* CONFIG_NET_CONTEXT_PRIORITY */ "Example: tcp upload2 v6 1 1K\n" "Example: tcp upload2 v4\n" "-n: Disable Nagle's algorithm\n" @@ -1173,8 +1245,9 @@ SHELL_STATIC_SUBCMD_SET_CREATE(zperf_cmd_tcp, , cmd_tcp_upload2), SHELL_CMD(download, &zperf_cmd_tcp_download, - "[]\n" - "Example: tcp download 5001\n", + "[]: Server port to listen on/connect to\n" + "[]: Bind to , an interface address\n" + "Example: tcp download 5001 192.168.0.1\n", cmd_tcp_download), SHELL_SUBCMD_SET_END ); @@ -1198,6 +1271,9 @@ SHELL_STATIC_SUBCMD_SET_CREATE(zperf_cmd_udp, "Available options:\n" "-S tos: Specify IPv4/6 type of service\n" "-a: Asynchronous call (shell will not block for the upload)\n" +#ifdef CONFIG_NET_CONTEXT_PRIORITY + "-p: Specify custom packet priority\n" +#endif /* CONFIG_NET_CONTEXT_PRIORITY */ "Example: udp upload 192.0.2.2 1111 1 1K 1M\n" "Example: udp upload 2001:db8::2\n", cmd_udp_upload), @@ -1212,6 +1288,9 @@ SHELL_STATIC_SUBCMD_SET_CREATE(zperf_cmd_udp, "Available options:\n" "-S tos: Specify IPv4/6 type of service\n" "-a: Asynchronous call (shell will not block for the upload)\n" +#ifdef CONFIG_NET_CONTEXT_PRIORITY + "-p: Specify custom packet priority\n" +#endif /* CONFIG_NET_CONTEXT_PRIORITY */ "Example: udp upload2 v4 1 1K 1M\n" "Example: udp upload2 v6\n" #if defined(CONFIG_NET_IPV6) && defined(MY_IP6ADDR_SET) @@ -1225,8 +1304,9 @@ SHELL_STATIC_SUBCMD_SET_CREATE(zperf_cmd_udp, , cmd_udp_upload2), SHELL_CMD(download, &zperf_cmd_udp_download, - "[]\n" - "Example: udp download 5001\n", + "[]: Server port to listen on/connect to\n" + "[]: Bind to , an interface address\n" + "Example: udp download 5001 192.168.0.1\n", cmd_udp_download), SHELL_SUBCMD_SET_END ); diff --git a/subsys/net/lib/zperf/zperf_tcp_receiver.c b/subsys/net/lib/zperf/zperf_tcp_receiver.c index 25f9adc3494..6cc3374d0ae 100644 --- a/subsys/net/lib/zperf/zperf_tcp_receiver.c +++ b/subsys/net/lib/zperf/zperf_tcp_receiver.c @@ -46,6 +46,7 @@ static void *tcp_user_data; static bool tcp_server_running; static bool tcp_server_stop; static uint16_t tcp_server_port; +static struct sockaddr tcp_server_addr; static K_SEM_DEFINE(tcp_server_run, 0, 1); static void tcp_received(const struct sockaddr *addr, size_t datalen) @@ -150,6 +151,7 @@ static void tcp_server_session(void) if (IS_ENABLED(CONFIG_NET_IPV4)) { struct sockaddr_in *in4_addr = zperf_get_sin(); + const struct in_addr *addr = NULL; fds[SOCK_ID_IPV4_LISTEN].fd = zsock_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); @@ -158,25 +160,22 @@ static void tcp_server_session(void) goto error; } - if (MY_IP4ADDR && strlen(MY_IP4ADDR)) { + addr = &net_sin(&tcp_server_addr)->sin_addr; + + if (!net_ipv4_is_addr_unspecified(addr)) { + memcpy(&in4_addr->sin_addr, addr, + sizeof(struct in_addr)); + } else if (MY_IP4ADDR && strlen(MY_IP4ADDR)) { /* Use Setting IP */ ret = zperf_get_ipv4_addr(MY_IP4ADDR, &in4_addr->sin_addr); if (ret < 0) { NET_WARN("Unable to set IPv4"); - goto use_existing_ipv4; + goto use_any_ipv4; } } else { - /* Use existing IP */ - const struct in_addr *addr; -use_existing_ipv4: - addr = zperf_get_default_if_in4_addr(); - if (!addr) { - NET_ERR("Unable to get IPv4 by default"); - goto error; - } - memcpy(&in4_addr->sin_addr, addr, - sizeof(struct in_addr)); +use_any_ipv4: + in4_addr->sin_addr.s_addr = INADDR_ANY; } in4_addr->sin_port = htons(tcp_server_port); @@ -197,6 +196,7 @@ static void tcp_server_session(void) if (IS_ENABLED(CONFIG_NET_IPV6)) { struct sockaddr_in6 *in6_addr = zperf_get_sin6(); + const struct in6_addr *addr = NULL; fds[SOCK_ID_IPV6_LISTEN].fd = zsock_socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); @@ -205,26 +205,24 @@ static void tcp_server_session(void) goto error; } - if (MY_IP6ADDR && strlen(MY_IP6ADDR)) { + addr = &net_sin6(&tcp_server_addr)->sin6_addr; + + if (!net_ipv6_is_addr_unspecified(addr)) { + memcpy(&in6_addr->sin6_addr, addr, + sizeof(struct in6_addr)); + } else if (MY_IP6ADDR && strlen(MY_IP6ADDR)) { /* Use Setting IP */ ret = zperf_get_ipv6_addr(MY_IP6ADDR, MY_PREFIX_LEN_STR, &in6_addr->sin6_addr); if (ret < 0) { NET_WARN("Unable to set IPv6"); - goto use_existing_ipv6; + goto use_any_ipv6; } } else { - /* Use existing IP */ - const struct in6_addr *addr; -use_existing_ipv6: - addr = zperf_get_default_if_in6_addr(); - if (!addr) { - NET_ERR("Unable to get IPv6 by default"); - goto error; - } - memcpy(&in6_addr->sin6_addr, addr, - sizeof(struct in6_addr)); +use_any_ipv6: + memcpy(&in6_addr->sin6_addr, net_ipv6_unspecified_address(), + sizeof(struct in6_addr)); } in6_addr->sin6_port = htons(tcp_server_port); @@ -387,6 +385,7 @@ int zperf_tcp_download(const struct zperf_download_params *param, tcp_server_port = param->port; tcp_server_running = true; tcp_server_stop = false; + memcpy(&tcp_server_addr, ¶m->addr, sizeof(struct sockaddr)); k_sem_give(&tcp_server_run); diff --git a/subsys/net/lib/zperf/zperf_tcp_uploader.c b/subsys/net/lib/zperf/zperf_tcp_uploader.c index 97fd4f57f71..5fcc0530c5c 100644 --- a/subsys/net/lib/zperf/zperf_tcp_uploader.c +++ b/subsys/net/lib/zperf/zperf_tcp_uploader.c @@ -20,6 +20,22 @@ static char sample_packet[PACKET_SIZE_MAX]; static struct zperf_async_upload_context tcp_async_upload_ctx; +static ssize_t sendall(int sock, const void *buf, size_t len) +{ + while (len) { + ssize_t out_len = zsock_send(sock, buf, len, 0); + + if (out_len < 0) { + return out_len; + } + + buf = (const char *)buf + out_len; + len -= out_len; + } + + return 0; +} + static int tcp_upload(int sock, unsigned int duration_in_ms, unsigned int packet_size, @@ -50,7 +66,7 @@ static int tcp_upload(int sock, do { /* Send the packet */ - ret = zsock_send(sock, sample_packet, packet_size, 0); + ret = sendall(sock, sample_packet, packet_size); if (ret < 0) { if (nb_errors == 0 && ret != -ENOMEM) { NET_ERR("Failed to send the packet (%d)", errno); @@ -117,7 +133,7 @@ int zperf_tcp_upload(const struct zperf_upload_params *param, } sock = zperf_prepare_upload_sock(¶m->peer_addr, param->options.tos, - IPPROTO_TCP); + param->options.priority, IPPROTO_TCP); if (sock < 0) { return sock; } diff --git a/subsys/net/lib/zperf/zperf_udp_receiver.c b/subsys/net/lib/zperf/zperf_udp_receiver.c index f96acde6f49..2724da4b1a9 100644 --- a/subsys/net/lib/zperf/zperf_udp_receiver.c +++ b/subsys/net/lib/zperf/zperf_udp_receiver.c @@ -48,6 +48,7 @@ static void *udp_user_data; static bool udp_server_running; static bool udp_server_stop; static uint16_t udp_server_port; +static struct sockaddr udp_server_addr; static K_SEM_DEFINE(udp_server_run, 0, 1); static inline void build_reply(struct zperf_udp_datagram *hdr, @@ -251,24 +252,22 @@ static void udp_server_session(void) goto error; } - if (MY_IP4ADDR && strlen(MY_IP4ADDR)) { + in4_addr = &net_sin(&udp_server_addr)->sin_addr; + + if (!net_ipv4_is_addr_unspecified(in4_addr)) { + memcpy(&in4_addr_my->sin_addr, in4_addr, + sizeof(struct in_addr)); + } else if (MY_IP4ADDR && strlen(MY_IP4ADDR)) { /* Use setting IP */ ret = zperf_get_ipv4_addr(MY_IP4ADDR, &in4_addr_my->sin_addr); if (ret < 0) { NET_WARN("Unable to set IPv4"); - goto use_existing_ipv4; + goto use_any_ipv4; } } else { - use_existing_ipv4: - /* Use existing IP */ - in4_addr = zperf_get_default_if_in4_addr(); - if (!in4_addr) { - NET_ERR("Unable to get IPv4 by default"); - goto error; - } - memcpy(&in4_addr_my->sin_addr, in4_addr, - sizeof(struct in_addr)); +use_any_ipv4: + in4_addr_my->sin_addr.s_addr = INADDR_ANY; } NET_INFO("Binding to %s", @@ -301,25 +300,25 @@ static void udp_server_session(void) goto error; } - if (MY_IP6ADDR && strlen(MY_IP6ADDR)) { + in6_addr = &net_sin6(&udp_server_addr)->sin6_addr; + + if (!net_ipv6_is_addr_unspecified(in6_addr)) { + memcpy(&in6_addr_my->sin6_addr, in6_addr, + sizeof(struct in6_addr)); + } else if (MY_IP6ADDR && strlen(MY_IP6ADDR)) { /* Use setting IP */ ret = zperf_get_ipv6_addr(MY_IP6ADDR, MY_PREFIX_LEN_STR, &in6_addr_my->sin6_addr); if (ret < 0) { NET_WARN("Unable to set IPv6"); - goto use_existing_ipv6; + goto use_any_ipv6; } } else { - use_existing_ipv6: - /* Use existing IP */ - in6_addr = zperf_get_default_if_in6_addr(); - if (!in6_addr) { - NET_ERR("Unable to get IPv4 by default"); - goto error; - } - memcpy(&in6_addr_my->sin6_addr, in6_addr, - sizeof(struct in6_addr)); +use_any_ipv6: + memcpy(&in6_addr_my->sin6_addr, + net_ipv6_unspecified_address(), + sizeof(struct in6_addr)); } NET_INFO("Binding to %s", @@ -441,6 +440,7 @@ int zperf_udp_download(const struct zperf_download_params *param, udp_server_port = param->port; udp_server_running = true; udp_server_stop = false; + memcpy(&udp_server_addr, ¶m->addr, sizeof(struct sockaddr)); k_sem_give(&udp_server_run); diff --git a/subsys/net/lib/zperf/zperf_udp_uploader.c b/subsys/net/lib/zperf/zperf_udp_uploader.c index 1310063437b..e7e2efb9a08 100644 --- a/subsys/net/lib/zperf/zperf_udp_uploader.c +++ b/subsys/net/lib/zperf/zperf_udp_uploader.c @@ -284,7 +284,7 @@ int zperf_udp_upload(const struct zperf_upload_params *param, } sock = zperf_prepare_upload_sock(¶m->peer_addr, param->options.tos, - IPPROTO_UDP); + param->options.priority, IPPROTO_UDP); if (sock < 0) { return sock; } diff --git a/subsys/zbus/Kconfig b/subsys/zbus/Kconfig index 622f89641a2..f250865b466 100644 --- a/subsys/zbus/Kconfig +++ b/subsys/zbus/Kconfig @@ -18,6 +18,36 @@ config ZBUS_CHANNEL_NAME config ZBUS_OBSERVER_NAME bool "Observer name field" +config ZBUS_MSG_SUBSCRIBER + select NET_BUF + bool "Message subscribers will receive all messages in sequence." + +if ZBUS_MSG_SUBSCRIBER + +choice + prompt "ZBus msg_subscribers buffer allocation" + +config ZBUS_MSG_SUBSCRIBER_NET_BUF_DYNAMIC + bool "Use heap to allocate msg_subscriber buffers data" + +config ZBUS_MSG_SUBSCRIBER_NET_BUF_STATIC + bool "Use fixed data size for msg_subscriber buffers pool" + +endchoice + +config ZBUS_MSG_SUBSCRIBER_NET_BUF_POOL_SIZE + default 16 + int "The count of net_buf available to be used simutaneously." + +if ZBUS_MSG_SUBSCRIBER_NET_BUF_STATIC + +config ZBUS_MSG_SUBSCRIBER_NET_BUF_STATIC_DATA_SIZE + int "The size of the biggest message used with ZBus." + +endif # ZBUS_MSG_SUBSCRIBER_NET_BUF_STATIC + +endif # ZBUS_MSG_SUBSCRIBER + config ZBUS_RUNTIME_OBSERVERS bool "Runtime observers support." default n diff --git a/subsys/zbus/zbus.c b/subsys/zbus/zbus.c index abaf92c8641..4ea2d986a3f 100644 --- a/subsys/zbus/zbus.c +++ b/subsys/zbus/zbus.c @@ -8,11 +8,47 @@ #include #include #include +#include #include LOG_MODULE_REGISTER(zbus, CONFIG_ZBUS_LOG_LEVEL); +#if defined(CONFIG_ZBUS_MSG_SUBSCRIBER) + +#if defined(CONFIG_ZBUS_MSG_SUBSCRIBER_NET_BUF_DYNAMIC) + +NET_BUF_POOL_HEAP_DEFINE(_zbus_msg_subscribers_pool, CONFIG_ZBUS_MSG_SUBSCRIBER_NET_BUF_POOL_SIZE, + sizeof(struct zbus_channel *), NULL); +BUILD_ASSERT(CONFIG_HEAP_MEM_POOL_SIZE > 0, "MSG_SUBSCRIBER feature requires heap memory pool."); + +static inline struct net_buf *_zbus_create_net_buf(struct net_buf_pool *pool, size_t size, + k_timeout_t timeout) +{ + return net_buf_alloc_len(&_zbus_msg_subscribers_pool, size, timeout); +} + +#else + +NET_BUF_POOL_FIXED_DEFINE(_zbus_msg_subscribers_pool, + (CONFIG_ZBUS_MSG_SUBSCRIBER_NET_BUF_STATIC_DATA_SIZE), + (CONFIG_ZBUS_MSG_SUBSCRIBER_NET_BUF_POOL_SIZE), + sizeof(struct zbus_channel *), NULL); + +static inline struct net_buf *_zbus_create_net_buf(struct net_buf_pool *pool, size_t size, + k_timeout_t timeout) +{ + __ASSERT(size <= CONFIG_ZBUS_MSG_SUBSCRIBER_NET_BUF_STATIC_DATA_SIZE, + "CONFIG_ZBUS_MSG_SUBSCRIBER_NET_BUF_STATIC_DATA_SIZE must be greater or equal to " + "%d", + (int)size); + return net_buf_alloc(&_zbus_msg_subscribers_pool, timeout); +} +#endif /* CONFIG_ZBUS_MSG_SUBSCRIBER_NET_BUF_DYNAMIC */ + +#endif /* CONFIG_ZBUS_MSG_SUBSCRIBER */ + int _zbus_init(void) { + const struct zbus_channel *curr = NULL; const struct zbus_channel *prev = NULL; @@ -44,32 +80,62 @@ int _zbus_init(void) SYS_INIT(_zbus_init, APPLICATION, CONFIG_ZBUS_CHANNELS_SYS_INIT_PRIORITY); static inline int _zbus_notify_observer(const struct zbus_channel *chan, - const struct zbus_observer *obs, k_timepoint_t end_time) + const struct zbus_observer *obs, k_timepoint_t end_time, + struct net_buf *buf) { - int err = 0; - - if (obs->type == ZBUS_OBSERVER_LISTENER_TYPE) { + switch (obs->type) { + case ZBUS_OBSERVER_LISTENER_TYPE: { obs->callback(chan); + break; + } + case ZBUS_OBSERVER_SUBSCRIBER_TYPE: { + return k_msgq_put(obs->queue, &chan, sys_timepoint_timeout(end_time)); + } +#if defined(CONFIG_ZBUS_MSG_SUBSCRIBER) + case ZBUS_OBSERVER_MSG_SUBSCRIBER_TYPE: { + struct net_buf *cloned_buf = net_buf_clone(buf, sys_timepoint_timeout(end_time)); - } else if (obs->type == ZBUS_OBSERVER_SUBSCRIBER_TYPE) { - err = k_msgq_put(obs->queue, &chan, sys_timepoint_timeout(end_time)); - } else { - CODE_UNREACHABLE; + if (cloned_buf == NULL) { + return -ENOMEM; + } + memcpy(net_buf_user_data(cloned_buf), &chan, sizeof(struct zbus_channel *)); + + net_buf_put(obs->message_fifo, cloned_buf); + + break; } - return err; +#endif /* CONFIG_ZBUS_MSG_SUBSCRIBER */ + + default: + _ZBUS_ASSERT(false, "Unreachable"); + } + return 0; } static inline int _zbus_vded_exec(const struct zbus_channel *chan, k_timepoint_t end_time) { int err = 0; int last_error = 0; - - _ZBUS_ASSERT(chan != NULL, "chan is required"); + struct net_buf *buf = NULL; /* Static observer event dispatcher logic */ struct zbus_channel_observation *observation; struct zbus_channel_observation_mask *observation_mask; +#if defined(CONFIG_ZBUS_MSG_SUBSCRIBER) + buf = _zbus_create_net_buf(&_zbus_msg_subscribers_pool, zbus_chan_msg_size(chan), + sys_timepoint_timeout(end_time)); + + _ZBUS_ASSERT(buf != NULL, "net_buf zbus_msg_subscribers_pool is " + "unavailable or heap is full"); + + net_buf_add_mem(buf, zbus_chan_msg(chan), zbus_chan_msg_size(chan)); +#endif /* CONFIG_ZBUS_MSG_SUBSCRIBER */ + + LOG_DBG("Notifing %s's observers. Starting VDED:", _ZBUS_CHAN_NAME(chan)); + + int __maybe_unused index = 0; + for (int16_t i = chan->data->observers_start_idx, limit = chan->data->observers_end_idx; i < limit; ++i) { STRUCT_SECTION_GET(zbus_channel_observation, i, &observation); @@ -83,15 +149,21 @@ static inline int _zbus_vded_exec(const struct zbus_channel *chan, k_timepoint_t continue; } - err = _zbus_notify_observer(chan, obs, end_time); - - _ZBUS_ASSERT(err == 0, - "could not deliver notification to observer %s. Error code %d", - _ZBUS_OBS_NAME(obs), err); + err = _zbus_notify_observer(chan, obs, end_time, buf); if (err) { last_error = err; + LOG_ERR("could not deliver notification to observer %s. Error code %d", + _ZBUS_OBS_NAME(obs), err); + if (err == -ENOMEM) { + if (IS_ENABLED(CONFIG_ZBUS_MSG_SUBSCRIBER)) { + net_buf_unref(buf); + } + return err; + } } + + LOG_DBG(" %d -> %s", index++, _ZBUS_OBS_NAME(obs)); } #if defined(CONFIG_ZBUS_RUNTIME_OBSERVERS) @@ -100,15 +172,13 @@ static inline int _zbus_vded_exec(const struct zbus_channel *chan, k_timepoint_t SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&chan->data->observers, obs_nd, tmp, node) { - _ZBUS_ASSERT(obs_nd != NULL, "observer node is NULL"); - const struct zbus_observer *obs = obs_nd->obs; if (!obs->enabled) { continue; } - err = _zbus_notify_observer(chan, obs, end_time); + err = _zbus_notify_observer(chan, obs, end_time, buf); if (err) { last_error = err; @@ -116,6 +186,8 @@ static inline int _zbus_vded_exec(const struct zbus_channel *chan, k_timepoint_t } #endif /* CONFIG_ZBUS_RUNTIME_OBSERVERS */ + IF_ENABLED(CONFIG_ZBUS_MSG_SUBSCRIBER, (net_buf_unref(buf);)) + return last_error; } @@ -215,15 +287,43 @@ int zbus_sub_wait(const struct zbus_observer *sub, const struct zbus_channel **c { _ZBUS_ASSERT(!k_is_in_isr(), "zbus cannot be used inside ISRs"); _ZBUS_ASSERT(sub != NULL, "sub is required"); + _ZBUS_ASSERT(sub->type == ZBUS_OBSERVER_SUBSCRIBER_TYPE, "sub must be a SUBSCRIBER"); + _ZBUS_ASSERT(sub->queue != NULL, "sub queue is required"); + _ZBUS_ASSERT(chan != NULL, "chan is required"); + + return k_msgq_get(sub->queue, chan, timeout); +} + +#if defined(CONFIG_ZBUS_MSG_SUBSCRIBER) + +int zbus_sub_wait_msg(const struct zbus_observer *sub, const struct zbus_channel **chan, void *msg, + k_timeout_t timeout) +{ + _ZBUS_ASSERT(!k_is_in_isr(), "zbus subscribers cannot be used inside ISRs"); + _ZBUS_ASSERT(sub != NULL, "sub is required"); + _ZBUS_ASSERT(sub->type == ZBUS_OBSERVER_MSG_SUBSCRIBER_TYPE, + "sub must be a MSG_SUBSCRIBER"); + _ZBUS_ASSERT(sub->message_fifo != NULL, "sub message_fifo is required"); _ZBUS_ASSERT(chan != NULL, "chan is required"); + _ZBUS_ASSERT(msg != NULL, "msg is required"); + + struct net_buf *buf = net_buf_get(sub->message_fifo, timeout); - if (sub->queue == NULL) { - return -EINVAL; + if (buf == NULL) { + return -ENOMSG; } - return k_msgq_get(sub->queue, chan, timeout); + *chan = *((struct zbus_channel **)net_buf_user_data(buf)); + + memcpy(msg, net_buf_remove_mem(buf, zbus_chan_msg_size(*chan)), zbus_chan_msg_size(*chan)); + + net_buf_unref(buf); + + return 0; } +#endif /* CONFIG_ZBUS_MSG_SUBSCRIBER */ + int zbus_obs_set_chan_notification_mask(const struct zbus_observer *obs, const struct zbus_channel *chan, bool masked) { diff --git a/tests/bluetooth/audio/ascs/src/main.c b/tests/bluetooth/audio/ascs/src/main.c index b0bc7f88150..126c82d63bb 100644 --- a/tests/bluetooth/audio/ascs/src/main.c +++ b/tests/bluetooth/audio/ascs/src/main.c @@ -250,7 +250,6 @@ ZTEST_F(ascs_test_suite, test_release_ase_on_acl_disconnection_client_terminates mock_bt_iso_disconnected(chan, BT_HCI_ERR_REMOTE_USER_TERM_CONN); /* Expected to notify the upper layers */ - expect_bt_bap_unicast_server_cb_release_called_once(stream); expect_bt_bap_stream_ops_released_called_once(stream); bt_bap_unicast_server_unregister_cb(&mock_bap_unicast_server_cb); @@ -288,7 +287,6 @@ ZTEST_F(ascs_test_suite, test_release_ase_on_acl_disconnection_server_terminates k_sleep(K_MSEC(CONFIG_BT_ASCS_ISO_DISCONNECT_DELAY)); /* Expected to notify the upper layers */ - expect_bt_bap_unicast_server_cb_release_called_once(stream); expect_bt_bap_stream_ops_released_called_once(stream); bt_bap_unicast_server_unregister_cb(&mock_bap_unicast_server_cb); @@ -351,7 +349,6 @@ ZTEST_F(ascs_test_suite, test_release_stream_pair_on_acl_disconnection_client_te const struct bt_bap_stream *streams[2] = { &snk_stream, &src_stream }; expect_bt_bap_stream_ops_released_called_twice(streams); - expect_bt_bap_unicast_server_cb_release_called_twice(streams); bt_bap_unicast_server_unregister_cb(&mock_bap_unicast_server_cb); } @@ -413,7 +410,6 @@ ZTEST_F(ascs_test_suite, test_release_stream_pair_on_acl_disconnection_server_te const struct bt_bap_stream *streams[2] = { &snk_stream, &src_stream }; expect_bt_bap_stream_ops_released_called_twice(streams); - expect_bt_bap_unicast_server_cb_release_called_twice(streams); bt_bap_unicast_server_unregister_cb(&mock_bap_unicast_server_cb); } diff --git a/tests/bluetooth/audio/mocks/src/kernel.c b/tests/bluetooth/audio/mocks/src/kernel.c index ce9dcab5d3a..03dc2cf582d 100644 --- a/tests/bluetooth/audio/mocks/src/kernel.c +++ b/tests/bluetooth/audio/mocks/src/kernel.c @@ -56,6 +56,13 @@ int k_work_cancel_delayable(struct k_work_delayable *dwork) return 0; } +int k_work_cancel(struct k_work *work) +{ + (void)sys_slist_find_and_remove(&work_pending, &work->node); + + return 0; +} + void k_work_init(struct k_work *work, k_work_handler_t handler) { work->handler = handler; diff --git a/tests/bluetooth/controller/mock_ctrl/include/hal/ticker_vendor_hal.h b/tests/bluetooth/controller/mock_ctrl/include/hal/ticker_vendor_hal.h index bbd4b3e462c..8cb4e791a30 100644 --- a/tests/bluetooth/controller/mock_ctrl/include/hal/ticker_vendor_hal.h +++ b/tests/bluetooth/controller/mock_ctrl/include/hal/ticker_vendor_hal.h @@ -3,7 +3,12 @@ * * SPDX-License-Identifier: Apache-2.0 */ -#define HAL_TICKER_CNTR_CLK_FREQ_HZ 32768U + +#define HAL_TICKER_CNTR_CLK_FREQ_HZ 32768U +#define HAL_TICKER_CNTR_CLK_UNIT_FSEC 30517578125UL +#define HAL_TICKER_FSEC_PER_USEC 1000000000UL +#define HAL_TICKER_PSEC_PER_USEC 1000000UL +#define HAL_TICKER_FSEC_PER_PSEC 1000UL /* Macro defining the minimum counter compare offset */ #define HAL_TICKER_CNTR_CMP_OFFSET_MIN 3 @@ -11,30 +16,56 @@ /* Macro defining the max. counter update latency in ticks */ #define HAL_TICKER_CNTR_SET_LATENCY 0 +/* Macro defines the h/w supported most significant bit */ +#define HAL_TICKER_CNTR_MSBIT 23 + +/* Macro defining the HW supported counter bits */ +#define HAL_TICKER_CNTR_MASK 0x00FFFFFF + /* Macro to translate microseconds to tick units. * NOTE: This returns the floor value. */ -#define HAL_TICKER_US_TO_TICKS(x) \ - (((uint32_t)(((uint64_t)(x) * 1000000000UL) / 30517578125UL)) & HAL_TICKER_CNTR_MASK) +#define HAL_TICKER_US_TO_TICKS(x) \ + ( \ + ((uint32_t)(((uint64_t) (x) * HAL_TICKER_FSEC_PER_USEC) / \ + HAL_TICKER_CNTR_CLK_UNIT_FSEC)) & \ + HAL_TICKER_CNTR_MASK \ + ) -/* Macro returning remainder in nanoseconds */ -#define HAL_TICKER_REMAINDER(x) \ - ((((uint64_t)(x) * 1000000000UL) - ((uint64_t)HAL_TICKER_US_TO_TICKS(x) * 30517578125UL)) /\ - 1000UL) +/* Macro to translate microseconds to tick units. + * NOTE: This returns the ceil value. + */ +#define HAL_TICKER_US_TO_TICKS_CEIL(x) \ + ( \ + DIV_ROUND_UP(((uint64_t) (x) * HAL_TICKER_FSEC_PER_USEC), \ + HAL_TICKER_CNTR_CLK_UNIT_FSEC) & \ + HAL_TICKER_CNTR_MASK \ + ) /* Macro to translate tick units to microseconds. */ -#define HAL_TICKER_TICKS_TO_US(x) ((uint32_t)(((uint64_t)(x) * 30517578125UL) / 1000000000UL)) +#define HAL_TICKER_TICKS_TO_US(x) \ + ( \ + ((uint32_t)(((uint64_t)(x) * HAL_TICKER_CNTR_CLK_UNIT_FSEC) / \ + HAL_TICKER_FSEC_PER_USEC)) \ + ) -/* Macro defines the h/w supported most significant bit */ -#define HAL_TICKER_CNTR_MSBIT 23 - -/* Macro defining the HW supported counter bits */ -#define HAL_TICKER_CNTR_MASK 0x00FFFFFF +/* Macro returning remainder in picoseconds (to fit in 32-bits) */ +#define HAL_TICKER_REMAINDER(x) \ + ( \ + ( \ + ((uint64_t) (x) * HAL_TICKER_FSEC_PER_USEC) \ + - ((uint64_t)HAL_TICKER_US_TO_TICKS(x) * \ + HAL_TICKER_CNTR_CLK_UNIT_FSEC) \ + ) \ + / HAL_TICKER_FSEC_PER_PSEC \ + ) /* Macro defining the remainder resolution/range * ~ 1000000 * HAL_TICKER_TICKS_TO_US(1) */ -#define HAL_TICKER_REMAINDER_RANGE HAL_TICKER_TICKS_TO_US(1000000) +#define HAL_TICKER_REMAINDER_RANGE \ + HAL_TICKER_TICKS_TO_US(HAL_TICKER_PSEC_PER_USEC) /* Macro defining the margin for positioning re-scheduled nodes */ -#define HAL_TICKER_RESCHEDULE_MARGIN HAL_TICKER_US_TO_TICKS(150) +#define HAL_TICKER_RESCHEDULE_MARGIN \ + HAL_TICKER_US_TO_TICKS(150) diff --git a/tests/bluetooth/ctrl_isoal/Kconfig b/tests/bluetooth/ctrl_isoal/Kconfig index b7c10723b93..30049d53a5e 100644 --- a/tests/bluetooth/ctrl_isoal/Kconfig +++ b/tests/bluetooth/ctrl_isoal/Kconfig @@ -12,6 +12,25 @@ config BT_CTLR_CONN_ISO_GROUPS range 1 240 default 1 +config BT_CTLR_DEBUG_ISOAL + bool "[DEPRECATED] Bluetooth ISO-AL debug" + select DEPRECATED + depends on BT_CTLR_ISO + help + This option enables debug support for the Bluetooth ISO-AL. + +module = BT_CTLR_ISOAL +legacy-debug-sym = BT_CTLR_DEBUG_ISOAL +module-str = "Bluetooth Controller ISO-AL" +source "subsys/bluetooth/common/Kconfig.template.log_config_bt" + +config BT_CTLR_ISOAL_LOG_DBG_VERBOSE + bool "ISO-AL verbose debug logging" + depends on BT_CTLR_ISOAL_LOG_LEVEL = 4 + default n + help + Use this option to enable ISO-AL verbose debug logging. + config BT_CTLR_ISOAL_SINKS int "Number of Isochronous Adaptation Layer sinks (for unit tests)" diff --git a/tests/bluetooth/ctrl_isoal/prj.conf b/tests/bluetooth/ctrl_isoal/prj.conf index 5009e2785f8..2f429dd8c47 100644 --- a/tests/bluetooth/ctrl_isoal/prj.conf +++ b/tests/bluetooth/ctrl_isoal/prj.conf @@ -9,3 +9,5 @@ CONFIG_BT_CTLR_CONN_ISO=y CONFIG_BT_CTLR_ISOAL_SINKS=4 CONFIG_BT_CTLR_ISOAL_SOURCES=4 CONFIG_BT_CTLR_ISO_RX_SDU_BUFFERS=4 + +CONFIG_BT_CTLR_ISOAL_LOG_LEVEL_INF=y diff --git a/tests/bluetooth/ctrl_isoal/src/isoal_test_common.c b/tests/bluetooth/ctrl_isoal/src/isoal_test_common.c index a20ba5fd084..35947656336 100644 --- a/tests/bluetooth/ctrl_isoal/src/isoal_test_common.c +++ b/tests/bluetooth/ctrl_isoal/src/isoal_test_common.c @@ -23,6 +23,7 @@ #include #include +#include #include "util/memq.h" @@ -109,7 +110,7 @@ void isoal_test_create_unframed_pdu(uint8_t llid, uint16_t isoal_test_insert_segment(bool sc, bool cmplt, uint32_t time_offset, uint8_t *dataptr, uint8_t length, struct isoal_pdu_rx *pdu_meta) { - struct pdu_iso_sdu_sh seg_hdr; + uint8_t seg_hdr[PDU_ISO_SEG_HDR_SIZE + PDU_ISO_SEG_TIMEOFFSET_SIZE]; uint16_t pdu_payload_size; uint8_t hdr_write_size; uint16_t pdu_data_loc; @@ -122,12 +123,13 @@ uint16_t isoal_test_insert_segment(bool sc, bool cmplt, uint32_t time_offset, ui zassert_true(pdu_payload_size <= TEST_RX_PDU_PAYLOAD_MAX, "pdu_payload_size (%d)", pdu_payload_size); - seg_hdr.sc = sc; - seg_hdr.cmplt = cmplt; - seg_hdr.len = length + (sc ? 0 : PDU_ISO_SEG_TIMEOFFSET_SIZE); + /* Write header independent of endian dependent structures */ + WRITE_BIT(seg_hdr[0], 0, sc); /* sc */ + WRITE_BIT(seg_hdr[0], 1, cmplt); /* cmplt */ + seg_hdr[1] = length + (sc ? 0 : PDU_ISO_SEG_TIMEOFFSET_SIZE); if (!sc) { - seg_hdr.timeoffset = time_offset; + sys_put_le24(time_offset, &seg_hdr[PDU_ISO_SEG_HDR_SIZE]); } memcpy(&pdu_meta->pdu->payload[pdu_meta->pdu->len], &seg_hdr, hdr_write_size); diff --git a/tests/bluetooth/ctrl_isoal/src/isoal_test_debug.c b/tests/bluetooth/ctrl_isoal/src/isoal_test_debug.c index 464d2586598..084677cf462 100644 --- a/tests/bluetooth/ctrl_isoal/src/isoal_test_debug.c +++ b/tests/bluetooth/ctrl_isoal/src/isoal_test_debug.c @@ -52,6 +52,12 @@ void isoal_test_debug_print_rx_pdu(struct isoal_pdu_rx *pdu_meta) { zassert_not_null(pdu_meta, ""); + struct pdu_iso *pdu; + uint8_t seg_length; + + pdu = pdu_meta->pdu; + seg_length = 0; + PRINT("\n"); PRINT("PDU %04u (%10u) | %12s [%10s] %03u: ", (uint32_t) pdu_meta->meta->payload_number, @@ -60,8 +66,42 @@ void isoal_test_debug_print_rx_pdu(struct isoal_pdu_rx *pdu_meta) DU_ERR_TO_STR(pdu_meta->meta->status), pdu_meta->pdu->len); - for (int i = 0; i < pdu_meta->pdu->len; i++) { - PRINT("%02x ", pdu_meta->pdu->payload[i]); + for (uint8_t i = 0U; i < pdu->len; i++) { + if (seg_length == 0U && pdu->ll_id == PDU_BIS_LLID_FRAMED) { + seg_length = pdu->payload[i + 1U]; + PRINT("[%s %s %03u]", + pdu->payload[i] & BIT(0) ? "C" : "S", + pdu->payload[i] & BIT(1) ? "C" : "-", + pdu->payload[i + 1U]); + if ((pdu->payload[i] & BIT(0)) == 0U) { + PRINT("(%8uus)", + ((uint32_t)pdu->payload[i + 2U] + + ((uint32_t)pdu->payload[i + 3U] << 8) + + ((uint32_t)pdu->payload[i + 4U] << 16))); + } + + PRINT(" / "); + PRINT("[%02x %02x]", + pdu->payload[i], + pdu->payload[i + 1U]); + if ((pdu->payload[i] & BIT(0)) == 0U) { + PRINT("(%02x %02x %02x)", + (uint32_t)pdu->payload[i + 4U], + (uint32_t)pdu->payload[i + 3U], + (uint32_t)pdu->payload[i + 2U]); + } + + PRINT(" : "); + seg_length -= pdu->payload[i] & BIT(0) ? 0 : PDU_ISO_SEG_TIMEOFFSET_SIZE; + i += PDU_ISO_SEG_HDR_SIZE + + (pdu->payload[i] & BIT(0) ? 0 : PDU_ISO_SEG_TIMEOFFSET_SIZE); + } + + PRINT("%02x ", pdu->payload[i]); + seg_length--; + if (seg_length == 0 && pdu->ll_id == PDU_BIS_LLID_FRAMED) { + PRINT("\n%44s", ""); + } } PRINT("\n"); PRINT("\n"); @@ -193,6 +233,7 @@ void isoal_test_debug_print_tx_sdu(struct isoal_sdu_tx *tx_sdu) PRINT("%02x ", buf[i]); } PRINT("\n"); + PRINT("Cntr TS. <%10u>\n", tx_sdu->cntr_time_stamp); PRINT(" Ref. <%10u>\n", tx_sdu->grp_ref_point); PRINT(" Event <%10u>\n", (uint32_t)tx_sdu->target_event); PRINT("\n"); diff --git a/tests/bluetooth/ctrl_isoal/src/main.c b/tests/bluetooth/ctrl_isoal/src/main.c index 92ca21ff199..8cbd36c509a 100644 --- a/tests/bluetooth/ctrl_isoal/src/main.c +++ b/tests/bluetooth/ctrl_isoal/src/main.c @@ -18,6 +18,8 @@ #include #include +#include + #include DEFINE_FFF_GLOBALS; @@ -35,7 +37,7 @@ DEFINE_FFF_GLOBALS; ZTEST_SUITE(test_rx_basics, NULL, NULL, isoal_test_rx_common_before, NULL, NULL); ZTEST_SUITE(test_rx_unframed, NULL, NULL, isoal_test_rx_common_before, NULL, NULL); -ZTEST_SUITE(test_rx_framed, NULL, NULL, NULL, isoal_test_rx_common_before, NULL); +ZTEST_SUITE(test_rx_framed, NULL, NULL, isoal_test_rx_common_before, NULL, NULL); ZTEST_SUITE(test_tx_basics, NULL, NULL, isoal_test_tx_common_before, NULL, NULL); ZTEST_SUITE(test_tx_unframed, NULL, NULL, isoal_test_tx_common_before, NULL, NULL); diff --git a/tests/bluetooth/ctrl_isoal/src/sub_sets/isoal_test_rx.c b/tests/bluetooth/ctrl_isoal/src/sub_sets/isoal_test_rx.c index 2ce8862e1d4..fdb86af1f5c 100644 --- a/tests/bluetooth/ctrl_isoal/src/sub_sets/isoal_test_rx.c +++ b/tests/bluetooth/ctrl_isoal/src/sub_sets/isoal_test_rx.c @@ -60,19 +60,19 @@ static isoal_status_t custom_sink_sdu_alloc_test(const struct isoal_sink *sink_c #define ZASSERT_ISOAL_SDU_ALLOC_TEST(_typ, _sink, _pdu) \ zassert_equal_ptr(_sink, \ sink_sdu_alloc_test_fake.arg0_##_typ, \ - "\t\t%p != %p", \ + "\t\tExpected alloc sink at %p, got %p.", \ _sink, \ sink_sdu_alloc_test_fake.arg0_##_typ); \ zassert_equal_ptr(_pdu, \ sink_sdu_alloc_test_fake.arg1_##_typ, \ - "\t\t%p != %p", \ + "\t\tExpected alloc PDU buffer at %p, got %p.", \ _pdu, \ sink_sdu_alloc_test_fake.arg1_##_typ) #define ZASSERT_ISOAL_SDU_ALLOC_TEST_CALL_COUNT(_expected) \ zassert_equal(_expected, \ sink_sdu_alloc_test_fake.call_count, \ - "Expected %u got %u", \ + "Expected alloc called %u times, actual %u.", \ _expected, \ sink_sdu_alloc_test_fake.call_count) @@ -125,59 +125,59 @@ static isoal_status_t custom_sink_sdu_emit_test(const struct isoal_sink *sink_ct _sdu_status) \ zassert_equal_ptr(_sink, \ sink_sdu_emit_test_fake.arg0_##_typ, \ - "\t\t%p != %p", \ + "\t\tExpected sink at %p, got %p.", \ _sink, \ sink_sdu_emit_test_fake.arg0_##_typ); \ zassert_equal(_state, \ sink_sdu_emit_test_handler_fake.arg1_##_typ.sdu_state, \ - "\t\t%d != %d", \ - _state, \ - sink_sdu_emit_test_handler_fake.arg1_##_typ.sdu_state); \ + "\t\tExpected SDU state '%s', got '%s'.", \ + STATE_TO_STR(_state), \ + STATE_TO_STR(sink_sdu_emit_test_handler_fake.arg1_##_typ.sdu_state)); \ zassert_equal(_frag_sz, \ sink_sdu_emit_test_handler_fake.arg1_##_typ.sdu_frag_size, \ - "\t\t%d != %d", \ + "\t\tExpected SDU frag of size %u, got %u.", \ _frag_sz, \ sink_sdu_emit_test_handler_fake.arg1_##_typ.sdu_frag_size); \ zassert_equal(_frag_status, \ sink_sdu_emit_test_handler_fake.arg1_##_typ.sdu.status, \ - "\t\t%d != %d", \ - _frag_status, \ - sink_sdu_emit_test_handler_fake.arg1_##_typ.sdu.status); \ + "\t\tExpected SDU with status '%s', got '%s'.", \ + DU_ERR_TO_STR(_frag_status), \ + DU_ERR_TO_STR(sink_sdu_emit_test_handler_fake.arg1_##_typ.sdu.status)); \ zassert_equal(_timestamp, \ sink_sdu_emit_test_handler_fake.arg1_##_typ.sdu.timestamp, \ - "\t\t%d != %d", \ + "\t\tExpected SDU with timestamp %u, got %u.", \ _timestamp, \ sink_sdu_emit_test_handler_fake.arg1_##_typ.sdu.timestamp); \ zassert_equal(_sn, \ sink_sdu_emit_test_handler_fake.arg1_##_typ.sdu.sn, \ - "\t\t%d != %d", \ + "\t\tExpected SDU with sequence number %u, got %u.", \ _sn, \ sink_sdu_emit_test_handler_fake.arg1_##_typ.sdu.sn); \ - zassert_equal(_dbuf, \ - sink_sdu_emit_test_handler_fake.arg1_##_typ.sdu.contents.dbuf, \ - "\t\t%p != %p", \ - _dbuf, \ - sink_sdu_emit_test_handler_fake.arg1_##_typ.sdu.contents.dbuf); \ + zassert_equal_ptr(_dbuf, \ + sink_sdu_emit_test_handler_fake.arg1_##_typ.sdu.contents.dbuf, \ + "\t\tExpected SDU data buffer at %p, got %p.", \ + _dbuf, \ + sink_sdu_emit_test_handler_fake.arg1_##_typ.sdu.contents.dbuf); \ zassert_equal(_dbuf_sz, \ sink_sdu_emit_test_handler_fake.arg1_##_typ.sdu.contents.size, \ - "\t\t%d != %d", \ + "\t\tExpected SDU data buffer of size %u, got %u.", \ _dbuf_sz, \ sink_sdu_emit_test_handler_fake.arg1_##_typ.sdu.contents.size); \ zassert_equal(_total_sz, \ sink_sdu_emit_test_handler_fake.arg2_##_typ.total_sdu_size, \ - "\t\t%d != %d", \ + "\t\tExpected total size of SDU %u,got %u.", \ _total_sz, \ sink_sdu_emit_test_handler_fake.arg2_##_typ.total_sdu_size); \ zassert_equal(_sdu_status, \ sink_sdu_emit_test_handler_fake.arg2_##_typ.collated_status, \ - "\t\t%d != %d", \ - _sdu_status, \ - sink_sdu_emit_test_handler_fake.arg2_##_typ.collated_status) + "\t\tExpected SDU with status '%s', got '%s'.", \ + DU_ERR_TO_STR(_sdu_status), \ + DU_ERR_TO_STR(sink_sdu_emit_test_handler_fake.arg2_##_typ.collated_status)) #define ZASSERT_ISOAL_SDU_EMIT_TEST_CALL_COUNT(_expected) \ zassert_equal(_expected, \ sink_sdu_emit_test_fake.call_count, \ - "Expected %u got %u", \ + "Expected emit called %u times, actual %u.", \ _expected, \ sink_sdu_emit_test_fake.call_count) @@ -212,24 +212,24 @@ custom_sink_sdu_write_test(void *dbuf, const uint8_t *pdu_payload, const size_t #define ZASSERT_ISOAL_SDU_WRITE_TEST(_typ, _frag_buf, _payload_buf, _length) \ zassert_equal_ptr(_frag_buf, \ sink_sdu_write_test_fake.arg0_##_typ, \ - "\t\t%p != %p", \ + "\t\tExpected write buffer at %p, got %p.", \ _frag_buf, \ sink_sdu_write_test_fake.arg0_##_typ); \ zassert_equal_ptr(_payload_buf, \ sink_sdu_write_test_fake.arg1_##_typ, \ - "\t\t%p != %p", \ + "\t\tExpected write source at %p, got %p.", \ _payload_buf, \ sink_sdu_write_test_fake.arg1_##_typ); \ zassert_equal(_length, \ sink_sdu_write_test_fake.arg2_##_typ, \ - "\t\t%d != %d", \ + "\t\tExpected write length of %u, got %u.", \ _length, \ sink_sdu_write_test_fake.arg2_##_typ) #define ZASSERT_ISOAL_SDU_WRITE_TEST_CALL_COUNT(_expected) \ zassert_equal(_expected, \ sink_sdu_write_test_fake.call_count, \ - "Expected %u got %u", \ + "Expected write called %u times, actual %u.", \ _expected, \ sink_sdu_write_test_fake.call_count) @@ -2767,7 +2767,7 @@ ZTEST(test_rx_unframed, test_rx_unframed_single_pdu_err) seqn = 0; testdata_indx = 0; testdata_size = 13; - sdu_size = 13; + sdu_size = 0; total_sdu_size = COLLATED_RX_SDU_INFO(sdu_size, sdu_size); collated_status = COLLATED_RX_SDU_INFO(ISOAL_SDU_STATUS_ERRORS, ISOAL_SDU_STATUS_ERRORS); @@ -2804,11 +2804,9 @@ ZTEST(test_rx_unframed, test_rx_unframed_single_pdu_err) &isoal_global.sink_state[sink_hdl], /* Sink */ &rx_pdu_meta_buf.pdu_meta); /* PDU */ - /* SDU payload should be written */ - ZASSERT_ISOAL_SDU_WRITE_TEST(val, - &rx_sdu_frag_buf, /* SDU buffer */ - &rx_pdu_meta_buf.pdu[3], /* PDU payload */ - (testdata_size - testdata_indx)); /* Size */ + /* SDU payload should not be written */ + ZASSERT_ISOAL_SDU_WRITE_TEST_CALL_COUNT(0); + /* SDU should be emitted */ ZASSERT_ISOAL_SDU_EMIT_TEST(val, &isoal_global.sink_state[sink_hdl], /* Sink */ @@ -2838,7 +2836,6 @@ ZTEST(test_rx_unframed, test_rx_unframed_single_pdu_err) sdu_timestamp = (uint32_t)((int64_t)pdu_timestamp + latency); testdata_indx = testdata_size; testdata_size += 10; - sdu_size = 10; total_sdu_size = COLLATED_RX_SDU_INFO(sdu_size, sdu_size); collated_status = COLLATED_RX_SDU_INFO(ISOAL_SDU_STATUS_LOST_DATA, ISOAL_SDU_STATUS_LOST_DATA); @@ -2866,11 +2863,9 @@ ZTEST(test_rx_unframed, test_rx_unframed_single_pdu_err) &isoal_global.sink_state[sink_hdl], /* Sink */ &rx_pdu_meta_buf.pdu_meta); /* PDU */ - /* SDU payload should be written */ - ZASSERT_ISOAL_SDU_WRITE_TEST(val, - &rx_sdu_frag_buf, /* SDU buffer */ - &rx_pdu_meta_buf.pdu[3], /* PDU payload */ - (testdata_size - testdata_indx)); /* Size */ + /* SDU payload should not be written */ + ZASSERT_ISOAL_SDU_WRITE_TEST_CALL_COUNT(0); + /* SDU should be emitted */ ZASSERT_ISOAL_SDU_EMIT_TEST(val, &isoal_global.sink_state[sink_hdl], /* Sink */ @@ -3239,7 +3234,6 @@ ZTEST(test_rx_unframed, test_rx_unframed_seq_pdu_err1) payload_number++; testdata_indx = testdata_size; testdata_size += 10; - sdu_size += 10; total_sdu_size = COLLATED_RX_SDU_INFO(sdu_size, sdu_size); collated_status = COLLATED_RX_SDU_INFO(ISOAL_SDU_STATUS_LOST_DATA, ISOAL_SDU_STATUS_LOST_DATA); @@ -3263,12 +3257,11 @@ ZTEST(test_rx_unframed, test_rx_unframed_seq_pdu_err1) /* Test recombine (Black Box) */ /* A new SDU should not be allocated */ + ZASSERT_ISOAL_SDU_ALLOC_TEST_CALL_COUNT(1); + + /* SDU payload should not be written */ + ZASSERT_ISOAL_SDU_WRITE_TEST_CALL_COUNT(1); - /* SDU payload should be written */ - ZASSERT_ISOAL_SDU_WRITE_TEST(val, - &rx_sdu_frag_buf, /* SDU buffer */ - &rx_pdu_meta_buf.pdu[3], /* PDU payload */ - (testdata_size - testdata_indx)); /* Size */ /* SDU should be emitted */ ZASSERT_ISOAL_SDU_EMIT_TEST(val, &isoal_global.sink_state[sink_hdl], /* Sink */ @@ -3535,7 +3528,7 @@ ZTEST(test_rx_unframed, test_rx_unframed_seq_pdu_err2) payload_number++; testdata_indx = testdata_size; testdata_size += 10; - sdu_size = 10; + sdu_size = 0; total_sdu_size = COLLATED_RX_SDU_INFO(sdu_size, 50); collated_status = COLLATED_RX_SDU_INFO(ISOAL_SDU_STATUS_LOST_DATA, ISOAL_SDU_STATUS_LOST_DATA); @@ -3565,11 +3558,9 @@ ZTEST(test_rx_unframed, test_rx_unframed_seq_pdu_err2) &isoal_global.sink_state[sink_hdl], /* Sink */ &rx_pdu_meta_buf.pdu_meta); /* PDU */ - /* SDU payload should be written */ - ZASSERT_ISOAL_SDU_WRITE_TEST(val, - &rx_sdu_frag_buf, /* SDU buffer */ - &rx_pdu_meta_buf.pdu[3], /* PDU payload */ - (testdata_size - testdata_indx)); /* Size */ + /* SDU payload should not be written */ + ZASSERT_ISOAL_SDU_WRITE_TEST_CALL_COUNT(1); + /* SDU should be emitted */ ZASSERT_ISOAL_SDU_EMIT_TEST(val, &isoal_global.sink_state[sink_hdl], /* Sink */ @@ -4569,7 +4560,7 @@ ZTEST(test_rx_unframed, test_rx_unframed_padding_error1) seqn = 0; testdata_indx = 0; testdata_size = 13; - sdu_size = 13; + sdu_size = 0; total_sdu_size = COLLATED_RX_SDU_INFO(sdu_size, sdu_size); collated_status = COLLATED_RX_SDU_INFO(ISOAL_SDU_STATUS_ERRORS, ISOAL_SDU_STATUS_ERRORS); @@ -4607,11 +4598,9 @@ ZTEST(test_rx_unframed, test_rx_unframed_padding_error1) &isoal_global.sink_state[sink_hdl], /* Sink */ &rx_pdu_meta_buf.pdu_meta); /* PDU */ - /* SDU payload should be written */ - ZASSERT_ISOAL_SDU_WRITE_TEST(val, - &rx_sdu_frag_buf, /* SDU buffer */ - &rx_pdu_meta_buf.pdu[3], /* PDU payload */ - (testdata_size - testdata_indx)); /* Size */ + /* SDU payload should not be written */ + ZASSERT_ISOAL_SDU_WRITE_TEST_CALL_COUNT(0); + /* SDU should be emitted */ ZASSERT_ISOAL_SDU_EMIT_TEST(val, &isoal_global.sink_state[sink_hdl], /* Sink */ @@ -5935,7 +5924,6 @@ ZTEST(test_rx_unframed, test_rx_unframed_dbl_pdu_invalid_llid2_pdu_err) payload_number++; testdata_indx = testdata_size; testdata_size += 10; - sdu_size += 10; total_sdu_size = COLLATED_RX_SDU_INFO(sdu_size, sdu_size); collated_status = COLLATED_RX_SDU_INFO(ISOAL_SDU_STATUS_ERRORS, ISOAL_SDU_STATUS_ERRORS); @@ -5958,12 +5946,11 @@ ZTEST(test_rx_unframed, test_rx_unframed_dbl_pdu_invalid_llid2_pdu_err) /* Test recombine (Black Box) */ /* A new SDU should not be allocated */ + ZASSERT_ISOAL_SDU_ALLOC_TEST_CALL_COUNT(1); + + /* SDU payload should not be written */ + ZASSERT_ISOAL_SDU_WRITE_TEST_CALL_COUNT(1); - /* SDU payload should be written */ - ZASSERT_ISOAL_SDU_WRITE_TEST(val, - &rx_sdu_frag_buf, /* SDU buffer */ - &rx_pdu_meta_buf.pdu[3], /* PDU payload */ - (testdata_size - testdata_indx)); /* Size */ /* SDU should be emitted */ ZASSERT_ISOAL_SDU_EMIT_TEST(val, &isoal_global.sink_state[sink_hdl], /* Sink */ @@ -8332,27 +8319,30 @@ ZTEST(test_rx_framed, test_rx_framed_dbl_pdu_dbl_sdu_pdu_err1) */ ZTEST(test_rx_framed, test_rx_framed_dbl_pdu_dbl_sdu_pdu_err2) { + const uint8_t test_data_size = 33; + const uint8_t max_sdu_burst = 2; + + struct rx_sdu_frag_buffer rx_sdu_frag_buf[max_sdu_burst]; + struct isoal_sdu_buffer sdu_buffer[max_sdu_burst]; + isoal_sdu_status_t collated_status[max_sdu_burst]; struct rx_pdu_meta_buffer rx_pdu_meta_buf; - struct rx_sdu_frag_buffer rx_sdu_frag_buf; - struct isoal_sdu_buffer sdu_buffer; - isoal_sdu_status_t collated_status; + isoal_sdu_len_t sdu_size[max_sdu_burst]; + uint16_t total_sdu_size[max_sdu_burst]; + uint32_t sdu_timestamp[max_sdu_burst]; + uint8_t testdata[test_data_size]; isoal_sink_handle_t sink_hdl; uint32_t stream_sync_delay; uint32_t group_sync_delay; - isoal_sdu_len_t sdu_size; uint8_t iso_interval_int; uint16_t pdu_data_loc[5]; uint32_t iso_interval_us; uint64_t payload_number; - uint16_t total_sdu_size; uint32_t sdu_timeoffset; uint32_t pdu_timestamp; - uint32_t sdu_timestamp; uint16_t testdata_indx; uint16_t testdata_size; uint32_t sdu_interval; isoal_sdu_cnt_t seqn; - uint8_t testdata[33]; isoal_status_t err; uint32_t latency; uint8_t role; @@ -8371,12 +8361,14 @@ ZTEST(test_rx_framed, test_rx_framed_dbl_pdu_dbl_sdu_pdu_err2) /* PDU 1 -------------------------------------------------------------*/ isoal_test_init_rx_pdu_buffer(&rx_pdu_meta_buf); - isoal_test_init_rx_sdu_buffer(&rx_sdu_frag_buf); - init_test_data_buffer(testdata, 33); + isoal_test_init_rx_sdu_buffer(&rx_sdu_frag_buf[0]); + init_test_data_buffer(testdata, test_data_size); memset(pdu_data_loc, 0, sizeof(pdu_data_loc)); - sdu_buffer.dbuf = &rx_sdu_frag_buf; - sdu_buffer.size = TEST_RX_SDU_FRAG_PAYLOAD_MAX; + sdu_buffer[0].dbuf = &rx_sdu_frag_buf[0]; + sdu_buffer[0].size = TEST_RX_SDU_FRAG_PAYLOAD_MAX; + sdu_buffer[1].dbuf = &rx_sdu_frag_buf[1]; + sdu_buffer[1].size = TEST_RX_SDU_FRAG_PAYLOAD_MAX; payload_number = 1000 * BN; pdu_timestamp = 9249; latency = calc_rx_latency_by_role(role, @@ -8388,13 +8380,13 @@ ZTEST(test_rx_framed, test_rx_framed_dbl_pdu_dbl_sdu_pdu_err2) group_sync_delay); sdu_timeoffset = group_sync_delay - 50; /* PDU will have errors. Time stamp is only an approximation */ - sdu_timestamp = (uint32_t)((int64_t)pdu_timestamp + latency - iso_interval_us); + sdu_timestamp[0] = (uint32_t)((int64_t)pdu_timestamp + latency - iso_interval_us); seqn = 0; testdata_indx = 0; testdata_size = 23; - sdu_size = 0; - total_sdu_size = COLLATED_RX_SDU_INFO(sdu_size, sdu_size); - collated_status = + sdu_size[0] = 0; + total_sdu_size[0] = COLLATED_RX_SDU_INFO(sdu_size[0], sdu_size[0]); + collated_status[0] = COLLATED_RX_SDU_INFO(ISOAL_SDU_STATUS_LOST_DATA, ISOAL_SDU_STATUS_LOST_DATA); sink_hdl = basic_rx_test_setup(0xADAD, /* Handle */ @@ -8418,7 +8410,7 @@ ZTEST(test_rx_framed, test_rx_framed_dbl_pdu_dbl_sdu_pdu_err2) &rx_pdu_meta_buf.pdu_meta); /* Set callback function return values */ - push_custom_sink_sdu_alloc_test_output_buffer(&sdu_buffer); + push_custom_sink_sdu_alloc_test_output_buffer(&sdu_buffer[0]); sink_sdu_alloc_test_fake.return_val = ISOAL_STATUS_OK; sink_sdu_write_test_fake.return_val = ISOAL_STATUS_OK; sink_sdu_emit_test_fake.return_val = ISOAL_STATUS_OK; @@ -8428,25 +8420,14 @@ ZTEST(test_rx_framed, test_rx_framed_dbl_pdu_dbl_sdu_pdu_err2) zassert_equal(err, ISOAL_STATUS_OK, "err = 0x%02x", err); /* Test recombine (Black Box) */ - /* A new SDU should be allocated */ - ZASSERT_ISOAL_SDU_ALLOC_TEST(val, - &isoal_global.sink_state[sink_hdl], /* Sink */ - &rx_pdu_meta_buf.pdu_meta); /* PDU */ + /* A new SDU should not be allocated */ + ZASSERT_ISOAL_SDU_ALLOC_TEST_CALL_COUNT(0); /* SDU payload should not be written */ + ZASSERT_ISOAL_SDU_WRITE_TEST_CALL_COUNT(0); - /* SDU should be emitted */ - ZASSERT_ISOAL_SDU_EMIT_TEST(val, - &isoal_global.sink_state[sink_hdl], /* Sink */ - BT_ISO_SINGLE, /* Frag state */ - sdu_size, /* Frag size */ - ISOAL_SDU_STATUS_LOST_DATA, /* Frag status */ - sdu_timestamp, /* Timestamp */ - seqn, /* Seq. number */ - sdu_buffer.dbuf, /* Buffer */ - sdu_buffer.size, /* Buffer size */ - total_sdu_size, /* Total size */ - collated_status); /* SDU status */ + /* SDU should not be emitted */ + ZASSERT_ISOAL_SDU_EMIT_TEST_CALL_COUNT(0); /* Test recombine (White Box) */ zassert_equal(isoal_global.sink_state[sink_hdl].sdu_production.fsm, @@ -8457,18 +8438,17 @@ ZTEST(test_rx_framed, test_rx_framed_dbl_pdu_dbl_sdu_pdu_err2) /* PDU 2 -------------------------------------------------------------*/ isoal_test_init_rx_pdu_buffer(&rx_pdu_meta_buf); - isoal_test_init_rx_sdu_buffer(&rx_sdu_frag_buf); + isoal_test_init_rx_sdu_buffer(&rx_sdu_frag_buf[1]); payload_number++; sdu_timeoffset = get_next_time_offset(sdu_timeoffset, iso_interval_us, sdu_interval, false); - sdu_timestamp = (uint32_t)((int64_t)pdu_timestamp + latency - sdu_timeoffset); - seqn++; + sdu_timestamp[1] = (uint32_t)((int64_t)pdu_timestamp + latency - sdu_timeoffset); testdata_indx = testdata_size; testdata_size += 10; - sdu_size = 10; - total_sdu_size = COLLATED_RX_SDU_INFO(sdu_size, sdu_size); - collated_status = COLLATED_RX_SDU_INFO(ISOAL_SDU_STATUS_VALID, ISOAL_SDU_STATUS_VALID); + sdu_size[1] = 10; + total_sdu_size[1] = COLLATED_RX_SDU_INFO(sdu_size[1], sdu_size[1]); + collated_status[1] = COLLATED_RX_SDU_INFO(ISOAL_SDU_STATUS_VALID, ISOAL_SDU_STATUS_VALID); isoal_test_create_framed_pdu_base(payload_number, pdu_timestamp, @@ -8480,7 +8460,7 @@ ZTEST(test_rx_framed, test_rx_framed_dbl_pdu_dbl_sdu_pdu_err2) &rx_pdu_meta_buf.pdu_meta); /* Set callback function return values */ - push_custom_sink_sdu_alloc_test_output_buffer(&sdu_buffer); + push_custom_sink_sdu_alloc_test_output_buffer(&sdu_buffer[1]); sink_sdu_alloc_test_fake.return_val = ISOAL_STATUS_OK; sink_sdu_write_test_fake.return_val = ISOAL_STATUS_OK; sink_sdu_emit_test_fake.return_val = ISOAL_STATUS_OK; @@ -8490,14 +8470,38 @@ ZTEST(test_rx_framed, test_rx_framed_dbl_pdu_dbl_sdu_pdu_err2) zassert_equal(err, ISOAL_STATUS_OK, "err = 0x%02x", err); /* Test recombine (Black Box) */ + /* SDU 0 -------------------------------------------------------------*/ + /* A new SDU should be allocated */ + ZASSERT_ISOAL_SDU_ALLOC_TEST(history[0], + &isoal_global.sink_state[sink_hdl], /* Sink */ + &rx_pdu_meta_buf.pdu_meta); /* PDU */ + + /* SDU payload should not be written */ + + /* SDU should be emitted */ + ZASSERT_ISOAL_SDU_EMIT_TEST(history[0], + &isoal_global.sink_state[sink_hdl], /* Sink */ + BT_ISO_SINGLE, /* Frag state */ + sdu_size[0], /* Frag size */ + ISOAL_SDU_STATUS_LOST_DATA, /* Frag status */ + sdu_timestamp[0], /* Timestamp */ + seqn, /* Seq. number */ + sdu_buffer[0].dbuf, /* Buffer */ + sdu_buffer[0].size, /* Buffer size */ + total_sdu_size[0], /* Total size */ + collated_status[0]); /* SDU status */ + + /* SDU 1 -------------------------------------------------------------*/ + seqn++; /* A new SDU should be allocated */ ZASSERT_ISOAL_SDU_ALLOC_TEST(val, &isoal_global.sink_state[sink_hdl], /* Sink */ &rx_pdu_meta_buf.pdu_meta); /* PDU */ /* SDU payload should be written */ + ZASSERT_ISOAL_SDU_WRITE_TEST_CALL_COUNT(1); ZASSERT_ISOAL_SDU_WRITE_TEST(val, - &rx_sdu_frag_buf, /* SDU buffer */ + &rx_sdu_frag_buf[1], /* SDU buffer */ &rx_pdu_meta_buf.pdu[3 + pdu_data_loc[1]], /* PDU payload */ (testdata_size - testdata_indx)); /* Size */ @@ -8506,14 +8510,14 @@ ZTEST(test_rx_framed, test_rx_framed_dbl_pdu_dbl_sdu_pdu_err2) ZASSERT_ISOAL_SDU_EMIT_TEST(val, &isoal_global.sink_state[sink_hdl], /* Sink */ BT_ISO_SINGLE, /* Frag state */ - sdu_size, /* Frag size */ + sdu_size[1], /* Frag size */ ISOAL_SDU_STATUS_VALID, /* Frag status */ - sdu_timestamp, /* Timestamp */ + sdu_timestamp[1], /* Timestamp */ seqn, /* Seq. number */ - sdu_buffer.dbuf, /* Buffer */ - sdu_buffer.size, /* Buffer size */ - total_sdu_size, /* Total size */ - collated_status); /* SDU status */ + sdu_buffer[1].dbuf, /* Buffer */ + sdu_buffer[1].size, /* Buffer size */ + total_sdu_size[1], /* Total size */ + collated_status[1]); /* SDU status */ /* Test recombine (White Box) */ zassert_equal(isoal_global.sink_state[sink_hdl].sdu_production.fsm, diff --git a/tests/bluetooth/ctrl_isoal/src/sub_sets/isoal_test_tx.c b/tests/bluetooth/ctrl_isoal/src/sub_sets/isoal_test_tx.c index fe375b7accd..c9a6fe67637 100644 --- a/tests/bluetooth/ctrl_isoal/src/sub_sets/isoal_test_tx.c +++ b/tests/bluetooth/ctrl_isoal/src/sub_sets/isoal_test_tx.c @@ -111,7 +111,8 @@ static void push_custom_source_pdu_write_test_sdu_payload(const uint8_t *data, c } static void check_next_custom_source_pdu_write_test_sdu_payload(const uint8_t *data, - const size_t length) + const size_t length, + const uint32_t line) { size_t pos = custom_source_pdu_write_test_sdu_payloads.pos; size_t buffer_size = custom_source_pdu_write_test_sdu_payloads.buffer_size; @@ -125,7 +126,8 @@ static void check_next_custom_source_pdu_write_test_sdu_payload(const uint8_t *d for (size_t i = 0; i < custom_source_pdu_write_test_sdu_payloads.out_size[pos]; i++) { zassert_equal(custom_source_pdu_write_test_sdu_payloads.out[pos][i], data[i], - "deviation at index %u, expected %u, got %u", + "[Line %lu] deviation at index %u, expected %u, got %u", + line, i, data[i], custom_source_pdu_write_test_sdu_payloads.out[pos][i]); @@ -193,14 +195,14 @@ static isoal_status_t custom_source_pdu_write_test(struct isoal_pdu_buffer *pdu_ _consume_len, \ source_pdu_write_test_fake.arg3_##_typ); \ check_next_custom_source_pdu_write_test_sdu_payload((const uint8_t *)_sdu_payload, \ - _consume_len); + _consume_len, __LINE__) #define ZASSERT_PDU_WRITE_TEST_CALL_COUNT(_expected) \ zassert_equal(_expected, \ source_pdu_write_test_fake.call_count, \ "Expected %u, got %u", \ _expected, \ - source_pdu_write_test_fake.call_count); + source_pdu_write_test_fake.call_count) /*------------------ PDU Emit Callback --------------------------------------*/ /** @@ -422,7 +424,7 @@ static isoal_source_handle_t basic_tx_test_setup(uint16_t handle, burst_number, flush_timeout, max_octets, - (iso_interval_int * CONN_INT_UNIT_US), + (iso_interval_int * ISO_INT_UNIT_US), sdu_interval, stream_sync_delay, group_sync_delay); @@ -478,6 +480,7 @@ static void isoal_test_create_sdu_fagment(uint8_t sdu_state, uint16_t sdu_total_length, uint16_t packet_number, uint32_t timestamp, + uint32_t cntr_timestamp, uint32_t ref_point, uint64_t target_event, struct isoal_sdu_tx *sdu_tx) @@ -486,6 +489,7 @@ static void isoal_test_create_sdu_fagment(uint8_t sdu_state, sdu_tx->packet_sn = packet_number; sdu_tx->iso_sdu_length = sdu_total_length; sdu_tx->time_stamp = timestamp; + sdu_tx->cntr_time_stamp = cntr_timestamp; sdu_tx->grp_ref_point = ref_point; sdu_tx->target_event = target_event; memcpy(sdu_tx->dbuf, dataptr, length); @@ -536,8 +540,8 @@ ZTEST(test_tx_basics, test_source_isoal_test_create_destroy) max_octets = 40; sdu_interval_int = 1; iso_interval_int = 1; - iso_interval = iso_interval_int * CONN_INT_UNIT_US; - sdu_interval = sdu_interval_int * CONN_INT_UNIT_US; + iso_interval = iso_interval_int * ISO_INT_UNIT_US; + sdu_interval = sdu_interval_int * ISO_INT_UNIT_US; stream_sync_delay = iso_interval - 200; group_sync_delay = iso_interval - 50; @@ -606,8 +610,8 @@ ZTEST(test_tx_basics, test_source_isoal_test_create_destroy) max_octets += max_octets / 2; sdu_interval_int++; iso_interval_int = iso_interval_int * sdu_interval_int; - sdu_interval = (sdu_interval_int * CONN_INT_UNIT_US) - (framed ? 100 : 0); - iso_interval = iso_interval_int * CONN_INT_UNIT_US; + sdu_interval = (sdu_interval_int * ISO_INT_UNIT_US) - (framed ? 100 : 0); + iso_interval = iso_interval_int * ISO_INT_UNIT_US; stream_sync_delay = iso_interval - (200 * i); group_sync_delay = iso_interval - 50; } @@ -654,9 +658,9 @@ ZTEST(test_tx_basics, test_source_isoal_test_create_err) flush_timeout = 1; framed = false; iso_interval_int = 1; - sdu_interval = CONN_INT_UNIT_US; - stream_sync_delay = CONN_INT_UNIT_US - 200; - group_sync_delay = CONN_INT_UNIT_US - 50; + sdu_interval = ISO_INT_UNIT_US; + stream_sync_delay = ISO_INT_UNIT_US - 200; + group_sync_delay = ISO_INT_UNIT_US - 50; res = isoal_init(); zassert_equal(res, ISOAL_STATUS_OK, "res = 0x%02x", res); @@ -747,12 +751,12 @@ ZTEST(test_tx_unframed, test_tx_unframed_1_sdu_1_frag_1_pdu_maxPDU) /* Settings */ role = ISOAL_ROLE_PERIPHERAL; iso_interval_int = 1; - sdu_interval = CONN_INT_UNIT_US; + sdu_interval = ISO_INT_UNIT_US; max_octets = TEST_TX_PDU_PAYLOAD_MAX - 5; BN = 1; FT = 1; - stream_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 200; - group_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 50; + stream_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 200; + group_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 50; /* SDU Frag 1 --------------------------------------------------------*/ isoal_test_init_tx_pdu_buffer(&tx_pdu_meta_buf); @@ -764,7 +768,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_1_sdu_1_frag_1_pdu_maxPDU) sdu_packet_number = 2000; event_number = 2000; sdu_timestamp = 9249; - ref_point = sdu_timestamp + (iso_interval_int * CONN_INT_UNIT_US) - 50; + ref_point = sdu_timestamp + (iso_interval_int * ISO_INT_UNIT_US) - 50; sdu_total_size = TEST_TX_PDU_PAYLOAD_MAX - 5; testdata_indx = 0; testdata_size = TEST_TX_PDU_PAYLOAD_MAX - 5; @@ -790,6 +794,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_1_sdu_1_frag_1_pdu_maxPDU) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -875,12 +880,12 @@ ZTEST(test_tx_unframed, test_tx_unframed_1_sdu_1_frag_1_pdu_bufSize) /* Settings */ role = ISOAL_ROLE_PERIPHERAL; iso_interval_int = 1; - sdu_interval = CONN_INT_UNIT_US; + sdu_interval = ISO_INT_UNIT_US; max_octets = TEST_TX_PDU_PAYLOAD_MAX + 5; BN = 1; FT = 1; - stream_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 200; - group_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 50; + stream_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 200; + group_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 50; /* SDU Frag 1 --------------------------------------------------------*/ isoal_test_init_tx_pdu_buffer(&tx_pdu_meta_buf); @@ -892,7 +897,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_1_sdu_1_frag_1_pdu_bufSize) sdu_packet_number = 2000; event_number = 2000; sdu_timestamp = 9249; - ref_point = sdu_timestamp + (iso_interval_int * CONN_INT_UNIT_US) - 50; + ref_point = sdu_timestamp + (iso_interval_int * ISO_INT_UNIT_US) - 50; sdu_total_size = TEST_TX_PDU_PAYLOAD_MAX; testdata_indx = 0; testdata_size = TEST_TX_PDU_PAYLOAD_MAX; @@ -918,6 +923,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_1_sdu_1_frag_1_pdu_bufSize) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -993,12 +999,12 @@ ZTEST(test_tx_unframed, test_tx_unframed_1_sdu_1_frag_3_pdu) /* Settings */ role = ISOAL_ROLE_PERIPHERAL; iso_interval_int = 1; - sdu_interval = CONN_INT_UNIT_US; + sdu_interval = ISO_INT_UNIT_US; max_octets = TEST_TX_PDU_PAYLOAD_MAX - 5; BN = 3; FT = 1; - stream_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 200; - group_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 50; + stream_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 200; + group_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 50; /* SDU Frag 1 --------------------------------------------------------*/ isoal_test_init_tx_pdu_buffer(&tx_pdu_meta_buf); @@ -1010,7 +1016,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_1_sdu_1_frag_3_pdu) sdu_packet_number = 2000; event_number = 2000; sdu_timestamp = 9249; - ref_point = sdu_timestamp + (iso_interval_int * CONN_INT_UNIT_US) - 50; + ref_point = sdu_timestamp + (iso_interval_int * ISO_INT_UNIT_US) - 50; sdu_total_size = 100; testdata_indx = 0; testdata_size = 100; @@ -1033,6 +1039,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_1_sdu_1_frag_3_pdu) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -1152,12 +1159,12 @@ ZTEST(test_tx_unframed, test_tx_unframed_1_sdu_3_frag_1_pdu) /* Settings */ role = ISOAL_ROLE_PERIPHERAL; iso_interval_int = 1; - sdu_interval = CONN_INT_UNIT_US; + sdu_interval = ISO_INT_UNIT_US; max_octets = TEST_TX_PDU_PAYLOAD_MAX + 5; BN = 1; FT = 1; - stream_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 200; - group_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 50; + stream_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 200; + group_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 50; /* SDU Frag 1 --------------------------------------------------------*/ isoal_test_init_tx_pdu_buffer(&tx_pdu_meta_buf); @@ -1169,7 +1176,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_1_sdu_3_frag_1_pdu) sdu_packet_number = 2000; event_number = 2000; sdu_timestamp = 9249; - ref_point = sdu_timestamp + (iso_interval_int * CONN_INT_UNIT_US) - 50; + ref_point = sdu_timestamp + (iso_interval_int * ISO_INT_UNIT_US) - 50; sdu_total_size = TEST_TX_PDU_PAYLOAD_MAX; testdata_indx = 0; testdata_size = TEST_TX_PDU_PAYLOAD_MAX / 3; @@ -1193,6 +1200,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_1_sdu_3_frag_1_pdu) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -1239,6 +1247,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_1_sdu_3_frag_1_pdu) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -1282,6 +1291,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_1_sdu_3_frag_1_pdu) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -1357,12 +1367,12 @@ ZTEST(test_tx_unframed, test_tx_unframed_1_sdu_3_frag_2_pdu) /* Settings */ role = ISOAL_ROLE_PERIPHERAL; iso_interval_int = 1; - sdu_interval = CONN_INT_UNIT_US; + sdu_interval = ISO_INT_UNIT_US; max_octets = TEST_TX_PDU_PAYLOAD_MAX + 5; BN = 2; FT = 1; - stream_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 200; - group_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 50; + stream_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 200; + group_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 50; /* SDU Frag 1 --------------------------------------------------------*/ isoal_test_init_tx_pdu_buffer(&tx_pdu_meta_buf[0]); @@ -1378,7 +1388,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_1_sdu_3_frag_2_pdu) sdu_packet_number = 2000; event_number = 2000; sdu_timestamp = 9249; - ref_point = sdu_timestamp + (iso_interval_int * CONN_INT_UNIT_US) - 50; + ref_point = sdu_timestamp + (iso_interval_int * ISO_INT_UNIT_US) - 50; sdu_total_size = TEST_TX_PDU_PAYLOAD_MAX * 2; testdata_indx = 0; testdata_size = (TEST_TX_PDU_PAYLOAD_MAX * 2) / 3; @@ -1401,6 +1411,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_1_sdu_3_frag_2_pdu) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -1449,6 +1460,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_1_sdu_3_frag_2_pdu) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -1515,6 +1527,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_1_sdu_3_frag_2_pdu) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -1689,12 +1702,12 @@ ZTEST(test_tx_unframed, test_tx_unframed_2_sdu_1_frag_2_pdu_ts_wrap1) /* Settings */ role = ISOAL_ROLE_PERIPHERAL; iso_interval_int = 1; - sdu_interval = CONN_INT_UNIT_US; + sdu_interval = ISO_INT_UNIT_US; max_octets = TEST_TX_PDU_PAYLOAD_MAX - 5; BN = 1; FT = 1; - stream_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 200; - group_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 50; + stream_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 200; + group_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 50; /* SDU Frag 1 --------------------------------------------------------*/ isoal_test_init_tx_pdu_buffer(&tx_pdu_meta_buf); @@ -1732,6 +1745,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_2_sdu_1_frag_2_pdu_ts_wrap1) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -1785,6 +1799,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_2_sdu_1_frag_2_pdu_ts_wrap1) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -1821,7 +1836,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_2_sdu_1_frag_2_pdu_ts_wrap1) /* Check TX Sync info */ tx_sync_seq_expected = 2; - tx_sync_timestamp_expected = (iso_interval_int * CONN_INT_UNIT_US) - 1; + tx_sync_timestamp_expected = (iso_interval_int * ISO_INT_UNIT_US) - 1; tx_sync_offset_expected = 0; err = isoal_tx_get_sync_info(source_hdl, &tx_sync_seq, &tx_sync_timestamp, &tx_sync_offset); @@ -1870,12 +1885,12 @@ ZTEST(test_tx_unframed, test_tx_unframed_2_sdu_3_frag_4_pdu) /* Settings */ role = ISOAL_ROLE_PERIPHERAL; iso_interval_int = 2; - sdu_interval = CONN_INT_UNIT_US; + sdu_interval = ISO_INT_UNIT_US; max_octets = TEST_TX_PDU_PAYLOAD_MAX + 5; BN = 4; FT = 1; - stream_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 200; - group_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 50; + stream_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 200; + group_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 50; /* SDU 1 Frag 1 ------------------------------------------------------*/ isoal_test_init_tx_pdu_buffer(&tx_pdu_meta_buf[0]); @@ -1891,7 +1906,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_2_sdu_3_frag_4_pdu) sdu_packet_number = 2000; event_number = 2000; sdu_timestamp = 9249; - ref_point = sdu_timestamp + (iso_interval_int * CONN_INT_UNIT_US) - 50; + ref_point = sdu_timestamp + (iso_interval_int * ISO_INT_UNIT_US) - 50; sdu_total_size = TEST_TX_PDU_PAYLOAD_MAX * 2; testdata_indx = 0; testdata_size = (TEST_TX_PDU_PAYLOAD_MAX * 2) / 3; @@ -1914,6 +1929,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_2_sdu_3_frag_4_pdu) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -1966,6 +1982,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_2_sdu_3_frag_4_pdu) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -2028,6 +2045,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_2_sdu_3_frag_4_pdu) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -2083,6 +2101,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_2_sdu_3_frag_4_pdu) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -2126,6 +2145,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_2_sdu_3_frag_4_pdu) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -2188,6 +2208,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_2_sdu_3_frag_4_pdu) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -2264,12 +2285,12 @@ ZTEST(test_tx_unframed, test_tx_unframed_2_sdu_3_frag_4_pdu_padding) /* Settings */ role = ISOAL_ROLE_PERIPHERAL; iso_interval_int = 2; - sdu_interval = CONN_INT_UNIT_US; + sdu_interval = ISO_INT_UNIT_US; max_octets = TEST_TX_PDU_PAYLOAD_MAX + 5; BN = 8; FT = 1; - stream_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 200; - group_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 50; + stream_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 200; + group_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 50; /* SDU 1 Frag 1 ------------------------------------------------------*/ isoal_test_init_tx_pdu_buffer(&tx_pdu_meta_buf[0]); @@ -2289,7 +2310,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_2_sdu_3_frag_4_pdu_padding) sdu_packet_number = 2000; event_number = 2000; sdu_timestamp = 9249; - ref_point = sdu_timestamp + (iso_interval_int * CONN_INT_UNIT_US) - 50; + ref_point = sdu_timestamp + (iso_interval_int * ISO_INT_UNIT_US) - 50; sdu_total_size = TEST_TX_PDU_PAYLOAD_MAX * 2; testdata_indx = 0; testdata_size = (TEST_TX_PDU_PAYLOAD_MAX * 2) / 3; @@ -2312,6 +2333,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_2_sdu_3_frag_4_pdu_padding) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -2368,6 +2390,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_2_sdu_3_frag_4_pdu_padding) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -2430,6 +2453,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_2_sdu_3_frag_4_pdu_padding) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -2525,6 +2549,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_2_sdu_3_frag_4_pdu_padding) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -2567,6 +2592,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_2_sdu_3_frag_4_pdu_padding) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -2629,6 +2655,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_2_sdu_3_frag_4_pdu_padding) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -2739,12 +2766,12 @@ ZTEST(test_tx_unframed, test_tx_unframed_1_zero_sdu_1_frag_1_pdu_maxPDU_padding) /* Settings */ role = ISOAL_ROLE_PERIPHERAL; iso_interval_int = 1; - sdu_interval = CONN_INT_UNIT_US; + sdu_interval = ISO_INT_UNIT_US; max_octets = TEST_TX_PDU_PAYLOAD_MAX - 5; BN = 3; FT = 1; - stream_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 200; - group_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 50; + stream_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 200; + group_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 50; /* SDU 1 Frag 1 ------------------------------------------------------*/ isoal_test_init_tx_pdu_buffer(&tx_pdu_meta_buf[0]); @@ -2764,7 +2791,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_1_zero_sdu_1_frag_1_pdu_maxPDU_padding) sdu_packet_number = 2000; event_number = 2000; sdu_timestamp = 9249; - ref_point = sdu_timestamp + (iso_interval_int * CONN_INT_UNIT_US) - 50; + ref_point = sdu_timestamp + (iso_interval_int * ISO_INT_UNIT_US) - 50; sdu_total_size = 0; testdata_indx = 0; testdata_size = 0; @@ -2790,6 +2817,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_1_zero_sdu_1_frag_1_pdu_maxPDU_padding) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -2907,12 +2935,12 @@ ZTEST(test_tx_unframed, test_tx_unframed_1_sdu_1_frag_pdu_alloc_err) /* Settings */ role = ISOAL_ROLE_PERIPHERAL; iso_interval_int = 1; - sdu_interval = CONN_INT_UNIT_US; + sdu_interval = ISO_INT_UNIT_US; max_octets = TEST_TX_PDU_PAYLOAD_MAX - 5; BN = 1; FT = 1; - stream_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 200; - group_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 50; + stream_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 200; + group_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 50; /* SDU Frag 1 --------------------------------------------------------*/ isoal_test_init_tx_pdu_buffer(&tx_pdu_meta_buf); @@ -2924,7 +2952,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_1_sdu_1_frag_pdu_alloc_err) sdu_packet_number = 2000; event_number = 2000; sdu_timestamp = 9249; - ref_point = sdu_timestamp + (iso_interval_int * CONN_INT_UNIT_US) - 50; + ref_point = sdu_timestamp + (iso_interval_int * ISO_INT_UNIT_US) - 50; sdu_total_size = TEST_TX_PDU_PAYLOAD_MAX - 5; testdata_indx = 0; testdata_size = TEST_TX_PDU_PAYLOAD_MAX - 5; @@ -2950,6 +2978,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_1_sdu_1_frag_pdu_alloc_err) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -3024,12 +3053,12 @@ ZTEST(test_tx_unframed, test_tx_unframed_1_sdu_1_frag_pdu_emit_err) /* Settings */ role = ISOAL_ROLE_PERIPHERAL; iso_interval_int = 1; - sdu_interval = CONN_INT_UNIT_US; + sdu_interval = ISO_INT_UNIT_US; max_octets = TEST_TX_PDU_PAYLOAD_MAX - 5; BN = 1; FT = 1; - stream_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 200; - group_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 50; + stream_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 200; + group_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 50; /* SDU Frag 1 --------------------------------------------------------*/ isoal_test_init_tx_pdu_buffer(&tx_pdu_meta_buf); @@ -3041,7 +3070,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_1_sdu_1_frag_pdu_emit_err) sdu_packet_number = 2000; event_number = 2000; sdu_timestamp = 9249; - ref_point = sdu_timestamp + (iso_interval_int * CONN_INT_UNIT_US) - 50; + ref_point = sdu_timestamp + (iso_interval_int * ISO_INT_UNIT_US) - 50; sdu_total_size = TEST_TX_PDU_PAYLOAD_MAX - 5; testdata_indx = 0; testdata_size = TEST_TX_PDU_PAYLOAD_MAX - 5; @@ -3067,6 +3096,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_1_sdu_1_frag_pdu_emit_err) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -3153,12 +3183,12 @@ ZTEST(test_tx_unframed, test_tx_unframed_4_sdu_1_frag_4_pdu_stream_loc) /* Settings */ role = ISOAL_ROLE_PERIPHERAL; iso_interval_int = 1; - sdu_interval = CONN_INT_UNIT_US / 2; + sdu_interval = ISO_INT_UNIT_US / 2; max_octets = TEST_TX_PDU_PAYLOAD_MAX - 5; BN = 2; FT = 1; - stream_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 200; - group_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 50; + stream_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 200; + group_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 50; /* SDU Frag 1 --------------------------------------------------------*/ /* Sets initial fragmentation status */ @@ -3171,7 +3201,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_4_sdu_1_frag_4_pdu_stream_loc) event_number = 2000; sdu_packet_number = (event_number * BN); sdu_timestamp = 9249; - ref_point = sdu_timestamp + (iso_interval_int * CONN_INT_UNIT_US) - 50; + ref_point = sdu_timestamp + (iso_interval_int * ISO_INT_UNIT_US) - 50; sdu_total_size = 23; testdata_indx = 0; testdata_size = 23; @@ -3197,6 +3227,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_4_sdu_1_frag_4_pdu_stream_loc) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -3253,9 +3284,9 @@ ZTEST(test_tx_unframed, test_tx_unframed_4_sdu_1_frag_4_pdu_stream_loc) isoal_test_init_tx_pdu_buffer(&tx_pdu_meta_buf); isoal_test_init_tx_sdu_buffer(&tx_sdu_frag_buf); sdu_packet_number += 29; - sdu_timestamp += ((iso_interval_int * CONN_INT_UNIT_US) * 15) - sdu_interval; + sdu_timestamp += ((iso_interval_int * ISO_INT_UNIT_US) * 15) - sdu_interval; event_number += 15; - ref_point += (iso_interval_int * CONN_INT_UNIT_US) * 15; + ref_point += (iso_interval_int * ISO_INT_UNIT_US) * 15; sdu_total_size = 10; testdata_indx = testdata_size; testdata_size += 10; @@ -3267,6 +3298,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_4_sdu_1_frag_4_pdu_stream_loc) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -3303,7 +3335,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_4_sdu_1_frag_4_pdu_stream_loc) /* Check TX Sync info */ tx_sync_seq_expected += 29; - tx_sync_timestamp_expected = ref_point - (iso_interval_int * CONN_INT_UNIT_US); + tx_sync_timestamp_expected = ref_point - (iso_interval_int * ISO_INT_UNIT_US); tx_sync_offset_expected = 0; err = isoal_tx_get_sync_info(source_hdl, &tx_sync_seq, &tx_sync_timestamp, &tx_sync_offset); @@ -3319,9 +3351,9 @@ ZTEST(test_tx_unframed, test_tx_unframed_4_sdu_1_frag_4_pdu_stream_loc) isoal_test_init_tx_sdu_buffer(&tx_sdu_frag_buf); /* Same SDU packet sequence number for testing */ /* Time stamp just before the exact multiple of the SDU interval */ - sdu_timestamp += ((iso_interval_int * CONN_INT_UNIT_US) * 15) - 1; + sdu_timestamp += ((iso_interval_int * ISO_INT_UNIT_US) * 15) - 1; event_number += 15; - ref_point += (iso_interval_int * CONN_INT_UNIT_US) * 15; + ref_point += (iso_interval_int * ISO_INT_UNIT_US) * 15; sdu_total_size = 10; testdata_indx = testdata_size; testdata_size += 10; @@ -3333,6 +3365,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_4_sdu_1_frag_4_pdu_stream_loc) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -3369,7 +3402,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_4_sdu_1_frag_4_pdu_stream_loc) /* Check TX Sync info */ tx_sync_seq_expected += 30; - tx_sync_timestamp_expected = ref_point - (iso_interval_int * CONN_INT_UNIT_US); + tx_sync_timestamp_expected = ref_point - (iso_interval_int * ISO_INT_UNIT_US); tx_sync_offset_expected = 0; err = isoal_tx_get_sync_info(source_hdl, &tx_sync_seq, &tx_sync_timestamp, &tx_sync_offset); @@ -3388,9 +3421,9 @@ ZTEST(test_tx_unframed, test_tx_unframed_4_sdu_1_frag_4_pdu_stream_loc) * +1 (reset to exact multiple of SDU interval from the last SDU) * +1 (push the time stamp 1us beyond the multiple mark) */ - sdu_timestamp += ((iso_interval_int * CONN_INT_UNIT_US) * 15) + 1 + 1; + sdu_timestamp += ((iso_interval_int * ISO_INT_UNIT_US) * 15) + 1 + 1; event_number += 15; - ref_point += (iso_interval_int * CONN_INT_UNIT_US) * 15; + ref_point += (iso_interval_int * ISO_INT_UNIT_US) * 15; sdu_total_size = 10; testdata_indx = testdata_size; testdata_size += 10; @@ -3402,6 +3435,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_4_sdu_1_frag_4_pdu_stream_loc) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -3438,7 +3472,7 @@ ZTEST(test_tx_unframed, test_tx_unframed_4_sdu_1_frag_4_pdu_stream_loc) /* Check TX Sync info */ tx_sync_seq_expected += 30; - tx_sync_timestamp_expected = ref_point - (iso_interval_int * CONN_INT_UNIT_US); + tx_sync_timestamp_expected = ref_point - (iso_interval_int * ISO_INT_UNIT_US); tx_sync_offset_expected = 0; err = isoal_tx_get_sync_info(source_hdl, &tx_sync_seq, &tx_sync_timestamp, &tx_sync_offset); @@ -3449,6 +3483,470 @@ ZTEST(test_tx_unframed, test_tx_unframed_4_sdu_1_frag_4_pdu_stream_loc) zassert_equal(tx_sync_offset, tx_sync_offset_expected, "%u != %u", tx_sync_seq, 0); } +/** + * Test Suite : TX framed SDU segmentation + * + * Tests framed event selection + */ +#define RUN_TX_FRAMED_FIND_CORRECT_TX_EVENT() \ + out_sdus_skipped = isoal_tx_framed_find_correct_tx_event(source, \ + &tx_sdu_frag_buf.sdu_tx, \ + &out_payload_number, \ + &out_ref_point, \ + &out_time_offset); \ + \ + zassert_equal(out_payload_number, expect_payload_number, "%llu != %llu", \ + out_payload_number, expect_payload_number); \ + zassert_equal(out_ref_point, expect_ref_point, "%u != %u", \ + out_ref_point, expect_ref_point); \ + zassert_equal(out_time_offset, expect_time_offset, "%u != %u", \ + out_time_offset, expect_time_offset); \ + zassert_equal(out_sdus_skipped, expect_sdus_skipped, "%u .!= %u", \ + out_sdus_skipped, expect_sdus_skipped) + +ZTEST(test_tx_framed, test_tx_framed_find_correct_tx_event) +{ + const uint8_t number_of_pdus = 1; + const uint8_t testdata_size_max = MAX_FRAMED_PDU_PAYLOAD(number_of_pdus); + + struct tx_sdu_frag_buffer tx_sdu_frag_buf; + struct isoal_source_session *session; + uint8_t testdata[testdata_size_max]; + isoal_sdu_len_t in_sdu_total_size; + isoal_source_handle_t source_hdl; + struct isoal_pdu_production *pp; + uint64_t expect_payload_number; + struct isoal_source *source; + uint64_t out_payload_number; + uint32_t expect_time_offset; + uint8_t expect_sdus_skipped; + uint32_t expected_timestamp; + uint32_t stream_sync_delay; + uint32_t in_cntr_timestamp; + uint32_t group_sync_delay; + uint64_t in_sdu_packet_sn; + uint32_t in_sdu_timestamp; + uint32_t expect_ref_point; + uint64_t in_target_event; + uint32_t iso_interval_us; + uint8_t iso_interval_int; + uint32_t out_time_offset; + uint8_t out_sdus_skipped; + uint16_t testdata_indx; + uint16_t testdata_size; + uint32_t out_ref_point; + uint32_t sdu_interval; + uint32_t in_ref_point; + uint8_t max_octets; + uint8_t role; + uint8_t BN; + uint8_t FT; + + /* Settings */ + role = BT_CONN_ROLE_PERIPHERAL; + iso_interval_int = 1; + iso_interval_us = iso_interval_int * ISO_INT_UNIT_US; + sdu_interval = iso_interval_us + 50; + max_octets = TEST_TX_PDU_PAYLOAD_MAX - 5; + BN = 2; + FT = 1; + stream_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 200; + group_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 50; + + init_test_data_buffer(testdata, testdata_size_max); + + /* Create source */ + source_hdl = basic_tx_test_setup(0xADAD, /* Handle */ + role, /* Role */ + true, /* Framed */ + BN, /* BN */ + FT, /* FT */ + max_octets, /* max_octets */ + sdu_interval, /* SDU Interval */ + iso_interval_int, /* ISO Interval */ + stream_sync_delay, /* Stream Sync Delay */ + group_sync_delay); /* Group Sync Delay */ + + source = &isoal_global.source_state[source_hdl]; + session = &source->session; + pp = &source->pdu_production; + + in_sdu_total_size = testdata_size_max; + testdata_indx = 0; + testdata_size = testdata_size_max; + + /* Test : Selection of event for first SDU where + * -- Last SDU packet number is uninitialized + * -- Last SDU time stamp is uninitialized + * -- Payload number is uninitialized + * -- Target event and reference point are one event ahead + * -- Time stamp is valid + * -- Time stamp indicates that target event is feasible + * Expected: + * -- Target event is used for transmission and calculations are based + * on that + * -- Time offset is based on the SDUs time stamp + */ + in_sdu_packet_sn = 2000; + in_target_event = 2000; + in_sdu_timestamp = 9249; + in_cntr_timestamp = in_sdu_timestamp + 200; + in_ref_point = in_sdu_timestamp + iso_interval_us - 50; + + pp->initialized = 0U; + session->tx_time_stamp = 0; + session->tx_time_offset = 0; + session->last_input_sn = 0; + session->last_input_time_stamp = 0; + pp->payload_number = 0; + + expect_sdus_skipped = 0; + expect_payload_number = in_target_event * BN; + expect_ref_point = in_ref_point; + expected_timestamp = in_sdu_timestamp; + expect_time_offset = expect_ref_point - expected_timestamp; + + isoal_test_init_tx_sdu_buffer(&tx_sdu_frag_buf); + isoal_test_create_sdu_fagment(BT_ISO_SINGLE, + &testdata[testdata_indx], + (testdata_size - testdata_indx), + in_sdu_total_size, + in_sdu_packet_sn, + in_sdu_timestamp, + in_cntr_timestamp, + in_ref_point, + in_target_event, + &tx_sdu_frag_buf.sdu_tx); + + RUN_TX_FRAMED_FIND_CORRECT_TX_EVENT(); + + /* Test : Selection of event for first SDU where + * -- Last SDU packet number is uninitialized + * -- Last SDU time stamp is uninitialized + * -- Payload number ahead of target event + * -- Target event and reference point are one event behind + * current payload + * -- Time stamp is valid + * -- Time stamp indicates that target event is feasible + * Expected: + * -- Target event + 1 is selected based on the payload being ahead and + * calculations are based on that reference + * -- Time offset is based on the SDUs time stamp + */ + in_sdu_packet_sn = 2000; + in_target_event = 2000; + in_sdu_timestamp = 9249; + in_cntr_timestamp = in_sdu_timestamp + 200; + in_ref_point = in_sdu_timestamp + iso_interval_us - 50; + + pp->initialized = 0U; + session->tx_time_stamp = 0; + session->tx_time_offset = 0; + session->last_input_sn = 0; + session->last_input_time_stamp = 0; + pp->payload_number = (in_target_event + 1) * BN; + + expect_sdus_skipped = 0; + expect_payload_number = (in_target_event + 1) * BN; + expect_ref_point = in_ref_point + iso_interval_us; + expected_timestamp = in_sdu_timestamp; + expect_time_offset = expect_ref_point - expected_timestamp; + + isoal_test_init_tx_sdu_buffer(&tx_sdu_frag_buf); + isoal_test_create_sdu_fagment(BT_ISO_SINGLE, + &testdata[testdata_indx], + (testdata_size - testdata_indx), + in_sdu_total_size, + in_sdu_packet_sn, + in_sdu_timestamp, + in_cntr_timestamp, + in_ref_point, + in_target_event, + &tx_sdu_frag_buf.sdu_tx); + + RUN_TX_FRAMED_FIND_CORRECT_TX_EVENT(); + + /* Test : Selection of event for first SDU where + * -- Last SDU packet number is uninitialized + * -- Last SDU time stamp is uninitialized + * -- Payload number ahead of target event + * -- Target event and reference point are one event behind + * current payload + * -- Time stamp is invalid + * -- Controller time stamp indicates that target event is + * feasible + * Expected: + * -- Target event + 1 is selected based on the payload being ahead and + * calculations are based on that reference + * -- Time offset is based on the controller's capture time + */ + in_sdu_packet_sn = 2000; + in_target_event = 2000; + in_sdu_timestamp = 0; + in_cntr_timestamp = 9249 + 200; + in_ref_point = in_cntr_timestamp + iso_interval_us - 50; + + pp->initialized = 0U; + session->tx_time_stamp = 0; + session->tx_time_offset = 0; + session->last_input_sn = 0; + session->last_input_time_stamp = 0; + pp->payload_number = (in_target_event + 1) * BN; + + expect_sdus_skipped = 0; + expect_payload_number = (in_target_event + 1) * BN; + expect_ref_point = in_ref_point + iso_interval_us; + expected_timestamp = in_cntr_timestamp; + expect_time_offset = expect_ref_point - expected_timestamp; + + isoal_test_init_tx_sdu_buffer(&tx_sdu_frag_buf); + isoal_test_create_sdu_fagment(BT_ISO_SINGLE, + &testdata[testdata_indx], + (testdata_size - testdata_indx), + in_sdu_total_size, + in_sdu_packet_sn, + in_sdu_timestamp, + in_cntr_timestamp, + in_ref_point, + in_target_event, + &tx_sdu_frag_buf.sdu_tx); + + RUN_TX_FRAMED_FIND_CORRECT_TX_EVENT(); + + /* Test : Selection of event for a subsequent SDU where + * -- Last SDU packet number is in sequence + * -- Last SDU time stamp is in sequence + * -- Payload number is in sequence + * -- Target event and reference point are one event ahead of + * current payload + * -- Time stamp is valid + * -- Time stamp indicates that target event is feasible + * Expected: + * -- Target event is selected based on the time stamp and calculations + * are based on that reference + * -- Time offset is based on the SDUs time stamp + */ + in_sdu_packet_sn = 2000; + in_target_event = 2000; + in_sdu_timestamp = 9249; + in_cntr_timestamp = 9249 + 200; + in_ref_point = in_sdu_timestamp + iso_interval_us - 50; + + pp->initialized = 1U; + session->tx_time_stamp = 0; + session->tx_time_offset = 0; + session->last_input_sn = in_sdu_packet_sn - 1; + session->last_input_time_stamp = in_sdu_timestamp - sdu_interval; + pp->payload_number = (in_target_event - 1) * BN; + + expect_sdus_skipped = 0; + expect_payload_number = in_target_event * BN; + expect_ref_point = in_ref_point; + expected_timestamp = in_sdu_timestamp; + expect_time_offset = expect_ref_point - expected_timestamp; + + isoal_test_init_tx_sdu_buffer(&tx_sdu_frag_buf); + isoal_test_create_sdu_fagment(BT_ISO_SINGLE, + &testdata[testdata_indx], + (testdata_size - testdata_indx), + in_sdu_total_size, + in_sdu_packet_sn, + in_sdu_timestamp, + in_cntr_timestamp, + in_ref_point, + in_target_event, + &tx_sdu_frag_buf.sdu_tx); + + RUN_TX_FRAMED_FIND_CORRECT_TX_EVENT(); + + /* Test : Selection of event for a subsequent SDU where + * -- Last SDU packet number is not in sequence + * -- Last SDU time stamp is not in sequence + * -- Payload number is not in sequence + * -- Target event and reference point are two events ahead + * -- Time stamp is valid but at the border of the range + * -- Time stamp indicates that target event - 1 is feasible + * Expected: + * -- Target event - 1 is selected based on the time stamp and + * calculations are based on that reference + * -- Time offset is based on the SDUs time stamp + */ + in_sdu_packet_sn = 2000; + in_target_event = 2001; + in_sdu_timestamp = 9249; + in_cntr_timestamp = 9249 + sdu_interval + iso_interval_us; + in_ref_point = in_sdu_timestamp + (iso_interval_us * 2) - 50; + + pp->initialized = 1U; + session->tx_time_stamp = 0; + session->tx_time_offset = 0; + session->last_input_sn = in_sdu_packet_sn - 3; + session->last_input_time_stamp = in_sdu_timestamp - (sdu_interval * 2); + pp->payload_number = (in_target_event - 2) * BN; + + expect_sdus_skipped = in_sdu_packet_sn - session->last_input_sn - 1; + expect_payload_number = (in_target_event - 1) * BN; + expect_ref_point = in_ref_point - iso_interval_us; + expected_timestamp = in_sdu_timestamp; + expect_time_offset = expect_ref_point - expected_timestamp; + + isoal_test_init_tx_sdu_buffer(&tx_sdu_frag_buf); + isoal_test_create_sdu_fagment(BT_ISO_SINGLE, + &testdata[testdata_indx], + (testdata_size - testdata_indx), + in_sdu_total_size, + in_sdu_packet_sn, + in_sdu_timestamp, + in_cntr_timestamp, + in_ref_point, + in_target_event, + &tx_sdu_frag_buf.sdu_tx); + + RUN_TX_FRAMED_FIND_CORRECT_TX_EVENT(); + + /* Test : Selection of event for a subsequent SDU where + * -- Last SDU packet number is not in sequence + * -- Last SDU time stamp is not in sequence + * -- Payload number is not in sequence + * -- Target event and reference point are two events ahead + * -- Time stamp is invalid + * Expected: + * -- Target event is selected based on the time stamp calculated + * from the difference between time stamps and calculations are based + * on that reference + * -- Time offset is based on the SDUs time stamp + */ + in_sdu_packet_sn = 2000; + in_target_event = 2001; + in_sdu_timestamp = 9249; + in_cntr_timestamp = 9249 + sdu_interval + iso_interval_us + 1; + in_ref_point = in_sdu_timestamp + (iso_interval_us * 2) - 50; + + pp->initialized = 1U; + session->tx_time_stamp = in_ref_point - iso_interval_us; + session->tx_time_offset = session->tx_time_stamp - + (in_sdu_timestamp - sdu_interval); + session->last_input_sn = in_sdu_packet_sn - 3; + session->last_input_time_stamp = in_sdu_timestamp - (sdu_interval * 2); + pp->payload_number = (in_target_event - 2) * BN; + + expect_sdus_skipped = in_sdu_packet_sn - session->last_input_sn - 1; + expect_payload_number = in_target_event * BN; + expect_ref_point = in_ref_point; + expected_timestamp = session->tx_time_stamp - session->tx_time_offset + + (in_sdu_timestamp - session->last_input_time_stamp); + expect_time_offset = expect_ref_point - expected_timestamp; + + isoal_test_init_tx_sdu_buffer(&tx_sdu_frag_buf); + isoal_test_create_sdu_fagment(BT_ISO_SINGLE, + &testdata[testdata_indx], + (testdata_size - testdata_indx), + in_sdu_total_size, + in_sdu_packet_sn, + in_sdu_timestamp, + in_cntr_timestamp, + in_ref_point, + in_target_event, + &tx_sdu_frag_buf.sdu_tx); + + RUN_TX_FRAMED_FIND_CORRECT_TX_EVENT(); + + /* Test : Selection of event for a subsequent SDU where + * -- Last SDU packet number is not in sequence + * -- Last SDU time stamp has been projected as part of a + * burst + * -- Payload number is not in sequence + * -- Target event and reference point are two events ahead + * -- Time stamp is invalid + * -- Time stamp delta is invalid + * Expected: + * -- Target event + 1 is selected based on the time stamp calculated + * from the difference in packet sn and calculations are based + * on that reference + * -- Time offset is based on the SDUs time stamp + */ + in_sdu_packet_sn = 2000; + in_target_event = 2001; + in_sdu_timestamp = 9249; + in_cntr_timestamp = 9249 + sdu_interval + iso_interval_us + 1; + in_ref_point = in_sdu_timestamp + (iso_interval_us * 2) - 50; + + pp->initialized = 1U; + session->tx_time_stamp = in_ref_point - iso_interval_us; + session->tx_time_offset = session->tx_time_stamp - + (in_sdu_timestamp + sdu_interval); + session->last_input_sn = in_sdu_packet_sn - 1; + session->last_input_time_stamp = in_sdu_timestamp + (sdu_interval * 2); + pp->payload_number = (in_target_event - 2) * BN; + + expect_sdus_skipped = in_sdu_packet_sn - session->last_input_sn - 1; + expect_payload_number = (in_target_event + 1) * BN; + expect_ref_point = in_ref_point + iso_interval_us; + expected_timestamp = session->tx_time_stamp - session->tx_time_offset + sdu_interval; + expect_time_offset = expect_ref_point - expected_timestamp; + + isoal_test_init_tx_sdu_buffer(&tx_sdu_frag_buf); + isoal_test_create_sdu_fagment(BT_ISO_SINGLE, + &testdata[testdata_indx], + (testdata_size - testdata_indx), + in_sdu_total_size, + in_sdu_packet_sn, + in_sdu_timestamp, + in_cntr_timestamp, + in_ref_point, + in_target_event, + &tx_sdu_frag_buf.sdu_tx); + + RUN_TX_FRAMED_FIND_CORRECT_TX_EVENT(); + + /* Test : Selection of event for a subsequent SDU where + * -- Last SDU packet number is in sequence + * -- Last SDU time stamp has been projected as part of a + * burst + * -- Payload number is ahead of selected event + * -- Target event and reference point are two events ahead + * -- Time stamp is valid + * -- Time stamp indicates that target event - 1 is feasible + * Expected: + * -- Target event -1 is selected based on the time stamp and + * calculations are based on that reference + * -- Payload number continues from last + * -- Time offset is based on the SDUs time stamp + */ + in_sdu_packet_sn = 2000; + in_target_event = 2001; + in_sdu_timestamp = 9249; + in_cntr_timestamp = 9249; + in_ref_point = in_sdu_timestamp + (iso_interval_us * 2) - 50; + + pp->initialized = 1U; + session->tx_time_stamp = 0; + session->tx_time_offset = 0; + session->last_input_sn = in_sdu_packet_sn - 1; + session->last_input_time_stamp = in_sdu_timestamp - sdu_interval; + pp->payload_number = ((in_target_event - 1) * BN) + 1; + + expect_sdus_skipped = in_sdu_packet_sn - session->last_input_sn - 1; + expect_payload_number = pp->payload_number; + expect_ref_point = in_ref_point - iso_interval_us; + expected_timestamp = in_sdu_timestamp; + expect_time_offset = expect_ref_point - expected_timestamp; + + isoal_test_init_tx_sdu_buffer(&tx_sdu_frag_buf); + isoal_test_create_sdu_fagment(BT_ISO_SINGLE, + &testdata[testdata_indx], + (testdata_size - testdata_indx), + in_sdu_total_size, + in_sdu_packet_sn, + in_sdu_timestamp, + in_cntr_timestamp, + in_ref_point, + in_target_event, + &tx_sdu_frag_buf.sdu_tx); + + RUN_TX_FRAMED_FIND_CORRECT_TX_EVENT(); +} + /** * Test Suite : TX framed SDU segmentation * @@ -3490,12 +3988,12 @@ ZTEST(test_tx_framed, test_tx_framed_1_sdu_1_frag_1_pdu_maxPDU) /* Settings */ role = ISOAL_ROLE_PERIPHERAL; iso_interval_int = 1; - sdu_interval = CONN_INT_UNIT_US + 50; + sdu_interval = ISO_INT_UNIT_US + 50; max_octets = TEST_TX_PDU_PAYLOAD_MAX - 5; BN = 1; FT = 1; - stream_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 200; - group_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 50; + stream_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 200; + group_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 50; /* SDU Frag 1 --------------------------------------------------------*/ isoal_test_init_tx_pdu_buffer(&tx_pdu_meta_buf); @@ -3510,7 +4008,7 @@ ZTEST(test_tx_framed, test_tx_framed_1_sdu_1_frag_1_pdu_maxPDU) sdu_packet_number = 2000; event_number = 2000; sdu_timestamp = 9249; - ref_point = sdu_timestamp + (iso_interval_int * CONN_INT_UNIT_US) - 50; + ref_point = sdu_timestamp + (iso_interval_int * ISO_INT_UNIT_US) - 50; sdu_total_size = TEST_TX_PDU_PAYLOAD_MAX - 5 - (PDU_ISO_SEG_HDR_SIZE + PDU_ISO_SEG_TIMEOFFSET_SIZE); testdata_indx = 0; @@ -3535,6 +4033,7 @@ ZTEST(test_tx_framed, test_tx_framed_1_sdu_1_frag_1_pdu_maxPDU) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -3645,12 +4144,12 @@ ZTEST(test_tx_framed, test_tx_framed_1_sdu_1_frag_1_pdu_bufSize) /* Settings */ role = ISOAL_ROLE_PERIPHERAL; iso_interval_int = 1; - sdu_interval = CONN_INT_UNIT_US + 50; + sdu_interval = ISO_INT_UNIT_US + 50; max_octets = TEST_TX_PDU_PAYLOAD_MAX + 5; BN = 1; FT = 1; - stream_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 200; - group_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 50; + stream_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 200; + group_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 50; /* SDU Frag 1 --------------------------------------------------------*/ isoal_test_init_tx_pdu_buffer(&tx_pdu_meta_buf); @@ -3665,7 +4164,7 @@ ZTEST(test_tx_framed, test_tx_framed_1_sdu_1_frag_1_pdu_bufSize) sdu_packet_number = 2000; event_number = 2000; sdu_timestamp = 9249; - ref_point = sdu_timestamp + (iso_interval_int * CONN_INT_UNIT_US) - 50; + ref_point = sdu_timestamp + (iso_interval_int * ISO_INT_UNIT_US) - 50; sdu_total_size = TEST_TX_PDU_PAYLOAD_MAX - (PDU_ISO_SEG_HDR_SIZE + PDU_ISO_SEG_TIMEOFFSET_SIZE); testdata_indx = 0; @@ -3690,6 +4189,7 @@ ZTEST(test_tx_framed, test_tx_framed_1_sdu_1_frag_1_pdu_bufSize) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -3755,7 +4255,8 @@ ZTEST(test_tx_framed, test_tx_framed_1_sdu_1_frag_1_pdu_bufSize) * Test Suite : TX framed SDU segmentation * * Tests segmentation of a single SDU contained in a single fragment - * into three PDUs where Max PDU is less than the PDU buffer size + * into three PDUs where Max PDU is less than the PDU buffer size. Also tests + * endianness of the segment header. */ ZTEST(test_tx_framed, test_tx_framed_1_sdu_1_frag_3_pdu) { @@ -3791,12 +4292,12 @@ ZTEST(test_tx_framed, test_tx_framed_1_sdu_1_frag_3_pdu) /* Settings */ role = ISOAL_ROLE_PERIPHERAL; iso_interval_int = 1; - sdu_interval = CONN_INT_UNIT_US + 50; + sdu_interval = ISO_INT_UNIT_US + 50; max_octets = TEST_TX_PDU_PAYLOAD_MAX - 5; BN = 3; FT = 1; - stream_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 200; - group_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 50; + stream_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 200; + group_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 50; /* SDU Frag 1 --------------------------------------------------------*/ isoal_test_init_tx_pdu_buffer(&tx_pdu_meta_buf); @@ -3810,7 +4311,7 @@ ZTEST(test_tx_framed, test_tx_framed_1_sdu_1_frag_3_pdu) sdu_packet_number = 2000; event_number = 2000; sdu_timestamp = 9249; - ref_point = sdu_timestamp + (iso_interval_int * CONN_INT_UNIT_US) - 50; + ref_point = sdu_timestamp + (iso_interval_int * ISO_INT_UNIT_US) - 50; sdu_total_size = 100 - ((3 * PDU_ISO_SEG_HDR_SIZE) + PDU_ISO_SEG_TIMEOFFSET_SIZE); testdata_indx = 0; testdata_size = 100 - ((3 * PDU_ISO_SEG_HDR_SIZE) + PDU_ISO_SEG_TIMEOFFSET_SIZE); @@ -3833,6 +4334,7 @@ ZTEST(test_tx_framed, test_tx_framed_1_sdu_1_frag_3_pdu) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -3852,10 +4354,11 @@ ZTEST(test_tx_framed, test_tx_framed_1_sdu_1_frag_3_pdu) /* Test segmentation (Black Box) */ /* Valid PDUs */ /* PDU 1 */ - seg_hdr[0].sc = 0; - seg_hdr[0].cmplt = 0; - seg_hdr[0].timeoffset = ref_point - sdu_timestamp; - seg_hdr[0].len = PDU_ISO_SEG_TIMEOFFSET_SIZE; + /* Test endianness */ + WRITE_BIT(((uint8_t *)&seg_hdr[0])[0], 0, 0); /* sc */ + WRITE_BIT(((uint8_t *)&seg_hdr[0])[0], 1, 0); /* cmplt */ + sys_put_le24(ref_point - sdu_timestamp, (uint8_t *)(&seg_hdr[0]) + PDU_ISO_SEG_HDR_SIZE); + ((uint8_t *)(&seg_hdr[0]))[1] = PDU_ISO_SEG_TIMEOFFSET_SIZE; /* len */ pdu_hdr_loc = 0; pdu_write_loc = PDU_ISO_SEG_HDR_SIZE + PDU_ISO_SEG_TIMEOFFSET_SIZE; sdu_read_loc = 0; @@ -3875,7 +4378,7 @@ ZTEST(test_tx_framed, test_tx_framed_1_sdu_1_frag_3_pdu) (pdu_write_size - pdu_write_loc)); seg_hdr[1] = seg_hdr[0]; - seg_hdr[1].len += (pdu_write_size - pdu_write_loc); + ((uint8_t *)(&seg_hdr[1]))[1] += (pdu_write_size - pdu_write_loc); ZASSERT_PDU_WRITE_TEST(history[2], pdu_buffer, @@ -3893,10 +4396,10 @@ ZTEST(test_tx_framed, test_tx_framed_1_sdu_1_frag_3_pdu) /* PDU 2 */ payload_number++; - seg_hdr[2].sc = 1; - seg_hdr[2].cmplt = 0; - seg_hdr[2].timeoffset = 0; - seg_hdr[2].len = 0; + WRITE_BIT(((uint8_t *)&seg_hdr[2])[0], 0, 1); /* sc */ + WRITE_BIT(((uint8_t *)&seg_hdr[2])[0], 1, 0); /* cmplt */ + sys_put_le24(0, (uint8_t *)(&seg_hdr[2]) + PDU_ISO_SEG_HDR_SIZE); + ((uint8_t *)(&seg_hdr[2]))[1] = 0; /* len */ pdu_hdr_loc = 0; sdu_read_loc += (pdu_write_size - pdu_write_loc); pdu_write_loc = PDU_ISO_SEG_HDR_SIZE; @@ -3916,7 +4419,7 @@ ZTEST(test_tx_framed, test_tx_framed_1_sdu_1_frag_3_pdu) (pdu_write_size - pdu_write_loc)); seg_hdr[3] = seg_hdr[2]; - seg_hdr[3].len += (pdu_write_size - pdu_write_loc); + ((uint8_t *)(&seg_hdr[3]))[1] += (pdu_write_size - pdu_write_loc); /* len */ ZASSERT_PDU_WRITE_TEST(history[5], pdu_buffer, @@ -3934,10 +4437,10 @@ ZTEST(test_tx_framed, test_tx_framed_1_sdu_1_frag_3_pdu) /* PDU 3 */ payload_number++; - seg_hdr[4].sc = 1; - seg_hdr[4].cmplt = 0; - seg_hdr[4].timeoffset = 0; - seg_hdr[4].len = 0; + WRITE_BIT(((uint8_t *)&seg_hdr[4])[0], 0, 1); /* sc */ + WRITE_BIT(((uint8_t *)&seg_hdr[4])[0], 1, 0); /* cmplt */ + sys_put_le24(0, (uint8_t *)(&seg_hdr[4]) + PDU_ISO_SEG_HDR_SIZE); + ((uint8_t *)(&seg_hdr[4]))[1] = 0; /* len */ pdu_hdr_loc = 0; sdu_read_loc += (pdu_write_size - pdu_write_loc); pdu_write_loc = PDU_ISO_SEG_HDR_SIZE; @@ -3960,8 +4463,8 @@ ZTEST(test_tx_framed, test_tx_framed_1_sdu_1_frag_3_pdu) (pdu_write_size - pdu_write_loc)); seg_hdr[5] = seg_hdr[4]; - seg_hdr[5].cmplt = 1; - seg_hdr[5].len += (pdu_write_size - pdu_write_loc); + WRITE_BIT(((uint8_t *)&seg_hdr[5])[0], 1, 1); /* cmplt */ + ((uint8_t *)(&seg_hdr[5]))[1] += (pdu_write_size - pdu_write_loc); /* len */ ZASSERT_PDU_WRITE_TEST(history[8], pdu_buffer, @@ -4022,12 +4525,12 @@ ZTEST(test_tx_framed, test_tx_framed_1_sdu_3_frag_1_pdu) /* Settings */ role = ISOAL_ROLE_PERIPHERAL; iso_interval_int = 1; - sdu_interval = CONN_INT_UNIT_US + 50; + sdu_interval = ISO_INT_UNIT_US + 50; max_octets = TEST_TX_PDU_PAYLOAD_MAX + 5; BN = 1; FT = 1; - stream_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 200; - group_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 50; + stream_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 200; + group_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 50; /* SDU Frag 1 --------------------------------------------------------*/ isoal_test_init_tx_pdu_buffer(&tx_pdu_meta_buf); @@ -4042,7 +4545,7 @@ ZTEST(test_tx_framed, test_tx_framed_1_sdu_3_frag_1_pdu) sdu_packet_number = 2000; event_number = 2000; sdu_timestamp = 9249; - ref_point = sdu_timestamp + (iso_interval_int * CONN_INT_UNIT_US) - 50; + ref_point = sdu_timestamp + (iso_interval_int * ISO_INT_UNIT_US) - 50; sdu_total_size = TEST_TX_PDU_PAYLOAD_MAX - (PDU_ISO_SEG_HDR_SIZE + PDU_ISO_SEG_TIMEOFFSET_SIZE); testdata_indx = 0; @@ -4069,6 +4572,7 @@ ZTEST(test_tx_framed, test_tx_framed_1_sdu_3_frag_1_pdu) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -4140,6 +4644,7 @@ ZTEST(test_tx_framed, test_tx_framed_1_sdu_3_frag_1_pdu) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -4194,6 +4699,7 @@ ZTEST(test_tx_framed, test_tx_framed_1_sdu_3_frag_1_pdu) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -4281,12 +4787,12 @@ ZTEST(test_tx_framed, test_tx_framed_1_sdu_3_frag_2_pdu) /* Settings */ role = ISOAL_ROLE_PERIPHERAL; iso_interval_int = 1; - sdu_interval = CONN_INT_UNIT_US + 50; + sdu_interval = ISO_INT_UNIT_US + 50; max_octets = TEST_TX_PDU_PAYLOAD_MAX + 5; BN = 2; FT = 1; - stream_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 200; - group_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 50; + stream_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 200; + group_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 50; /* SDU Frag 1 --------------------------------------------------------*/ isoal_test_init_tx_pdu_buffer(&tx_pdu_meta_buf[0]); @@ -4305,7 +4811,7 @@ ZTEST(test_tx_framed, test_tx_framed_1_sdu_3_frag_2_pdu) sdu_packet_number = 2000; event_number = 2000; sdu_timestamp = 9249; - ref_point = sdu_timestamp + (iso_interval_int * CONN_INT_UNIT_US) - 50; + ref_point = sdu_timestamp + (iso_interval_int * ISO_INT_UNIT_US) - 50; sdu_total_size = (TEST_TX_PDU_PAYLOAD_MAX * 2) - ((PDU_ISO_SEG_HDR_SIZE * 2) + PDU_ISO_SEG_TIMEOFFSET_SIZE); testdata_indx = 0; @@ -4331,6 +4837,7 @@ ZTEST(test_tx_framed, test_tx_framed_1_sdu_3_frag_2_pdu) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -4404,6 +4911,7 @@ ZTEST(test_tx_framed, test_tx_framed_1_sdu_3_frag_2_pdu) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -4496,6 +5004,7 @@ ZTEST(test_tx_framed, test_tx_framed_1_sdu_3_frag_2_pdu) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -4583,12 +5092,12 @@ ZTEST(test_tx_framed, test_tx_framed_2_sdu_3_frag_4_pdu) /* Settings */ role = ISOAL_ROLE_PERIPHERAL; iso_interval_int = 2; - sdu_interval = CONN_INT_UNIT_US + 50; + sdu_interval = ISO_INT_UNIT_US + 50; max_octets = TEST_TX_PDU_PAYLOAD_MAX + 5; BN = 4; FT = 1; - stream_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 200; - group_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 50; + stream_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 200; + group_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 50; /* SDU 1 Frag 1 ------------------------------------------------------*/ isoal_test_init_tx_pdu_buffer(&tx_pdu_meta_buf[0]); @@ -4607,7 +5116,7 @@ ZTEST(test_tx_framed, test_tx_framed_2_sdu_3_frag_4_pdu) sdu_packet_number = 2000; event_number = 2000; sdu_timestamp = 9249; - ref_point = 9249 + (iso_interval_int * CONN_INT_UNIT_US) - 50; + ref_point = 9249 + (iso_interval_int * ISO_INT_UNIT_US) - 50; sdu_total_size = (TEST_TX_PDU_PAYLOAD_MAX * 2) - ((PDU_ISO_SEG_HDR_SIZE * 2) + PDU_ISO_SEG_TIMEOFFSET_SIZE); testdata_indx = 0; @@ -4633,6 +5142,7 @@ ZTEST(test_tx_framed, test_tx_framed_2_sdu_3_frag_4_pdu) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -4708,6 +5218,7 @@ ZTEST(test_tx_framed, test_tx_framed_2_sdu_3_frag_4_pdu) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -4800,6 +5311,7 @@ ZTEST(test_tx_framed, test_tx_framed_2_sdu_3_frag_4_pdu) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -4851,7 +5363,7 @@ ZTEST(test_tx_framed, test_tx_framed_2_sdu_3_frag_4_pdu) sdu_packet_number++; event_number = 2000; sdu_timestamp = 9249 + sdu_interval; - ref_point = 9249 + (iso_interval_int * CONN_INT_UNIT_US) - 50; + ref_point = 9249 + (iso_interval_int * ISO_INT_UNIT_US) - 50; sdu_total_size = (TEST_TX_PDU_PAYLOAD_MAX * 2) - ((PDU_ISO_SEG_HDR_SIZE * 2) + PDU_ISO_SEG_TIMEOFFSET_SIZE); testdata_indx = 0; @@ -4866,6 +5378,7 @@ ZTEST(test_tx_framed, test_tx_framed_2_sdu_3_frag_4_pdu) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -4932,6 +5445,7 @@ ZTEST(test_tx_framed, test_tx_framed_2_sdu_3_frag_4_pdu) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -5023,6 +5537,7 @@ ZTEST(test_tx_framed, test_tx_framed_2_sdu_3_frag_4_pdu) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -5115,12 +5630,12 @@ ZTEST(test_tx_framed, test_tx_framed_2_sdu_3_frag_4_pdu_padding) /* Settings */ role = ISOAL_ROLE_PERIPHERAL; iso_interval_int = 2; - sdu_interval = CONN_INT_UNIT_US + 50; + sdu_interval = ISO_INT_UNIT_US + 50; max_octets = TEST_TX_PDU_PAYLOAD_MAX + 5; BN = 6; FT = 1; - stream_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 200; - group_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 50; + stream_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 200; + group_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 50; /* SDU 1 Frag 1 ------------------------------------------------------*/ isoal_test_init_tx_pdu_buffer(&tx_pdu_meta_buf[0]); @@ -5137,7 +5652,7 @@ ZTEST(test_tx_framed, test_tx_framed_2_sdu_3_frag_4_pdu_padding) sdu_packet_number = 2000; event_number = 2000; sdu_timestamp = 9249; - ref_point = 9249 + (iso_interval_int * CONN_INT_UNIT_US) - 50; + ref_point = 9249 + (iso_interval_int * ISO_INT_UNIT_US) - 50; sdu_total_size = testdata_size_max; testdata_indx = 0; testdata_size = testdata_size_max / number_of_sdu_frags; @@ -5160,6 +5675,7 @@ ZTEST(test_tx_framed, test_tx_framed_2_sdu_3_frag_4_pdu_padding) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -5232,6 +5748,7 @@ ZTEST(test_tx_framed, test_tx_framed_2_sdu_3_frag_4_pdu_padding) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -5323,6 +5840,7 @@ ZTEST(test_tx_framed, test_tx_framed_2_sdu_3_frag_4_pdu_padding) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -5374,7 +5892,7 @@ ZTEST(test_tx_framed, test_tx_framed_2_sdu_3_frag_4_pdu_padding) sdu_packet_number++; event_number = 2000; sdu_timestamp = 9249 + sdu_interval; - ref_point = 9249 + (iso_interval_int * CONN_INT_UNIT_US) - 50; + ref_point = 9249 + (iso_interval_int * ISO_INT_UNIT_US) - 50; sdu_total_size = testdata_size_max; testdata_indx = 0; testdata_size = testdata_size_max / number_of_sdu_frags; @@ -5386,6 +5904,7 @@ ZTEST(test_tx_framed, test_tx_framed_2_sdu_3_frag_4_pdu_padding) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -5447,6 +5966,7 @@ ZTEST(test_tx_framed, test_tx_framed_2_sdu_3_frag_4_pdu_padding) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -5537,6 +6057,7 @@ ZTEST(test_tx_framed, test_tx_framed_2_sdu_3_frag_4_pdu_padding) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -5661,12 +6182,12 @@ ZTEST(test_tx_framed, test_tx_framed_2_sdu_1_frag_2_pdu_refPoint2) /* Settings */ role = ISOAL_ROLE_PERIPHERAL; iso_interval_int = 1; - sdu_interval = CONN_INT_UNIT_US + 50; + sdu_interval = ISO_INT_UNIT_US + 50; max_octets = TEST_TX_PDU_PAYLOAD_MAX + 5; BN = 1; FT = 1; - stream_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 200; - group_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 50; + stream_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 200; + group_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 50; /* SDU 1 Frag 1 ------------------------------------------------------*/ isoal_test_init_tx_pdu_buffer(&tx_pdu_meta_buf); @@ -5682,7 +6203,7 @@ ZTEST(test_tx_framed, test_tx_framed_2_sdu_1_frag_2_pdu_refPoint2) sdu_packet_number = 2000; event_number = 2000; sdu_timestamp = 9249; - ref_point = sdu_timestamp + (iso_interval_int * CONN_INT_UNIT_US) - 50; + ref_point = sdu_timestamp + (iso_interval_int * ISO_INT_UNIT_US) - 50; sdu_total_size = TEST_TX_PDU_PAYLOAD_MAX - (PDU_ISO_SEG_HDR_SIZE + PDU_ISO_SEG_TIMEOFFSET_SIZE); testdata_indx = 0; @@ -5707,6 +6228,7 @@ ZTEST(test_tx_framed, test_tx_framed_2_sdu_1_frag_2_pdu_refPoint2) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -5787,6 +6309,7 @@ ZTEST(test_tx_framed, test_tx_framed_2_sdu_1_frag_2_pdu_refPoint2) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -5800,7 +6323,7 @@ ZTEST(test_tx_framed, test_tx_framed_2_sdu_1_frag_2_pdu_refPoint2) /* PDU 2 */ /* Advance the target event and the reference point to what it should be */ event_number++; - ref_point += iso_interval_int * CONN_INT_UNIT_US; + ref_point += iso_interval_int * ISO_INT_UNIT_US; payload_number++; seg_hdr[0].sc = 0; seg_hdr[0].cmplt = 0; @@ -5890,12 +6413,12 @@ ZTEST(test_tx_framed, test_tx_framed_1_sdu_1_frag_1_pdu_refPoint3) /* Settings */ role = ISOAL_ROLE_PERIPHERAL; iso_interval_int = 1; - sdu_interval = CONN_INT_UNIT_US + 50; + sdu_interval = ISO_INT_UNIT_US + 50; max_octets = TEST_TX_PDU_PAYLOAD_MAX + 5; BN = 1; FT = 1; - stream_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 200; - group_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 50; + stream_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 200; + group_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 50; /* SDU 1 Frag 1 ------------------------------------------------------*/ isoal_test_init_tx_pdu_buffer(&tx_pdu_meta_buf); @@ -5936,6 +6459,7 @@ ZTEST(test_tx_framed, test_tx_framed_1_sdu_1_frag_1_pdu_refPoint3) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -5955,7 +6479,7 @@ ZTEST(test_tx_framed, test_tx_framed_1_sdu_1_frag_1_pdu_refPoint3) /* PDU 1 */ /* Advance the target event and the reference point to what it should be */ event_number++; - ref_point += iso_interval_int * CONN_INT_UNIT_US; + ref_point += iso_interval_int * ISO_INT_UNIT_US; payload_number = event_number * BN; seg_hdr[0].sc = 0; seg_hdr[0].cmplt = 0; @@ -6046,12 +6570,12 @@ ZTEST(test_tx_framed, test_tx_framed_2_sdu_1_frag_2_pdu_ts_wrap1) /* Settings */ role = ISOAL_ROLE_PERIPHERAL; iso_interval_int = 1; - sdu_interval = CONN_INT_UNIT_US + 50; + sdu_interval = ISO_INT_UNIT_US + 50; max_octets = TEST_TX_PDU_PAYLOAD_MAX + 5; BN = 1; FT = 1; - stream_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 200; - group_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 50; + stream_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 200; + group_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 50; /* SDU 1 Frag 1 ------------------------------------------------------*/ isoal_test_init_tx_pdu_buffer(&tx_pdu_meta_buf); @@ -6092,6 +6616,7 @@ ZTEST(test_tx_framed, test_tx_framed_2_sdu_1_frag_2_pdu_ts_wrap1) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -6172,6 +6697,7 @@ ZTEST(test_tx_framed, test_tx_framed_2_sdu_1_frag_2_pdu_ts_wrap1) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -6185,7 +6711,7 @@ ZTEST(test_tx_framed, test_tx_framed_2_sdu_1_frag_2_pdu_ts_wrap1) /* PDU 2 */ /* Advance the target event and the reference point to what it should be */ event_number++; - ref_point += iso_interval_int * CONN_INT_UNIT_US; + ref_point += iso_interval_int * ISO_INT_UNIT_US; payload_number++; seg_hdr[0].sc = 0; seg_hdr[0].cmplt = 0; @@ -6270,12 +6796,12 @@ ZTEST(test_tx_framed, test_tx_framed_1_zero_sdu_1_frag_1_pdu_maxPDU) /* Settings */ role = ISOAL_ROLE_PERIPHERAL; iso_interval_int = 1; - sdu_interval = CONN_INT_UNIT_US + 50; + sdu_interval = ISO_INT_UNIT_US + 50; max_octets = TEST_TX_PDU_PAYLOAD_MAX - 5; BN = 1; FT = 1; - stream_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 200; - group_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 50; + stream_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 200; + group_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 50; /* SDU Frag 1 --------------------------------------------------------*/ isoal_test_init_tx_pdu_buffer(&tx_pdu_meta_buf); @@ -6288,7 +6814,7 @@ ZTEST(test_tx_framed, test_tx_framed_1_zero_sdu_1_frag_1_pdu_maxPDU) sdu_packet_number = 2000; event_number = 2000; sdu_timestamp = 9249; - ref_point = sdu_timestamp + (iso_interval_int * CONN_INT_UNIT_US) - 50; + ref_point = sdu_timestamp + (iso_interval_int * ISO_INT_UNIT_US) - 50; sdu_total_size = TEST_TX_PDU_PAYLOAD_MAX - 5 - (PDU_ISO_SEG_HDR_SIZE + PDU_ISO_SEG_TIMEOFFSET_SIZE); testdata_indx = 0; @@ -6312,6 +6838,7 @@ ZTEST(test_tx_framed, test_tx_framed_1_zero_sdu_1_frag_1_pdu_maxPDU) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -6413,12 +6940,12 @@ ZTEST(test_tx_framed, test_tx_framed_1_zero_sdu_1_frag_1_pdu_padding) /* Settings */ role = ISOAL_ROLE_PERIPHERAL; iso_interval_int = 1; - sdu_interval = CONN_INT_UNIT_US + 50; + sdu_interval = ISO_INT_UNIT_US + 50; max_octets = TEST_TX_PDU_PAYLOAD_MAX - 5; BN = 3; FT = 1; - stream_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 200; - group_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 50; + stream_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 200; + group_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 50; /* SDU Frag 1 --------------------------------------------------------*/ isoal_test_init_tx_pdu_buffer(&tx_pdu_meta_buf[0]); @@ -6439,7 +6966,7 @@ ZTEST(test_tx_framed, test_tx_framed_1_zero_sdu_1_frag_1_pdu_padding) sdu_packet_number = 2000; event_number = 2000; sdu_timestamp = 9249; - ref_point = sdu_timestamp + (iso_interval_int * CONN_INT_UNIT_US) - 50; + ref_point = sdu_timestamp + (iso_interval_int * ISO_INT_UNIT_US) - 50; sdu_total_size = TEST_TX_PDU_PAYLOAD_MAX - 5 - (PDU_ISO_SEG_HDR_SIZE + PDU_ISO_SEG_TIMEOFFSET_SIZE); testdata_indx = 0; @@ -6463,6 +6990,7 @@ ZTEST(test_tx_framed, test_tx_framed_1_zero_sdu_1_frag_1_pdu_padding) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -6598,12 +7126,12 @@ ZTEST(test_tx_framed, test_tx_framed_1_sdu_1_frag_pdu_alloc_err) /* Settings */ role = ISOAL_ROLE_PERIPHERAL; iso_interval_int = 1; - sdu_interval = CONN_INT_UNIT_US + 50; + sdu_interval = ISO_INT_UNIT_US + 50; max_octets = TEST_TX_PDU_PAYLOAD_MAX - 5; BN = 1; FT = 1; - stream_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 200; - group_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 50; + stream_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 200; + group_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 50; /* SDU Frag 1 --------------------------------------------------------*/ isoal_test_init_tx_pdu_buffer(&tx_pdu_meta_buf); @@ -6618,7 +7146,7 @@ ZTEST(test_tx_framed, test_tx_framed_1_sdu_1_frag_pdu_alloc_err) sdu_packet_number = 2000; event_number = 2000; sdu_timestamp = 9249; - ref_point = sdu_timestamp + (iso_interval_int * CONN_INT_UNIT_US) - 50; + ref_point = sdu_timestamp + (iso_interval_int * ISO_INT_UNIT_US) - 50; sdu_total_size = TEST_TX_PDU_PAYLOAD_MAX - 5 - (PDU_ISO_SEG_HDR_SIZE + PDU_ISO_SEG_TIMEOFFSET_SIZE); testdata_indx = 0; @@ -6643,6 +7171,7 @@ ZTEST(test_tx_framed, test_tx_framed_1_sdu_1_frag_pdu_alloc_err) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -6726,12 +7255,12 @@ ZTEST(test_tx_framed, test_tx_framed_1_sdu_1_frag_pdu_emit_err) /* Settings */ role = ISOAL_ROLE_PERIPHERAL; iso_interval_int = 1; - sdu_interval = CONN_INT_UNIT_US + 50; + sdu_interval = ISO_INT_UNIT_US + 50; max_octets = TEST_TX_PDU_PAYLOAD_MAX - 5; BN = 1; FT = 1; - stream_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 200; - group_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 50; + stream_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 200; + group_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 50; /* SDU Frag 1 --------------------------------------------------------*/ isoal_test_init_tx_pdu_buffer(&tx_pdu_meta_buf); @@ -6746,7 +7275,7 @@ ZTEST(test_tx_framed, test_tx_framed_1_sdu_1_frag_pdu_emit_err) sdu_packet_number = 2000; event_number = 2000; sdu_timestamp = 9249; - ref_point = sdu_timestamp + (iso_interval_int * CONN_INT_UNIT_US) - 50; + ref_point = sdu_timestamp + (iso_interval_int * ISO_INT_UNIT_US) - 50; sdu_total_size = TEST_TX_PDU_PAYLOAD_MAX - 5 - (PDU_ISO_SEG_HDR_SIZE + PDU_ISO_SEG_TIMEOFFSET_SIZE); testdata_indx = 0; @@ -6771,6 +7300,7 @@ ZTEST(test_tx_framed, test_tx_framed_1_sdu_1_frag_pdu_emit_err) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -6879,8 +7409,8 @@ ZTEST(test_tx_framed, test_tx_framed_2_sdu_1_frag_pdu_timeout) max_octets = TEST_TX_PDU_PAYLOAD_MAX + 5; BN = 1; FT = 1; - stream_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 200; - group_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 50; + stream_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 200; + group_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 50; /* SDU 1 Frag 1 ------------------------------------------------------*/ isoal_test_init_tx_pdu_buffer(&tx_pdu_meta_buf); @@ -6893,7 +7423,7 @@ ZTEST(test_tx_framed, test_tx_framed_2_sdu_1_frag_pdu_timeout) sdu_packet_number = 0; event_number = 2000; sdu_timestamp = 9249; - ref_point = sdu_timestamp + (iso_interval_int * CONN_INT_UNIT_US) - 50; + ref_point = sdu_timestamp + (iso_interval_int * ISO_INT_UNIT_US) - 50; sdu_total_size = TEST_TX_PDU_PAYLOAD_MAX - (PDU_ISO_SEG_HDR_SIZE + PDU_ISO_SEG_TIMEOFFSET_SIZE); testdata_indx = 0; @@ -6917,6 +7447,7 @@ ZTEST(test_tx_framed, test_tx_framed_2_sdu_1_frag_pdu_timeout) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -6993,6 +7524,7 @@ ZTEST(test_tx_framed, test_tx_framed_2_sdu_1_frag_pdu_timeout) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -7056,7 +7588,7 @@ ZTEST(test_tx_framed, test_tx_framed_2_sdu_1_frag_pdu_timeout) event_number++; sdu_packet_number++; sdu_timestamp = sdu_timestamp + sdu_interval; - ref_point = ref_point + (iso_interval_int * CONN_INT_UNIT_US); + ref_point = ref_point + (iso_interval_int * ISO_INT_UNIT_US); sdu_total_size = 20; testdata_indx = testdata_size; testdata_size += 20; @@ -7067,6 +7599,7 @@ ZTEST(test_tx_framed, test_tx_framed_2_sdu_1_frag_pdu_timeout) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -7130,26 +7663,35 @@ ZTEST(test_tx_framed, test_tx_framed_2_sdu_1_frag_pdu_timeout) } /** - * Test Suite : TX framed EBQ test IAL-CIS-FRA-PER-BV07C + * Test Suite : TX framed SDU segmentation * - * Tests packing multiple SDU segments into the same PDU and release on event - * timeout. + * Tests that consecutive events are used irrespective of the target event info + * as long as they are feasible. */ -ZTEST(test_tx_framed_ebq, test_tx_framed_cis_fra_per_bv07c) +ZTEST(test_tx_framed, test_tx_framed_event_utilization_1) { - uint8_t testdata[40]; - struct tx_pdu_meta_buffer tx_pdu_meta_buf; + const uint8_t number_of_pdus = 3; + const uint8_t sdu_fragment_data_size = 25; + const uint8_t testdata_size_max = sdu_fragment_data_size * 4; + /* Two SDUs and one that would overflow into a new PDU */ + const uint8_t number_of_seg_hdr_buf = 3; + + struct tx_pdu_meta_buffer tx_pdu_meta_buf[number_of_pdus]; + struct pdu_iso_sdu_sh seg_hdr[number_of_seg_hdr_buf]; + struct isoal_pdu_buffer pdu_buffer[number_of_pdus]; struct tx_sdu_frag_buffer tx_sdu_frag_buf; - struct isoal_pdu_buffer pdu_buffer; + uint8_t testdata[testdata_size_max]; isoal_source_handle_t source_hdl; - struct pdu_iso_sdu_sh seg_hdr[2]; isoal_sdu_len_t sdu_total_size; - isoal_pdu_len_t pdu_write_size; + isoal_pdu_len_t pdu_write_end; uint32_t stream_sync_delay; uint64_t sdu_packet_number; uint32_t group_sync_delay; - uint16_t iso_interval_int; + uint64_t pdu_event_number; + uint8_t iso_interval_int; + uint32_t iso_interval_us; uint64_t payload_number; + uint32_t pdu_ref_point; uint32_t sdu_timestamp; uint16_t testdata_indx; uint16_t testdata_size; @@ -7167,31 +7709,42 @@ ZTEST(test_tx_framed_ebq, test_tx_framed_cis_fra_per_bv07c) uint8_t FT; /* Settings */ - role = ISOAL_ROLE_PERIPHERAL; - iso_interval_int = 800; - sdu_interval = 500000; - max_octets = TEST_TX_PDU_PAYLOAD_MAX + 5; - BN = 1; + role = BT_CONN_ROLE_PERIPHERAL; + iso_interval_int = 1; + iso_interval_us = iso_interval_int * ISO_INT_UNIT_US; + sdu_interval = ISO_INT_UNIT_US - 50; /* Less than an ISO interval */ + max_octets = TEST_TX_PDU_PAYLOAD_MAX; + BN = 2; FT = 1; - stream_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 200; - group_sync_delay = (iso_interval_int * CONN_INT_UNIT_US) - 50; + stream_sync_delay = iso_interval_us - 200; + group_sync_delay = iso_interval_us - 50; - /* SDU 1 Frag 1 ------------------------------------------------------*/ - isoal_test_init_tx_pdu_buffer(&tx_pdu_meta_buf); + /* SDU 0 -------------------------------------------------------------*/ + isoal_test_init_tx_pdu_buffer(&tx_pdu_meta_buf[0]); + isoal_test_init_tx_pdu_buffer(&tx_pdu_meta_buf[1]); + isoal_test_init_tx_pdu_buffer(&tx_pdu_meta_buf[2]); isoal_test_init_tx_sdu_buffer(&tx_sdu_frag_buf); - init_test_data_buffer(testdata, 40); - (void)memset(seg_hdr, 0, sizeof(seg_hdr)); - pdu_buffer.handle = (void *)&tx_pdu_meta_buf.node_tx; - pdu_buffer.pdu = (struct pdu_iso *)tx_pdu_meta_buf.node_tx.pdu; - pdu_buffer.size = TEST_TX_PDU_PAYLOAD_MAX; + init_test_data_buffer(testdata, testdata_size_max); + (void)memset(&seg_hdr, 0, sizeof(seg_hdr)); + pdu_buffer[0].handle = (void *)&tx_pdu_meta_buf[0].node_tx; + pdu_buffer[0].pdu = (struct pdu_iso *)tx_pdu_meta_buf[0].node_tx.pdu; + pdu_buffer[0].size = TEST_TX_PDU_PAYLOAD_MAX; + pdu_buffer[1].handle = (void *)&tx_pdu_meta_buf[1].node_tx; + pdu_buffer[1].pdu = (struct pdu_iso *)tx_pdu_meta_buf[1].node_tx.pdu; + pdu_buffer[1].size = TEST_TX_PDU_PAYLOAD_MAX; + pdu_buffer[2].handle = (void *)&tx_pdu_meta_buf[2].node_tx; + pdu_buffer[2].pdu = (struct pdu_iso *)tx_pdu_meta_buf[2].node_tx.pdu; + pdu_buffer[2].size = TEST_TX_PDU_PAYLOAD_MAX; sdu_packet_number = 0; - event_number = 0; + event_number = 5; + pdu_event_number = event_number; sdu_timestamp = 9249; - ref_point = sdu_timestamp + (iso_interval_int * CONN_INT_UNIT_US) - 50; - sdu_total_size = 10; + ref_point = sdu_timestamp + iso_interval_us; + pdu_ref_point = ref_point; + sdu_total_size = sdu_fragment_data_size; testdata_indx = 0; - testdata_size = 10; - payload_number = event_number * BN; + testdata_size = sdu_fragment_data_size; + sdu_fragments = 0; source_hdl = basic_tx_test_setup(0xADAD, /* Handle */ role, /* Role */ @@ -7210,12 +7763,17 @@ ZTEST(test_tx_framed_ebq, test_tx_framed_cis_fra_per_bv07c) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); - SET_NEXT_PDU_ALLOC_BUFFER(&pdu_buffer); - SET_NEXT_PDU_ALLOC_BUFFER(&pdu_buffer); + SET_NEXT_PDU_ALLOC_BUFFER(&pdu_buffer[0]); + SET_NEXT_PDU_ALLOC_BUFFER(&pdu_buffer[1]); + SET_NEXT_PDU_ALLOC_BUFFER(&pdu_buffer[0]); + SET_NEXT_PDU_ALLOC_BUFFER(&pdu_buffer[1]); + SET_NEXT_PDU_ALLOC_BUFFER(&pdu_buffer[2]); + SET_NEXT_PDU_ALLOC_BUFFER(&pdu_buffer[0]); PDU_ALLOC_TEST_RETURNS(ISOAL_STATUS_OK); PDU_WRITE_TEST_RETURNS(ISOAL_STATUS_OK); PDU_EMIT_TEST_RETURNS(ISOAL_STATUS_OK); @@ -7227,31 +7785,493 @@ ZTEST(test_tx_framed_ebq, test_tx_framed_cis_fra_per_bv07c) /* Test segmentation (Black Box) */ /* Valid PDUs */ - /* PDU 1 */ + /* PDU 0 */ + payload_number = event_number * BN; seg_hdr[0].sc = 0; seg_hdr[0].cmplt = 0; - seg_hdr[0].timeoffset = ref_point - sdu_timestamp; + seg_hdr[0].timeoffset = pdu_ref_point - sdu_timestamp; seg_hdr[0].len = PDU_ISO_SEG_TIMEOFFSET_SIZE; pdu_hdr_loc = 0; pdu_write_loc = PDU_ISO_SEG_HDR_SIZE + PDU_ISO_SEG_TIMEOFFSET_SIZE; sdu_read_loc = 0; - pdu_write_size = pdu_write_loc + testdata_size; - sdu_fragments = 1; + pdu_write_end = sdu_fragment_data_size + pdu_write_loc; + sdu_fragments++; ZASSERT_PDU_WRITE_TEST(history[0], - pdu_buffer, + pdu_buffer[0], pdu_hdr_loc, &seg_hdr[0], (PDU_ISO_SEG_HDR_SIZE + PDU_ISO_SEG_TIMEOFFSET_SIZE)); ZASSERT_PDU_WRITE_TEST(history[1], - pdu_buffer, + pdu_buffer[0], pdu_write_loc, &testdata[sdu_read_loc], - (pdu_write_size - pdu_write_loc)); + (pdu_write_end - pdu_write_loc)); - seg_hdr[1] = seg_hdr[0]; - seg_hdr[1].cmplt = 1; + seg_hdr[0].cmplt = 1; + seg_hdr[0].len += (pdu_write_end - pdu_write_loc); + + ZASSERT_PDU_WRITE_TEST(history[2], + pdu_buffer[0], + pdu_hdr_loc, + &seg_hdr[0], + PDU_ISO_SEG_HDR_SIZE); + + /* PDU should not be emitted */ + ZASSERT_PDU_EMIT_TEST_CALL_COUNT(0); + + /* PDU release not expected (No Error) */ + ZASSERT_PDU_RELEASE_TEST_CALL_COUNT(0); + + /* SDU 1 -------------------------------------------------------------*/ + isoal_test_init_tx_sdu_buffer(&tx_sdu_frag_buf); + sdu_packet_number++; + event_number += 2; + ref_point += iso_interval_us * 2; + sdu_timestamp += sdu_interval; + testdata_indx = testdata_size; + testdata_size += sdu_fragment_data_size; + + isoal_test_create_sdu_fagment(BT_ISO_SINGLE, + &testdata[testdata_indx], + (testdata_size - testdata_indx), + sdu_total_size, + sdu_packet_number, + sdu_timestamp, + sdu_timestamp, + ref_point, + event_number, + &tx_sdu_frag_buf.sdu_tx); + + err = isoal_tx_sdu_fragment(source_hdl, &tx_sdu_frag_buf.sdu_tx); + + zassert_equal(err, ISOAL_STATUS_OK, "err = 0x%02x", err); + + /* Test segmentation (Black Box) */ + /* Valid PDUs */ + /* PDU 10 */ + pdu_hdr_loc = pdu_write_end; + seg_hdr[1].sc = 0; + seg_hdr[1].cmplt = 0; + seg_hdr[1].timeoffset = pdu_ref_point - sdu_timestamp; + seg_hdr[1].len = PDU_ISO_SEG_TIMEOFFSET_SIZE; + pdu_write_loc = pdu_write_end + (PDU_ISO_SEG_HDR_SIZE + PDU_ISO_SEG_TIMEOFFSET_SIZE); + pdu_write_end = TEST_TX_PDU_PAYLOAD_MAX; + sdu_read_loc = testdata_indx; + + ZASSERT_PDU_WRITE_TEST(history[3], + pdu_buffer[0], + pdu_hdr_loc, + &seg_hdr[1], + (PDU_ISO_SEG_HDR_SIZE + PDU_ISO_SEG_TIMEOFFSET_SIZE)); + + ZASSERT_PDU_WRITE_TEST(history[4], + pdu_buffer[0], + pdu_write_loc, + &testdata[sdu_read_loc], + (pdu_write_end - pdu_write_loc)); + + /* PDU should not be allocated */ + + seg_hdr[1].len += (pdu_write_end - pdu_write_loc); + + ZASSERT_PDU_WRITE_TEST(history[5], + pdu_buffer[0], + pdu_hdr_loc, + &seg_hdr[1], + PDU_ISO_SEG_HDR_SIZE); + + ZASSERT_PDU_EMIT_TEST(history[0], + &tx_pdu_meta_buf[0].node_tx, + payload_number, + sdu_fragments, + PDU_BIS_LLID_FRAMED, + pdu_write_end, + isoal_global.source_state[source_hdl].session.handle); + + /* PDU release not expected (No Error) */ + ZASSERT_PDU_RELEASE_TEST_CALL_COUNT(0); + + /* PDU 11 */ + payload_number++; + seg_hdr[2].sc = 1; + seg_hdr[2].cmplt = 0; + seg_hdr[2].timeoffset = 0; + seg_hdr[2].len = 0; + sdu_read_loc += (pdu_write_end - pdu_write_loc); + pdu_hdr_loc = 0; + pdu_write_end = testdata_size - testdata_indx - (pdu_write_end - pdu_write_loc) + + PDU_ISO_SEG_HDR_SIZE; + pdu_write_loc = PDU_ISO_SEG_HDR_SIZE; + sdu_fragments = 1; + + ZASSERT_PDU_WRITE_TEST(history[6], + pdu_buffer[1], + pdu_hdr_loc, + &seg_hdr[2], + PDU_ISO_SEG_HDR_SIZE); + + ZASSERT_PDU_WRITE_TEST(history[7], + pdu_buffer[1], + pdu_write_loc, + &testdata[sdu_read_loc], + (pdu_write_end - pdu_write_loc)); + + seg_hdr[2].cmplt = 1; + seg_hdr[2].len += (pdu_write_end - pdu_write_loc); + + ZASSERT_PDU_WRITE_TEST(history[8], + pdu_buffer[1], + pdu_hdr_loc, + &seg_hdr[2], + PDU_ISO_SEG_HDR_SIZE); + + /* PDU should not be emitted */ + ZASSERT_PDU_EMIT_TEST_CALL_COUNT(1); + + /* PDU release not expected (No Error) */ + ZASSERT_PDU_RELEASE_TEST_CALL_COUNT(0); + + /* SDU 2 -------------------------------------------------------------*/ + isoal_test_init_tx_pdu_buffer(&tx_pdu_meta_buf[0]); + isoal_test_init_tx_sdu_buffer(&tx_sdu_frag_buf); + sdu_packet_number++; + event_number += 2; + ref_point += iso_interval_us * 2; + sdu_timestamp += sdu_interval; + testdata_indx = testdata_size; + testdata_size += sdu_fragment_data_size; + + isoal_test_create_sdu_fagment(BT_ISO_SINGLE, + &testdata[testdata_indx], + (testdata_size - testdata_indx), + sdu_total_size, + sdu_packet_number, + sdu_timestamp, + sdu_timestamp, + ref_point, + event_number, + &tx_sdu_frag_buf.sdu_tx); + + err = isoal_tx_sdu_fragment(source_hdl, &tx_sdu_frag_buf.sdu_tx); + + zassert_equal(err, ISOAL_STATUS_OK, "err = 0x%02x", err); + + /* Test segmentation (Black Box) */ + /* Valid PDUs */ + /* PDU 11 */ + + ZASSERT_PDU_EMIT_TEST(history[1], + &tx_pdu_meta_buf[1].node_tx, + payload_number, + sdu_fragments, + PDU_BIS_LLID_FRAMED, + pdu_write_end, + isoal_global.source_state[source_hdl].session.handle); + + /* PDU release not expected (No Error) */ + ZASSERT_PDU_RELEASE_TEST_CALL_COUNT(0); + + /* PDU 12 */ + payload_number++; + pdu_event_number++; + pdu_ref_point += iso_interval_us; + seg_hdr[0].sc = 0; + seg_hdr[0].cmplt = 0; + seg_hdr[0].timeoffset = pdu_ref_point - sdu_timestamp; + seg_hdr[0].len = 3; + sdu_read_loc = testdata_indx; + pdu_hdr_loc = 0; + pdu_write_end = testdata_size - testdata_indx + PDU_ISO_SEG_HDR_SIZE + + PDU_ISO_SEG_TIMEOFFSET_SIZE; + pdu_write_loc = PDU_ISO_SEG_HDR_SIZE + PDU_ISO_SEG_TIMEOFFSET_SIZE; + sdu_fragments = 1; + + ZASSERT_PDU_WRITE_TEST(history[9], + pdu_buffer[0], + pdu_hdr_loc, + &seg_hdr[0], + (PDU_ISO_SEG_HDR_SIZE + PDU_ISO_SEG_TIMEOFFSET_SIZE)); + + ZASSERT_PDU_WRITE_TEST(history[10], + pdu_buffer[0], + pdu_write_loc, + &testdata[sdu_read_loc], + (pdu_write_end - pdu_write_loc)); + + seg_hdr[0].cmplt = 1; + seg_hdr[0].len += (pdu_write_end - pdu_write_loc); + + ZASSERT_PDU_WRITE_TEST(history[11], + pdu_buffer[0], + pdu_hdr_loc, + &seg_hdr[0], + PDU_ISO_SEG_HDR_SIZE); + + /* PDU should not be emitted */ + ZASSERT_PDU_EMIT_TEST_CALL_COUNT(2); + + /* PDU release not expected (No Error) */ + ZASSERT_PDU_RELEASE_TEST_CALL_COUNT(0); + + /* Send Event Timeout ----------------------------------------------- */ + isoal_tx_event_prepare(source_hdl, pdu_event_number - 1); + + /* PDU should not be emitted */ + ZASSERT_PDU_EMIT_TEST_CALL_COUNT(2); + + /* PDU release not expected (No Error) */ + ZASSERT_PDU_RELEASE_TEST_CALL_COUNT(0); + + /* SDU 3 -------------------------------------------------------------*/ + isoal_test_init_tx_pdu_buffer(&tx_pdu_meta_buf[1]); + isoal_test_init_tx_pdu_buffer(&tx_pdu_meta_buf[2]); + isoal_test_init_tx_sdu_buffer(&tx_sdu_frag_buf); + sdu_packet_number++; + event_number += 2; + ref_point += iso_interval_us * 2; + sdu_timestamp += sdu_interval; + sdu_total_size = sdu_fragment_data_size; + testdata_indx = testdata_size; + testdata_size += sdu_fragment_data_size; + + isoal_test_create_sdu_fagment(BT_ISO_SINGLE, + &testdata[testdata_indx], + (testdata_size - testdata_indx), + sdu_total_size, + sdu_packet_number, + sdu_timestamp, + sdu_timestamp, + ref_point, + event_number, + &tx_sdu_frag_buf.sdu_tx); + + err = isoal_tx_sdu_fragment(source_hdl, &tx_sdu_frag_buf.sdu_tx); + + zassert_equal(err, ISOAL_STATUS_OK, "err = 0x%02x", err); + + /* Test segmentation (Black Box) */ + /* Valid PDUs */ + /* PDU 12 */ + ZASSERT_PDU_EMIT_TEST(history[2], + &tx_pdu_meta_buf[0].node_tx, + payload_number, + sdu_fragments, + PDU_BIS_LLID_FRAMED, + pdu_write_end, + isoal_global.source_state[source_hdl].session.handle); + + /* PDU 13 */ + payload_number++; + + /* Padding PDU */ + ZASSERT_PDU_EMIT_TEST_CALL_COUNT(4); + ZASSERT_PDU_EMIT_TEST(history[3], + &tx_pdu_meta_buf[1].node_tx, + payload_number, + 0, + PDU_BIS_LLID_FRAMED, + 0, + isoal_global.source_state[source_hdl].session.handle); + + /* PDU release not expected (No Error) */ + ZASSERT_PDU_RELEASE_TEST_CALL_COUNT(0); + + /* PDU 14 */ + payload_number++; + pdu_event_number++; + pdu_ref_point += iso_interval_us; + seg_hdr[1].sc = 0; + seg_hdr[1].cmplt = 0; + seg_hdr[1].timeoffset = pdu_ref_point - sdu_timestamp; + seg_hdr[1].len = 3; + sdu_read_loc = testdata_indx; + pdu_hdr_loc = 0; + pdu_write_end = testdata_size - testdata_indx + PDU_ISO_SEG_HDR_SIZE + + PDU_ISO_SEG_TIMEOFFSET_SIZE; + pdu_write_loc = PDU_ISO_SEG_HDR_SIZE + PDU_ISO_SEG_TIMEOFFSET_SIZE; + sdu_fragments = 1; + + ZASSERT_PDU_WRITE_TEST(history[12], + pdu_buffer[2], + pdu_hdr_loc, + &seg_hdr[1], + PDU_ISO_SEG_HDR_SIZE + PDU_ISO_SEG_TIMEOFFSET_SIZE); + + ZASSERT_PDU_WRITE_TEST(history[13], + pdu_buffer[2], + pdu_write_loc, + &testdata[sdu_read_loc], + (pdu_write_end - pdu_write_loc)); + + seg_hdr[1].cmplt = 1; + seg_hdr[1].len += (pdu_write_end - pdu_write_loc); + + ZASSERT_PDU_WRITE_TEST(history[14], + pdu_buffer[2], + pdu_hdr_loc, + &seg_hdr[1], + PDU_ISO_SEG_HDR_SIZE); + + /* PDU should not be emitted */ + ZASSERT_PDU_EMIT_TEST_CALL_COUNT(4); + + /* PDU release not expected (No Error) */ + ZASSERT_PDU_RELEASE_TEST_CALL_COUNT(0); + + /* Send Event Timeout ----------------------------------------------- */ + isoal_tx_event_prepare(source_hdl, pdu_event_number); + + + ZASSERT_PDU_EMIT_TEST(history[4], + &tx_pdu_meta_buf[2].node_tx, + payload_number, + sdu_fragments, + PDU_BIS_LLID_FRAMED, + pdu_write_end, + isoal_global.source_state[source_hdl].session.handle); + + /* PDU 5 */ + payload_number++; + + /* Padding PDU */ + ZASSERT_PDU_EMIT_TEST(history[5], + &tx_pdu_meta_buf[0].node_tx, + payload_number, + 0, + PDU_BIS_LLID_FRAMED, + 0, + isoal_global.source_state[source_hdl].session.handle); + + /* PDU release not expected (No Error) */ + ZASSERT_PDU_RELEASE_TEST_CALL_COUNT(0); +} + +/** + * Test Suite : TX framed EBQ test IAL-CIS-FRA-PER-BV07C + * + * Tests packing multiple SDU segments into the same PDU and release on event + * timeout. + */ +ZTEST(test_tx_framed_ebq, test_tx_framed_cis_fra_per_bv07c) +{ + uint8_t testdata[40]; + struct tx_pdu_meta_buffer tx_pdu_meta_buf; + struct tx_sdu_frag_buffer tx_sdu_frag_buf; + struct isoal_pdu_buffer pdu_buffer; + isoal_source_handle_t source_hdl; + struct pdu_iso_sdu_sh seg_hdr[2]; + isoal_sdu_len_t sdu_total_size; + isoal_pdu_len_t pdu_write_size; + uint32_t stream_sync_delay; + uint64_t sdu_packet_number; + uint32_t group_sync_delay; + uint16_t iso_interval_int; + uint64_t payload_number; + uint32_t sdu_timestamp; + uint16_t testdata_indx; + uint16_t testdata_size; + uint16_t pdu_write_loc; + uint16_t sdu_read_loc; + uint64_t event_number; + uint32_t sdu_interval; + uint8_t sdu_fragments; + uint16_t pdu_hdr_loc; + uint32_t ref_point; + isoal_status_t err; + uint8_t max_octets; + uint8_t role; + uint8_t BN; + uint8_t FT; + + /* Settings */ + role = ISOAL_ROLE_PERIPHERAL; + iso_interval_int = 800; + sdu_interval = 500000; + max_octets = TEST_TX_PDU_PAYLOAD_MAX + 5; + BN = 1; + FT = 1; + stream_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 200; + group_sync_delay = (iso_interval_int * ISO_INT_UNIT_US) - 50; + + /* SDU 1 Frag 1 ------------------------------------------------------*/ + isoal_test_init_tx_pdu_buffer(&tx_pdu_meta_buf); + isoal_test_init_tx_sdu_buffer(&tx_sdu_frag_buf); + init_test_data_buffer(testdata, 40); + (void)memset(seg_hdr, 0, sizeof(seg_hdr)); + pdu_buffer.handle = (void *)&tx_pdu_meta_buf.node_tx; + pdu_buffer.pdu = (struct pdu_iso *)tx_pdu_meta_buf.node_tx.pdu; + pdu_buffer.size = TEST_TX_PDU_PAYLOAD_MAX; + sdu_packet_number = 0; + event_number = 0; + sdu_timestamp = 9249; + ref_point = sdu_timestamp + (iso_interval_int * ISO_INT_UNIT_US) - 50; + sdu_total_size = 10; + testdata_indx = 0; + testdata_size = 10; + payload_number = event_number * BN; + + source_hdl = basic_tx_test_setup(0xADAD, /* Handle */ + role, /* Role */ + true, /* Framed */ + BN, /* BN */ + FT, /* FT */ + max_octets, /* max_octets */ + sdu_interval, /* SDU Interval */ + iso_interval_int, /* ISO Interval */ + stream_sync_delay, /* Stream Sync Delay */ + group_sync_delay); /* Group Sync Delay */ + + isoal_test_create_sdu_fagment(BT_ISO_SINGLE, + &testdata[testdata_indx], + (testdata_size - testdata_indx), + sdu_total_size, + sdu_packet_number, + sdu_timestamp, + sdu_timestamp, + ref_point, + event_number, + &tx_sdu_frag_buf.sdu_tx); + + SET_NEXT_PDU_ALLOC_BUFFER(&pdu_buffer); + SET_NEXT_PDU_ALLOC_BUFFER(&pdu_buffer); + PDU_ALLOC_TEST_RETURNS(ISOAL_STATUS_OK); + PDU_WRITE_TEST_RETURNS(ISOAL_STATUS_OK); + PDU_EMIT_TEST_RETURNS(ISOAL_STATUS_OK); + PDU_RELEASE_TEST_RETURNS(ISOAL_STATUS_OK); + + err = isoal_tx_sdu_fragment(source_hdl, &tx_sdu_frag_buf.sdu_tx); + + zassert_equal(err, ISOAL_STATUS_OK, "err = 0x%02x", err); + + /* Test segmentation (Black Box) */ + /* Valid PDUs */ + /* PDU 1 */ + seg_hdr[0].sc = 0; + seg_hdr[0].cmplt = 0; + seg_hdr[0].timeoffset = ref_point - sdu_timestamp; + seg_hdr[0].len = PDU_ISO_SEG_TIMEOFFSET_SIZE; + pdu_hdr_loc = 0; + pdu_write_loc = PDU_ISO_SEG_HDR_SIZE + PDU_ISO_SEG_TIMEOFFSET_SIZE; + sdu_read_loc = 0; + pdu_write_size = pdu_write_loc + testdata_size; + sdu_fragments = 1; + + ZASSERT_PDU_WRITE_TEST(history[0], + pdu_buffer, + pdu_hdr_loc, + &seg_hdr[0], + (PDU_ISO_SEG_HDR_SIZE + PDU_ISO_SEG_TIMEOFFSET_SIZE)); + + ZASSERT_PDU_WRITE_TEST(history[1], + pdu_buffer, + pdu_write_loc, + &testdata[sdu_read_loc], + (pdu_write_size - pdu_write_loc)); + + seg_hdr[1] = seg_hdr[0]; + seg_hdr[1].cmplt = 1; seg_hdr[1].len += (pdu_write_size - pdu_write_loc); ZASSERT_PDU_WRITE_TEST(history[2], @@ -7281,6 +8301,7 @@ ZTEST(test_tx_framed_ebq, test_tx_framed_cis_fra_per_bv07c) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); @@ -7350,7 +8371,7 @@ ZTEST(test_tx_framed_ebq, test_tx_framed_cis_fra_per_bv07c) event_number++; sdu_packet_number++; sdu_timestamp = sdu_timestamp + sdu_interval; - ref_point = ref_point + (iso_interval_int * CONN_INT_UNIT_US); + ref_point = ref_point + (iso_interval_int * ISO_INT_UNIT_US); sdu_total_size = 20; testdata_indx = testdata_size; testdata_size += 20; @@ -7361,6 +8382,7 @@ ZTEST(test_tx_framed_ebq, test_tx_framed_cis_fra_per_bv07c) sdu_total_size, sdu_packet_number, sdu_timestamp, + sdu_timestamp, ref_point, event_number, &tx_sdu_frag_buf.sdu_tx); diff --git a/tests/bluetooth/hci_uart_async/CMakeLists.txt b/tests/bluetooth/hci_uart_async/CMakeLists.txt new file mode 100644 index 00000000000..2380f9f1152 --- /dev/null +++ b/tests/bluetooth/hci_uart_async/CMakeLists.txt @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +set(EXTRA_CONF_FILE + ../../../samples/bluetooth/hci_uart_async/prj.conf +) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +project(test_samples_bluetooth_hci_uart_async) +target_sources(app PRIVATE + ../../../samples/bluetooth/hci_uart_async/src/hci_uart_async.c + src/test_hci_uart_async.c +) diff --git a/tests/bluetooth/hci_uart_async/app.overlay b/tests/bluetooth/hci_uart_async/app.overlay new file mode 100644 index 00000000000..d9186722ba6 --- /dev/null +++ b/tests/bluetooth/hci_uart_async/app.overlay @@ -0,0 +1,11 @@ +/ { + chosen { + zephyr,bt-c2h-uart = &test_uart; + }; + + test_uart: test_uart { + compatible = "vnd,serial"; + status = "okay"; + buffer-size = <100>; + }; +}; diff --git a/tests/bluetooth/hci_uart_async/boards/native_posix.conf b/tests/bluetooth/hci_uart_async/boards/native_posix.conf new file mode 100644 index 00000000000..e638cd6a0ef --- /dev/null +++ b/tests/bluetooth/hci_uart_async/boards/native_posix.conf @@ -0,0 +1,3 @@ +# Print logs and test results on stdout. For some reason, this not the +# default when SERIAL=y. +CONFIG_LOG_BACKEND_NATIVE_POSIX=y diff --git a/tests/bluetooth/hci_uart_async/prj.conf b/tests/bluetooth/hci_uart_async/prj.conf new file mode 100644 index 00000000000..b038c7c4312 --- /dev/null +++ b/tests/bluetooth/hci_uart_async/prj.conf @@ -0,0 +1,10 @@ +CONFIG_BT_NO_DRIVER=y + +CONFIG_RING_BUFFER=y + +CONFIG_ASSERT=y +CONFIG_LOG=y +CONFIG_TEST=y + +CONFIG_ZTEST=y +CONFIG_ZTEST_NEW_API=y diff --git a/tests/bluetooth/hci_uart_async/src/test_hci_uart_async.c b/tests/bluetooth/hci_uart_async/src/test_hci_uart_async.c new file mode 100644 index 00000000000..1996aba177a --- /dev/null +++ b/tests/bluetooth/hci_uart_async/src/test_hci_uart_async.c @@ -0,0 +1,238 @@ +/* Copyright (c) 2023 Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +LOG_MODULE_REGISTER(test, LOG_LEVEL_DBG); + +/* This is a mock UART. Using `serial_vnd_...` on this simulates + * traffic from the external Host. + */ +static const struct device *const zephyr_bt_c2h_uart = DEVICE_DT_GET(DT_CHOSEN(zephyr_bt_c2h_uart)); + +/* The DUT is Sandwiched between the mock serial interface and a mock + * controller. {{{ + */ +static void serial_vnd_data_callback(const struct device *dev, void *user_data); +static int drv_send(struct net_buf *buf); +static int drv_open(void); +static const struct bt_hci_driver drv = { + .name = "Mock Controller", + .bus = BT_HCI_DRIVER_BUS_VIRTUAL, + .open = drv_open, + .send = drv_send, +}; +static int sys_init_hci_driver_register(void) +{ + serial_vnd_set_callback(zephyr_bt_c2h_uart, serial_vnd_data_callback, NULL); + bt_hci_driver_register(&drv); + return 0; +} +SYS_INIT(sys_init_hci_driver_register, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE); +/* }}} */ + +/* Start the DUT "main thread". The settings for this thread are selected as + * true as possible to the real main thread. {{{ + */ +static struct k_thread hci_uart_thread; +static K_THREAD_PINNED_STACK_DEFINE(hci_uart_thread_stack, CONFIG_MAIN_STACK_SIZE); +static void hci_uart_thread_entry(void *p1, void *p2, void *p3) +{ + extern void hci_uart_main(void); + hci_uart_main(); +} +static int sys_init_spawn_hci_uart(void) +{ + k_thread_name_set(&hci_uart_thread, "hci_uart_main"); + k_thread_create(&hci_uart_thread, hci_uart_thread_stack, + K_THREAD_STACK_SIZEOF(hci_uart_thread_stack), hci_uart_thread_entry, NULL, + NULL, NULL, CONFIG_MAIN_THREAD_PRIORITY, 0, K_NO_WAIT); + return 0; +} +SYS_INIT(sys_init_spawn_hci_uart, POST_KERNEL, 64); +/* }}} */ + +/* Mock controller callbacks. {{{ */ + +static int drv_open(void) +{ + LOG_DBG("drv_open"); + return 0; +} + +/** This FIFO holds the references to all h2c packets the DUT has sent + * to the controller using #bt_send. + * + * Each test should mock a controller by calling #net_buf_get on this + * FIFO and simulate a controller's #bt_hci_driver::drv_send. The mocks + * should use #bt_recv to send c2h packets to the DUT. + */ +K_FIFO_DEFINE(drv_send_fifo); /* elem T: net_buf */ +static int drv_send(struct net_buf *buf) +{ + LOG_DBG("buf %p type %d len %u", buf, bt_buf_get_type(buf), buf->len); + LOG_HEXDUMP_DBG(buf->data, buf->len, "buf"); + + __ASSERT_NO_MSG(buf); + net_buf_put(&drv_send_fifo, buf); + return 0; +} + +/* }}} */ + +/* Mock UART c2h TX handler. {{{ */ + +static void serial_vnd_data_callback(const struct device *dev, void *user_data) +{ + uint32_t size = serial_vnd_out_data_size_get(dev); + uint8_t data[size]; + + serial_vnd_read_out_data(dev, data, size); + LOG_HEXDUMP_DBG(data, size, "uart tx"); + + /* If a test needs to look at the c2h UART traffic, it can be + * captured here. + */ +} + +/* }}} */ + +#define HCI_NORMAL_CMD_BUF_COUNT (CONFIG_BT_BUF_CMD_TX_COUNT - 1) +#define TEST_PARAM_HOST_COMPLETE_COUNT 10 +#define TIMEOUT_PRESUME_STUCK K_SECONDS(1) + +/** Corresponds to: + * - #bt_hci_cmd_hdr + */ +const uint8_t h4_msg_cmd_dummy1[] = { + 0x01, /* H4: opcode = CMD */ + 0x01, 0x00, /* H4: CMD: opcode = 1 */ + 0x00, /* H4: CMD: len = 0 */ +}; + +/** Corresponds to: + * - #bt_hci_cmd_hdr + * - #bt_hci_cp_host_num_completed_packets + */ +const uint8_t h4_msg_cmd_host_num_complete[] = { + 0x01, /* H4: opcode = CMD */ + 0x35, 0x0c, /* H4: CMD: opcode = BT_HCI_OP_HOST_NUM_COMPLETED_PACKETS */ + 0x05, /* H4: CMD: len = 5 */ + 0x01, /* H4: CMD: num_handles = 1 */ + 0x00, 0x00, /* H4: CMD: connection_handle = 0 */ + 0x01, 0x00, /* H4: CMD: num_complete = 1 */ +}; + +/** Corresponds to: + * - #bt_hci_evt_hdr + * - #bt_hci_evt_cmd_complete + */ +const uint8_t hci_msg_rx_evt_cmd_complete[] = { + BT_HCI_EVT_CMD_COMPLETE, /* EVT: opcode */ + 0x03, /* EVT: len */ + 0x01, /* EVT: CMDC: ncmd = 1 */ + /* EVT: CMDC: opcode */ + 0x00, + 0x00, +}; + +ZTEST_SUITE(hci_uart, NULL, NULL, NULL, NULL, NULL); +ZTEST(hci_uart, test_h2c_cmd_flow_control) +{ + /* This test assumes the DUT does not care about the contents of + * the HCI messages, other than the HCI type/endpoint and the + * size. This allows the test to cheat and skip the HCI Reset, + * connection setup etc and use dummy command-packets. + */ + + /* Send commands, saturating the controller's command pipeline. */ + for (uint16_t i = 0; i < HCI_NORMAL_CMD_BUF_COUNT; i++) { + int write_size = serial_vnd_queue_in_data(zephyr_bt_c2h_uart, h4_msg_cmd_dummy1, + sizeof(h4_msg_cmd_dummy1)); + __ASSERT_NO_MSG(write_size == sizeof(h4_msg_cmd_dummy1)); + } + + /* At this point, the HCI flow control limit for the cmd + * endpoint has been reached. It will remain so until the + * controller mock has sent a 'HCI Command Complete' event. + * + * But the 'HCI Host Number of Completed Packets' command is + * exempt from HCI flow control. (It's like it has its own + * endpoint, that has no flow control.) + * + * We now send several 'HCI Host Number of Completed Packets' + * packets before handling any commands in the controller. This + * tests whether the DUT is able to engage the lower transport + * flow controller (i.e. UART flow-control) or somehow handle + * the special packets out-of-order in real-time. + */ + for (uint16_t i = 0; i < TEST_PARAM_HOST_COMPLETE_COUNT; i++) { + int write_size = + serial_vnd_queue_in_data(zephyr_bt_c2h_uart, h4_msg_cmd_host_num_complete, + sizeof(h4_msg_cmd_host_num_complete)); + __ASSERT_NO_MSG(write_size == sizeof(h4_msg_cmd_host_num_complete)); + } + + LOG_DBG("All h2c packets queued on UART"); + + /* Then, we check that all packets are delivered without loss. */ + + /* Expect all the normal commands first. */ + for (uint16_t i = 0; i < HCI_NORMAL_CMD_BUF_COUNT; i++) { + /* The mock controller processes a command. */ + { + struct net_buf *buf = net_buf_get(&drv_send_fifo, TIMEOUT_PRESUME_STUCK); + + zassert_not_null(buf); + zassert_equal(buf->len, sizeof(h4_msg_cmd_dummy1) - 1, "Wrong length"); + zassert_mem_equal(buf->data, &h4_msg_cmd_dummy1[1], + sizeof(h4_msg_cmd_dummy1) - 1); + net_buf_unref(buf); + } + + /* The controller sends a HCI Command Complete response. */ + { + int err; + struct net_buf *buf = bt_buf_get_rx(BT_BUF_EVT, K_NO_WAIT); + + zassert_not_null(buf); + net_buf_add_mem(buf, hci_msg_rx_evt_cmd_complete, + sizeof(hci_msg_rx_evt_cmd_complete)); + err = bt_recv(buf); + zassert_equal(err, 0, "bt_recv failed"); + } + } + + /* Expect all the 'HCI Host Number of Completed Packets'. */ + for (uint16_t i = 0; i < TEST_PARAM_HOST_COMPLETE_COUNT; i++) { + /* The mock controller processes a 'HCI Host Number of Completed Packets'. */ + { + struct net_buf *buf = net_buf_get(&drv_send_fifo, TIMEOUT_PRESUME_STUCK); + + zassert_not_null(buf); + zassert_equal(buf->len, sizeof(h4_msg_cmd_host_num_complete) - 1, + "Wrong length"); + zassert_mem_equal(buf->data, &h4_msg_cmd_host_num_complete[1], + sizeof(h4_msg_cmd_dummy1) - 2); + net_buf_unref(buf); + } + + /* There is no response to 'HCI Host Number of Completed Packets'. */ + } + + LOG_DBG("All h2c packets received by controller."); +} diff --git a/tests/bluetooth/hci_uart_async/testcase.yaml b/tests/bluetooth/hci_uart_async/testcase.yaml new file mode 100644 index 00000000000..f7f4a6255eb --- /dev/null +++ b/tests/bluetooth/hci_uart_async/testcase.yaml @@ -0,0 +1,6 @@ +tests: + samples.bluetooth.hci_uart_async: + tags: bluetooth uart + harness: ztest + platform_allow: + - native_posix diff --git a/tests/bluetooth/init/prj_ctlr_4_0.conf b/tests/bluetooth/init/prj_ctlr_4_0.conf index ff3917be54b..4f9ec70d3bb 100644 --- a/tests/bluetooth/init/prj_ctlr_4_0.conf +++ b/tests/bluetooth/init/prj_ctlr_4_0.conf @@ -19,7 +19,6 @@ CONFIG_BT_CTLR_XTAL_ADVANCED=y CONFIG_BT_CTLR_SCHED_ADVANCED=y CONFIG_BT_CTLR_RADIO_ENABLE_FAST=n CONFIG_BT_CTLR_TIFS_HW=y -CONFIG_BT_CTLR_FAST_ENC=n CONFIG_BT_CTLR_CONN_RSSI=n CONFIG_BT_CTLR_ADV_INDICATION=n CONFIG_BT_CTLR_SCAN_REQ_NOTIFY=n diff --git a/tests/bluetooth/init/prj_ctlr_4_0_dbg.conf b/tests/bluetooth/init/prj_ctlr_4_0_dbg.conf index 4c101cc6e16..5fc61cf98c6 100644 --- a/tests/bluetooth/init/prj_ctlr_4_0_dbg.conf +++ b/tests/bluetooth/init/prj_ctlr_4_0_dbg.conf @@ -21,7 +21,6 @@ CONFIG_BT_CTLR_XTAL_ADVANCED=y CONFIG_BT_CTLR_SCHED_ADVANCED=y CONFIG_BT_CTLR_RADIO_ENABLE_FAST=n CONFIG_BT_CTLR_TIFS_HW=y -CONFIG_BT_CTLR_FAST_ENC=n CONFIG_BT_CTLR_CONN_RSSI=n CONFIG_BT_CTLR_ADV_INDICATION=n CONFIG_BT_CTLR_SCAN_REQ_NOTIFY=n diff --git a/tests/bluetooth/init/prj_ctlr_5_x_dbg.conf b/tests/bluetooth/init/prj_ctlr_5_x_dbg.conf new file mode 100644 index 00000000000..50f72889476 --- /dev/null +++ b/tests/bluetooth/init/prj_ctlr_5_x_dbg.conf @@ -0,0 +1,75 @@ +CONFIG_BT=y +CONFIG_BT_CTLR=y +CONFIG_BT_LL_SW_SPLIT=y +CONFIG_BT_CTLR_DUP_FILTER_LEN=16 +CONFIG_BT_CTLR_CONN_PARAM_REQ=y +CONFIG_BT_CTLR_LE_PING=y +CONFIG_BT_CTLR_PRIVACY=n +CONFIG_BT_CTLR_EXT_SCAN_FP=n +CONFIG_BT_DATA_LEN_UPDATE=n +CONFIG_BT_PHY_UPDATE=y +CONFIG_BT_CTLR_CHAN_SEL_2=y +CONFIG_BT_CTLR_MIN_USED_CHAN=y +CONFIG_BT_CTLR_ADV_EXT=y +CONFIG_BT_CTLR_ADV_PERIODIC=y +CONFIG_BT_CTLR_ADV_ISO=y +CONFIG_BT_CTLR_SYNC_PERIODIC=y +CONFIG_BT_CTLR_SYNC_ISO=y +CONFIG_BT_CTLR_CENTRAL_ISO=y +CONFIG_BT_CTLR_PERIPHERAL_ISO=y +CONFIG_BT_CTLR_DTM_HCI=y +CONFIG_BT_CTLR_ADVANCED_FEATURES=y +CONFIG_BT_CTLR_PHY_2M=y +CONFIG_BT_CTLR_PHY_2M_NRF=y +CONFIG_BT_CTLR_PHY_CODED=y +CONFIG_BT_CTLR_ADV_AUX_PDU_BACK2BACK=y +CONFIG_BT_CTLR_LLL_PRIO=0 +CONFIG_BT_CTLR_ULL_HIGH_PRIO=1 +CONFIG_BT_CTLR_XTAL_ADVANCED=n +CONFIG_BT_CTLR_SCHED_ADVANCED=y +CONFIG_BT_CTLR_RADIO_ENABLE_FAST=y +CONFIG_BT_CTLR_TIFS_HW=n +CONFIG_BT_CTLR_TX_RETRY_DISABLE=y +CONFIG_BT_CTLR_CONN_RSSI=y +CONFIG_BT_CTLR_ADV_INDICATION=y +CONFIG_BT_CTLR_SCAN_REQ_NOTIFY=y +CONFIG_BT_CTLR_SCAN_REQ_RSSI=y +CONFIG_BT_CTLR_SCAN_INDICATION=y +CONFIG_BT_CTLR_EVENT_OVERHEAD_RESERVE_MAX=n +CONFIG_BT_CTLR_PROFILE_ISR=y +CONFIG_BT_CTLR_DEBUG_PINS=y +CONFIG_BT_CTLR_TEST=y +CONFIG_BT_HCI_VS_EXT=y +CONFIG_BT_HCI_MESH_EXT=n +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_CENTRAL=y +CONFIG_BT_EXT_ADV=y +CONFIG_BT_PER_ADV=y +CONFIG_BT_PER_ADV_SYNC=y +CONFIG_BT_ISO_BROADCASTER=y +CONFIG_BT_ISO_SYNC_RECEIVER=y +CONFIG_BT_ISO_CENTRAL=y +CONFIG_BT_ISO_PERIPHERAL=y +CONFIG_BT_SMP=y +CONFIG_BT_SIGNING=y +CONFIG_BT_SMP_SC_ONLY=y +CONFIG_BT_TINYCRYPT_ECC=y +CONFIG_BT_USE_DEBUG_KEYS=y +CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y +CONFIG_BT_GATT_CLIENT=y +CONFIG_BT_DEBUG_MONITOR_UART=y +CONFIG_BT_HCI_CORE_LOG_LEVEL_DBG=y +CONFIG_BT_CONN_LOG_LEVEL_DBG=y +CONFIG_BT_KEYS_LOG_LEVEL_DBG=y +CONFIG_BT_L2CAP_LOG_LEVEL_DBG=y +CONFIG_BT_SMP_LOG_LEVEL_DBG=y +CONFIG_BT_HCI_DRIVER_LOG_LEVEL_DBG=y +CONFIG_BT_SMP_SELFTEST=y +CONFIG_BT_ATT_LOG_LEVEL_DBG=y +CONFIG_BT_GATT_LOG_LEVEL_DBG=y +CONFIG_BT_BREDR=n +CONFIG_DEBUG=y +CONFIG_FLASH=y +CONFIG_SOC_FLASH_NRF_RADIO_SYNC_TICKER=n +CONFIG_ZTEST=y +CONFIG_ZTEST_NEW_API=y diff --git a/tests/bluetooth/init/prj_ctlr_dbg.conf b/tests/bluetooth/init/prj_ctlr_dbg.conf index 6aff0965ce9..2f8224cdfea 100644 --- a/tests/bluetooth/init/prj_ctlr_dbg.conf +++ b/tests/bluetooth/init/prj_ctlr_dbg.conf @@ -23,7 +23,6 @@ CONFIG_BT_CTLR_XTAL_ADVANCED=n CONFIG_BT_CTLR_SCHED_ADVANCED=n CONFIG_BT_CTLR_RADIO_ENABLE_FAST=y CONFIG_BT_CTLR_TIFS_HW=n -CONFIG_BT_CTLR_FAST_ENC=y CONFIG_BT_CTLR_TX_RETRY_DISABLE=y CONFIG_BT_CTLR_CONN_RSSI=y CONFIG_BT_CTLR_ADV_INDICATION=y diff --git a/tests/bluetooth/init/prj_ctlr_ticker.conf b/tests/bluetooth/init/prj_ctlr_ticker.conf new file mode 100644 index 00000000000..4e5b962231e --- /dev/null +++ b/tests/bluetooth/init/prj_ctlr_ticker.conf @@ -0,0 +1,64 @@ +CONFIG_BT=y +CONFIG_BT_CTLR=y +CONFIG_BT_LL_SW_SPLIT=y +CONFIG_BT_CTLR_DUP_FILTER_LEN=16 +CONFIG_BT_CTLR_CONN_PARAM_REQ=y +CONFIG_BT_CTLR_LE_PING=y +CONFIG_BT_CTLR_PRIVACY=n +CONFIG_BT_CTLR_EXT_SCAN_FP=n +CONFIG_BT_DATA_LEN_UPDATE=n +CONFIG_BT_PHY_UPDATE=y +CONFIG_BT_CTLR_CHAN_SEL_2=y +CONFIG_BT_CTLR_MIN_USED_CHAN=y +CONFIG_BT_CTLR_ADV_EXT=y +CONFIG_BT_CTLR_DTM_HCI=y +CONFIG_BT_CTLR_ADVANCED_FEATURES=y +CONFIG_BT_CTLR_PHY_2M=y +CONFIG_BT_CTLR_PHY_2M_NRF=y +CONFIG_BT_CTLR_PHY_CODED=y +CONFIG_BT_CTLR_ADV_AUX_PDU_BACK2BACK=y +CONFIG_BT_CTLR_LLL_PRIO=0 +CONFIG_BT_CTLR_ULL_HIGH_PRIO=1 +CONFIG_BT_CTLR_XTAL_ADVANCED=n +CONFIG_BT_CTLR_SCHED_ADVANCED=n +CONFIG_BT_CTLR_RADIO_ENABLE_FAST=y +CONFIG_BT_CTLR_TIFS_HW=n +CONFIG_BT_CTLR_TX_RETRY_DISABLE=y +CONFIG_BT_CTLR_CONN_RSSI=y +CONFIG_BT_CTLR_ADV_INDICATION=y +CONFIG_BT_CTLR_SCAN_REQ_NOTIFY=y +CONFIG_BT_CTLR_SCAN_REQ_RSSI=y +CONFIG_BT_CTLR_SCAN_INDICATION=y +CONFIG_BT_CTLR_PROFILE_ISR=y +CONFIG_BT_CTLR_DEBUG_PINS=y +CONFIG_BT_CTLR_TEST=y +CONFIG_BT_TICKER_EXT=n +CONFIG_BT_TICKER_SLOT_AGNOSTIC=y +CONFIG_BT_TICKER_PREFER_START_BEFORE_STOP=y +CONFIG_BT_HCI_VS_EXT=y +CONFIG_BT_HCI_MESH_EXT=n +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_CENTRAL=y +CONFIG_BT_SMP=y +CONFIG_BT_SIGNING=y +CONFIG_BT_SMP_SC_ONLY=y +CONFIG_BT_TINYCRYPT_ECC=y +CONFIG_BT_USE_DEBUG_KEYS=y +CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y +CONFIG_BT_GATT_CLIENT=y +CONFIG_BT_DEBUG_MONITOR_UART=y +CONFIG_BT_HCI_CORE_LOG_LEVEL_DBG=y +CONFIG_BT_CONN_LOG_LEVEL_DBG=y +CONFIG_BT_KEYS_LOG_LEVEL_DBG=y +CONFIG_BT_L2CAP_LOG_LEVEL_DBG=y +CONFIG_BT_SMP_LOG_LEVEL_DBG=y +CONFIG_BT_HCI_DRIVER_LOG_LEVEL_DBG=y +CONFIG_BT_SMP_SELFTEST=y +CONFIG_BT_ATT_LOG_LEVEL_DBG=y +CONFIG_BT_GATT_LOG_LEVEL_DBG=y +CONFIG_BT_BREDR=n +CONFIG_DEBUG=y +CONFIG_FLASH=y +CONFIG_SOC_FLASH_NRF_RADIO_SYNC_TICKER=n +CONFIG_ZTEST=y +CONFIG_ZTEST_NEW_API=y diff --git a/tests/bluetooth/init/prj_ctlr_tiny.conf b/tests/bluetooth/init/prj_ctlr_tiny.conf index 7679de9a661..ce75583da16 100644 --- a/tests/bluetooth/init/prj_ctlr_tiny.conf +++ b/tests/bluetooth/init/prj_ctlr_tiny.conf @@ -22,7 +22,6 @@ CONFIG_BT_CTLR_XTAL_ADVANCED=n CONFIG_BT_CTLR_SCHED_ADVANCED=n CONFIG_BT_CTLR_RADIO_ENABLE_FAST=n CONFIG_BT_CTLR_TIFS_HW=y -CONFIG_BT_CTLR_FAST_ENC=n CONFIG_BT_CTLR_CONN_RSSI=n CONFIG_BT_CTLR_ADV_INDICATION=n CONFIG_BT_CTLR_SCAN_REQ_NOTIFY=n diff --git a/tests/bluetooth/init/testcase.yaml b/tests/bluetooth/init/testcase.yaml index 4c252115178..17d57ec9d47 100644 --- a/tests/bluetooth/init/testcase.yaml +++ b/tests/bluetooth/init/testcase.yaml @@ -122,6 +122,38 @@ tests: integration_platforms: - nrf52840dk_nrf52840 - nrf52dk_nrf52832 + bluetooth.init.test_ctlr_5_x_dbg: + extra_args: + - CONF_FILE=prj_ctlr_5_x_dbg.conf + - DTC_OVERLAY_FILE=pa_lna.overlay + platform_allow: + - nrf52840dk_nrf52840 + - nrf52dk_nrf52832 + integration_platforms: + - nrf52840dk_nrf52840 + - nrf52dk_nrf52832 + bluetooth.init.test_ctlr_sw_switch_single_timer: + extra_args: + - CONF_FILE=prj_ctlr.conf + - CONFIG_BT_CTLR_ADVANCED_FEATURES=y + - CONFIG_BT_CTLR_SW_SWITCH_SINGLE_TIMER=y + platform_allow: + - nrf5340dk_nrf5340_cpunet + - nrf52840dk_nrf52840 + - nrf52dk_nrf52832 + integration_platforms: + - nrf5340dk_nrf5340_cpunet + - nrf52840dk_nrf52840 + - nrf52dk_nrf52832 + bluetooth.init.test_ctlr_ticker: + extra_args: + - CONF_FILE=prj_ctlr_ticker.conf + platform_allow: + - nrf52840dk_nrf52840 + - nrf52dk_nrf52832 + integration_platforms: + - nrf52840dk_nrf52840 + - nrf52dk_nrf52832 bluetooth.init.test_ctlr_broadcaster: extra_args: CONF_FILE=prj_ctlr_broadcaster.conf platform_allow: diff --git a/tests/bluetooth/shell/testcase.yaml b/tests/bluetooth/shell/testcase.yaml index 4b46c8d6781..ef4eed67eb3 100644 --- a/tests/bluetooth/shell/testcase.yaml +++ b/tests/bluetooth/shell/testcase.yaml @@ -13,6 +13,13 @@ tests: tags: bluetooth harness: keyboard min_flash: 145 + bluetooth.shell.power_control_request: + extra_configs: + - CONFIG_BT_TRANSMIT_POWER_CONTROL=y + - CONFIG_BT_CTLR=n + platform_allow: + - native_posix + build_only: true bluetooth.shell.cdc_acm: extra_args: - OVERLAY_CONFIG=cdc_acm.conf diff --git a/tests/bluetooth/tester/overlay-mesh-v1d1.conf b/tests/bluetooth/tester/overlay-mesh-v1d1.conf new file mode 100644 index 00000000000..52045ce34df --- /dev/null +++ b/tests/bluetooth/tester/overlay-mesh-v1d1.conf @@ -0,0 +1,30 @@ +CONFIG_ENTROPY_GENERATOR=y + +CONFIG_BT_MESH_V1d1=y +CONFIG_BT_MESH_OP_AGG_CLI=y +CONFIG_BT_MESH_OP_AGG_SRV=y +# PTS requires more key slots. +# First one is implicitly taken by Device Key. +CONFIG_BT_MESH_MODEL_KEY_COUNT=3 +CONFIG_BT_MESH_LARGE_COMP_DATA_CLI=y +CONFIG_BT_MESH_LARGE_COMP_DATA_SRV=y +CONFIG_BT_MESH_SAR_CFG_SRV=y +CONFIG_BT_MESH_SAR_CFG_CLI=y +CONFIG_BT_MESH_TX_SEG_MSG_COUNT=10 +CONFIG_BT_MESH_RPR_SRV=y +CONFIG_BT_MESH_RPR_CLI=y +CONFIG_BT_MESH_RPR_AD_TYPES_MAX=2 +CONFIG_BT_MESH_BLOB_CLI=y +CONFIG_BT_MESH_DFU_CLI=y +CONFIG_BT_MESH_BLOB_SRV=y +CONFIG_BT_MESH_DFU_SRV=y +CONFIG_BT_MESH_DFD_SRV=y +CONFIG_BT_MESH_DFU_SLOT_CNT=2 +CONFIG_BT_MESH_PRIV_BEACONS=y +CONFIG_BT_MESH_PRIV_BEACON_SRV=y +CONFIG_BT_MESH_PRIV_BEACON_CLI=y +CONFIG_BT_MESH_OD_PRIV_PROXY_SRV=y +CONFIG_BT_MESH_MODEL_EXTENSIONS=y +CONFIG_BT_MESH_COMP_PAGE_1=y +CONFIG_BT_MESH_COMP_PAGE_2=y +CONFIG_SETTINGS=y diff --git a/tests/bluetooth/tester/src/btp/btp_mesh.h b/tests/bluetooth/tester/src/btp/btp_mesh.h index d7d2b2cdc7b..b5ab169b263 100644 --- a/tests/bluetooth/tester/src/btp/btp_mesh.h +++ b/tests/bluetooth/tester/src/btp/btp_mesh.h @@ -30,9 +30,12 @@ struct btp_mesh_read_supported_commands_rp { #define BTP_MESH_CONFIG_PROVISIONING 0x02 +#define BTP_MESH_PROV_AUTH_MAX_LEN 32 + struct btp_mesh_config_provisioning_cmd { uint8_t uuid[16]; - uint8_t static_auth[16]; + uint8_t static_auth[BTP_MESH_PROV_AUTH_MAX_LEN]; + uint8_t static_auth_size; uint8_t out_size; uint16_t out_actions; uint8_t in_size; @@ -41,7 +44,8 @@ struct btp_mesh_config_provisioning_cmd { } __packed; struct btp_mesh_config_provisioning_cmd_v2 { uint8_t uuid[16]; - uint8_t static_auth[16]; + uint8_t static_auth[BTP_MESH_PROV_AUTH_MAX_LEN]; + uint8_t static_auth_size; uint8_t out_size; uint16_t out_actions; uint8_t in_size; @@ -73,6 +77,10 @@ struct btp_mesh_provision_node_cmd_v2 { } __packed; #define BTP_MESH_INIT 0x04 +struct btp_mesh_init_cmd { + uint8_t comp; +} __packed; + #define BTP_MESH_RESET 0x05 #define BTP_MESH_INPUT_NUMBER 0x06 struct btp_mesh_input_number_cmd { @@ -121,6 +129,7 @@ struct btp_mesh_lpn_set_cmd { #define BTP_MESH_MODEL_SEND 0x0f struct btp_mesh_model_send_cmd { + uint8_t ttl; uint16_t src; uint16_t dst; uint8_t payload_len; @@ -766,6 +775,271 @@ struct btp_proxy_connect_cmd { uint16_t net_idx; } __packed; +struct sar_transmitter { + uint8_t seg_int_step; + uint8_t unicast_retrans_count; + uint8_t unicast_retrans_without_prog_count; + uint8_t unicast_retrans_int_step; + uint8_t unicast_retrans_int_inc; + uint8_t multicast_retrans_count; + uint8_t multicast_retrans_int; +} __packed; + +struct sar_receiver { + uint8_t seg_thresh; + uint8_t ack_delay_inc; + uint8_t ack_retrans_count; + uint8_t discard_timeout; + uint8_t rx_seg_int_step; +} __packed; + +#define BTP_MESH_SAR_TRANSMITTER_GET 0x4f +struct btp_mesh_sar_transmitter_get_cmd { + uint16_t dst; +} __packed; + +#define BTP_MESH_SAR_TRANSMITTER_SET 0x50 +struct btp_mesh_sar_transmitter_set_cmd { + uint16_t dst; + struct sar_transmitter tx; +} __packed; + +#define BTP_MESH_SAR_RECEIVER_GET 0x51 +struct btp_mesh_sar_receiver_get_cmd { + uint16_t dst; +} __packed; + +#define BTP_MESH_SAR_RECEIVER_SET 0x52 +struct btp_mesh_sar_receiver_set_cmd { + uint16_t dst; + struct sar_receiver rx; +} __packed; + +#define BTP_MESH_LARGE_COMP_DATA_GET 0x53 +struct btp_mesh_large_comp_data_get_cmd { + uint16_t net_idx; + uint16_t addr; + uint8_t page; + uint16_t offset; +} __packed; +struct btp_mesh_large_comp_data_get_rp { + uint8_t data[0]; +} __packed; + +#define BTP_MESH_MODELS_METADATA_GET 0x54 +struct btp_mesh_models_metadata_get_cmd { + uint16_t net_idx; + uint16_t addr; + uint8_t page; + uint16_t offset; +} __packed; +struct btp_mesh_models_metadata_get_rp { + uint8_t data[0]; +} __packed; + +#define BTP_MESH_OPCODES_AGGREGATOR_INIT 0x55 +struct btp_mesh_opcodes_aggregator_init_cmd { + uint16_t net_idx; + uint16_t app_idx; + uint16_t dst; + uint16_t elem_addr; +} __packed; + +#define BTP_MESH_OPCODES_AGGREGATOR_SEND 0x56 + +#define BTP_MESH_COMP_CHANGE_PREPARE 0x57 + +#define BTP_MESH_RPR_SCAN_START 0x59 +struct btp_rpr_scan_start_cmd { + uint16_t dst; + uint8_t timeout; + uint8_t uuid[16]; +} __packed; + +#define BTP_MESH_RPR_EXT_SCAN_START 0x5a +struct btp_rpr_ext_scan_start_cmd { + uint16_t dst; + uint8_t timeout; + uint8_t uuid[16]; + uint8_t ad_count; + uint8_t ad_types[]; +} __packed; + +#define BTP_MESH_RPR_SCAN_CAPS_GET 0x5b +struct btp_rpr_scan_caps_get_cmd { + uint16_t dst; +} __packed; + +#define BTP_MESH_RPR_SCAN_GET 0x5c +struct btp_rpr_scan_get_cmd { + uint16_t dst; +} __packed; + +#define BTP_MESH_RPR_SCAN_STOP 0x5d +struct btp_rpr_scan_stop_cmd { + uint16_t dst; +} __packed; + +#define BTP_MESH_RPR_LINK_GET 0x5e +struct btp_rpr_link_get_cmd { + uint16_t dst; +} __packed; + +#define BTP_MESH_RPR_LINK_CLOSE 0x5f +struct btp_rpr_link_close_cmd { + uint16_t dst; +} __packed; + +#define BTP_MESH_RPR_PROV_REMOTE 0x60 +struct btp_rpr_prov_remote_cmd { + uint16_t dst; + uint8_t uuid[16]; + uint16_t net_idx; + uint16_t addr; +} __packed; + +#define BTP_MESH_RPR_REPROV_REMOTE 0x61 +struct btp_rpr_reprov_remote_cmd { + uint16_t dst; + uint16_t addr; + bool comp_change; +} __packed; + +#define BTP_MMDL_DFU_INFO_GET 0x5f +struct btp_mmdl_dfu_info_get_cmd { + uint8_t limit; +} __packed; + +#define BTP_MMDL_BLOB_INFO_GET 0x60 +struct btp_mmdl_blob_info_get_cmd { + uint8_t addr_cnt; + uint8_t addr[]; +} __packed; + +#define BTP_MMDL_DFU_UPDATE_METADATA_CHECK 0x61 +struct btp_mmdl_dfu_metadata_check_cmd { + uint8_t index; + uint8_t slot_idx; + uint8_t slot_size; + uint8_t fwid_len; + uint8_t metadata_len; + uint8_t data[]; +} __packed; + +struct btp_mmdl_dfu_metadata_check_rp { + uint8_t idx; + uint8_t status; + uint8_t effect; +} __packed; + +#define BTP_MMDL_DFU_FIRMWARE_UPDATE_GET 0x62 +#define BTP_MMDL_DFU_FIRMWARE_UPDATE_CANCEL 0x63 +#define BTP_MMDL_DFU_FIRMWARE_UPDATE_START 0x64 +struct btp_mmdl_dfu_firmware_update_cmd { + uint8_t addr_cnt; + uint8_t slot_idx; + uint8_t slot_size; + uint8_t fwid_len; + uint8_t metadata_len; + uint8_t block_size; + uint16_t chunk_size; + uint8_t data[]; +} __packed; + +struct btp_mmdl_dfu_firmware_update_rp { + uint8_t status; +} __packed; + +#define BTP_MMDL_BLOB_SRV_RECV 0x65 +struct btp_mmdl_blob_srv_recv_cmd { + uint64_t id; + uint16_t timeout; + uint8_t ttl; +} __packed; + +#define BTP_MMDL_BLOB_TRANSFER_START 0x66 +struct btp_mmdl_blob_transfer_start_cmd { + uint64_t id; + uint16_t size; + uint8_t block_size; + uint16_t chunk_size; + uint16_t timeout; + uint8_t ttl; +} __packed; + +#define BTP_MMDL_BLOB_TRANSFER_CANCEL 0x67 +#define BTP_MMDL_BLOB_TRANSFER_GET 0x68 +#define BTP_MMDL_BLOB_SRV_CANCEL 0x69 +#define BTP_MMDL_DFU_FIRMWARE_UPDATE_APPLY 0x6A +#define BTP_MMDL_DFU_SRV_APPLY 0x6B + +#define BTP_MESH_PRIV_BEACON_GET 0x6c +struct btp_priv_beacon_get_cmd { + uint16_t dst; +} __packed; + +#define BTP_MESH_PRIV_BEACON_SET 0x6d +struct btp_priv_beacon_set_cmd { + uint16_t dst; + uint8_t enabled; + uint8_t rand_interval; +} __packed; + +#define BTP_MESH_PRIV_GATT_PROXY_GET 0x6e +struct btp_priv_gatt_proxy_get_cmd { + uint16_t dst; +} __packed; + +#define BTP_MESH_PRIV_GATT_PROXY_SET 0x6f +struct btp_priv_gatt_proxy_set_cmd { + uint16_t dst; + uint8_t state; +} __packed; + +#define BTP_MESH_PRIV_NODE_ID_GET 0x70 +struct btp_priv_node_id_get_cmd { + uint16_t dst; + uint16_t key_net_idx; +} __packed; + +#define BTP_MESH_PRIV_NODE_ID_SET 0x71 +struct btp_priv_node_id_set_cmd { + uint16_t dst; + uint16_t net_idx; + uint8_t state; +} __packed; + +#define BTP_MESH_PROXY_PRIVATE_IDENTITY 0x72 + +#define BTP_MESH_OD_PRIV_PROXY_GET 0x73 +struct btp_od_priv_proxy_get_cmd { + uint16_t dst; +} __packed; + +#define BTP_MESH_OD_PRIV_PROXY_SET 0x74 + +struct btp_od_priv_proxy_set_cmd { + uint16_t dst; + uint8_t val; +} __packed; + +#define BTP_MESH_SRPL_CLEAR 0x75 + +struct btp_srpl_clear_cmd { + uint16_t dst; + uint16_t range_start; + uint8_t range_len; + uint8_t acked; +} __packed; + +#define BTP_MESH_PROXY_SOLICIT 0x76 + +struct btp_proxy_solicit_cmd { + uint16_t net_idx; +} __packed; + +#define BTP_MESH_START 0x78 + /* events */ #define BTP_MESH_EV_OUT_NUMBER_ACTION 0x80 struct btp_mesh_out_number_action_ev { @@ -789,6 +1063,7 @@ struct btp_mesh_in_action_ev { #define BTP_MESH_PROV_BEARER_PB_ADV 0x00 #define BTP_MESH_PROV_BEARER_PB_GATT 0x01 +#define BTP_MESH_PROV_BEARER_REMOTE 0x04 #define BTP_MESH_EV_PROV_LINK_OPEN 0x84 struct btp_mesh_prov_link_open_ev { uint8_t bearer; @@ -858,3 +1133,13 @@ struct btp_mesh_prov_node_added_ev { uint8_t uuid[16]; uint8_t num_elems; } __packed; + +#define BTP_MESH_EV_MODEL_RECV 0x8f +struct btp_mesh_model_recv_ev { + uint16_t src; + uint16_t dst; + uint8_t payload_len; + uint8_t payload[]; +} __packed; + +#define MESH_EV_BLOB_LOST_TARGET 0x90 diff --git a/tests/bluetooth/tester/src/btp/bttester.h b/tests/bluetooth/tester/src/btp/bttester.h index b30eee70d44..84d4722a913 100644 --- a/tests/bluetooth/tester/src/btp/bttester.h +++ b/tests/bluetooth/tester/src/btp/bttester.h @@ -73,6 +73,9 @@ uint8_t tester_unregister_vocs(void); uint8_t tester_init_ias(void); uint8_t tester_unregister_ias(void); +uint8_t tester_init_mmdl(void); +uint8_t tester_unregister_mmdl(void); + uint8_t tester_init_gap(void); uint8_t tester_unregister_gap(void); diff --git a/tests/bluetooth/tester/src/btp_core.c b/tests/bluetooth/tester/src/btp_core.c index 36f92619bfc..7ece1224ed0 100644 --- a/tests/bluetooth/tester/src/btp_core.c +++ b/tests/bluetooth/tester/src/btp_core.c @@ -118,6 +118,9 @@ static uint8_t register_service(const void *cmd, uint16_t cmd_len, case BTP_SERVICE_ID_MESH: status = tester_init_mesh(); break; + case BTP_SERVICE_ID_MESH_MDL: + status = tester_init_mmdl(); + break; #endif /* CONFIG_BT_MESH */ #if defined(CONFIG_BT_VCP_VOL_REND) case BTP_SERVICE_ID_VCS: @@ -201,6 +204,9 @@ static uint8_t unregister_service(const void *cmd, uint16_t cmd_len, case BTP_SERVICE_ID_MESH: status = tester_unregister_mesh(); break; + case BTP_SERVICE_ID_MESH_MDL: + status = tester_unregister_mmdl(); + break; #endif /* CONFIG_BT_MESH */ #if defined(CONFIG_BT_VCP_VOL_REND) case BTP_SERVICE_ID_VCS: diff --git a/tests/bluetooth/tester/src/btp_gatt.c b/tests/bluetooth/tester/src/btp_gatt.c index 4d046cc3aad..6509ca7fe4b 100644 --- a/tests/bluetooth/tester/src/btp_gatt.c +++ b/tests/bluetooth/tester/src/btp_gatt.c @@ -1651,11 +1651,11 @@ static uint8_t read_multiple(const void *cmd, uint16_t cmd_len, return BTP_STATUS_FAILED; } - if (cp->handles_count > ARRAY_SIZE(handles)) { + if (cp->handles_count == 0 || cp->handles_count > ARRAY_SIZE(handles)) { return BTP_STATUS_FAILED; } - for (i = 0; i < ARRAY_SIZE(handles); i++) { + for (i = 0; i < cp->handles_count; i++) { handles[i] = sys_le16_to_cpu(cp->handles[i]); } @@ -1670,7 +1670,7 @@ static uint8_t read_multiple(const void *cmd, uint16_t cmd_len, } read_params.func = read_cb; - read_params.handle_count = i; + read_params.handle_count = cp->handles_count; read_params.multiple.handles = handles; /* not used in read func */ read_params.multiple.variable = false; read_params.chan_opt = BT_ATT_CHAN_OPT_NONE; diff --git a/tests/bluetooth/tester/src/btp_mesh.c b/tests/bluetooth/tester/src/btp_mesh.c index c922c812631..bdc37c2f2e5 100644 --- a/tests/bluetooth/tester/src/btp_mesh.c +++ b/tests/bluetooth/tester/src/btp_mesh.c @@ -12,16 +12,23 @@ #include #include #include +#include #include #include +#include +#include +#include "mesh/access.h" #include #define LOG_MODULE_NAME bttester_mesh LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_BTTESTER_LOG_LEVEL); #include "btp/btp.h" +#include "dfu_slot.h" #define CID_LOCAL 0x05F1 +#define COMPANY_ID_LF 0x05F1 +#define COMPANY_ID_NORDIC_SEMI 0x05F9 /* Health server data */ #define CUR_FAULTS_MAX 4 @@ -43,7 +50,8 @@ static uint8_t priv_key[32]; /* Configured provisioning data */ static uint8_t dev_uuid[16]; -static uint8_t static_auth[16]; +static uint8_t static_auth[BTP_MESH_PROV_AUTH_MAX_LEN]; +static uint8_t static_auth_size; /* Vendor Model data */ #define VND_MODEL_ID_1 0x1234 @@ -51,7 +59,394 @@ static uint8_t vnd_app_key[16]; static uint16_t vnd_app_key_idx = 0x000f; /* Model send data */ -#define MODEL_BOUNDS_MAX 2 +#define MODEL_BOUNDS_MAX 100 + +#if defined(CONFIG_BT_MESH_BLOB_SRV) || defined(CONFIG_BT_MESH_BLOB_CLI) +/* BLOB Model data*/ +static uint8_t blob_rx_sum; +static bool blob_valid; +static const char *blob_data = "11111111111111111111111111111111"; + +static int blob_io_open(const struct bt_mesh_blob_io *io, + const struct bt_mesh_blob_xfer *xfer, + enum bt_mesh_blob_io_mode mode) +{ + blob_rx_sum = 0; + blob_valid = true; + return 0; +} + +static int blob_chunk_wr(const struct bt_mesh_blob_io *io, + const struct bt_mesh_blob_xfer *xfer, + const struct bt_mesh_blob_block *block, + const struct bt_mesh_blob_chunk *chunk) +{ + for (int i = 0; i < chunk->size; ++i) { + blob_rx_sum += chunk->data[i]; + if (chunk->data[i] != + blob_data[(i + chunk->offset) % strlen(blob_data)]) { + blob_valid = false; + } + } + + return 0; +} + +static int blob_chunk_rd(const struct bt_mesh_blob_io *io, + const struct bt_mesh_blob_xfer *xfer, + const struct bt_mesh_blob_block *block, + const struct bt_mesh_blob_chunk *chunk) +{ + for (int i = 0; i < chunk->size; ++i) { + chunk->data[i] = + blob_data[(i + chunk->offset) % strlen(blob_data)]; + } + + return 0; +} + +static const struct bt_mesh_blob_io dummy_blob_io = { + .open = blob_io_open, + .rd = blob_chunk_rd, + .wr = blob_chunk_wr, +}; +#endif + +#if defined(CONFIG_BT_MESH_DFD_SRV) +static const struct bt_mesh_dfu_slot *dfu_self_update_slot; + +static bool is_self_update(struct bt_mesh_dfd_srv *srv) +{ + for (int i = 0; i < ARRAY_SIZE(srv->targets); i++) { + if (bt_mesh_has_addr(srv->targets[i].blob.addr)) { + return true; + } + } + + return false; +} + +/* DFD Model data*/ +static int dfd_srv_recv(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot, + const struct bt_mesh_blob_io **io) +{ + LOG_DBG("Uploading new firmware image to the distributor."); + + *io = &dummy_blob_io; + + return 0; +} + +static void dfd_srv_del(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot) +{ + LOG_DBG("Deleting the firmware image from the distributor."); +} + +static int dfd_srv_send(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot, + const struct bt_mesh_blob_io **io) +{ + LOG_DBG("Starting the firmware distribution."); + + *io = &dummy_blob_io; + + dfu_self_update_slot = NULL; + + if (is_self_update(srv)) { + LOG_DBG("DFD server starts self-update..."); + dfu_self_update_slot = slot; + } + + return 0; +} + +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD +static struct { + uint8_t uri[CONFIG_BT_MESH_DFU_URI_MAXLEN]; + uint8_t uri_len; + uint8_t fwid[CONFIG_BT_MESH_DFU_FWID_MAXLEN]; + uint8_t fwid_len; + const struct bt_mesh_dfu_slot *slot; + uint8_t progress; + bool started; +} dfd_srv_oob_ctx; + +static void oob_check_handler(struct k_work *work); +static K_WORK_DEFINE(oob_check, oob_check_handler); +static void oob_store_handler(struct k_work *work); +static K_WORK_DEFINE(oob_store, oob_store_handler); + +static int dfd_srv_start_oob_upload(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot, + const char *uri, uint8_t uri_len, + const uint8_t *fwid, uint16_t fwid_len) +{ + LOG_DBG("Start OOB Upload"); + + memcpy(dfd_srv_oob_ctx.uri, uri, uri_len); + dfd_srv_oob_ctx.uri_len = uri_len; + memcpy(dfd_srv_oob_ctx.fwid, fwid, fwid_len); + dfd_srv_oob_ctx.fwid_len = fwid_len; + dfd_srv_oob_ctx.slot = slot; + dfd_srv_oob_ctx.progress = 0; + dfd_srv_oob_ctx.started = true; + + k_work_submit(&oob_check); + + return BT_MESH_DFD_SUCCESS; +} + +static void dfd_srv_cancel_oob_upload(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot) +{ + LOG_DBG("Cancel OOB Upload"); + + dfd_srv_oob_ctx.started = false; +} + +static uint8_t dfd_srv_oob_progress_get(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot) +{ + uint8_t progress; + + if (dfd_srv_oob_ctx.started) { + progress = dfd_srv_oob_ctx.progress; + + dfd_srv_oob_ctx.progress = MIN(dfd_srv_oob_ctx.progress + 25, 99); + + if (dfd_srv_oob_ctx.progress == 99) { + k_work_submit(&oob_store); + } + } else { + progress = 0; + } + + LOG_DBG("OOB Progress Get (%sstarted: %d %%)", dfd_srv_oob_ctx.started ? "" : "not ", + progress); + return progress; +} +#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */ + +static struct bt_mesh_dfd_srv_cb dfd_srv_cb = { + .recv = dfd_srv_recv, + .del = dfd_srv_del, + .send = dfd_srv_send, +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD + .start_oob_upload = dfd_srv_start_oob_upload, + .cancel_oob_upload = dfd_srv_cancel_oob_upload, + .oob_progress_get = dfd_srv_oob_progress_get, +#endif +}; + +static struct bt_mesh_dfd_srv dfd_srv = BT_MESH_DFD_SRV_INIT(&dfd_srv_cb); + +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD +#define SUPPORTED_SCHEME "http" + +static void oob_check_handler(struct k_work *work) +{ + uint8_t scheme[10]; + int i; + int status; + int err; + + for (i = 0; i < MIN(dfd_srv_oob_ctx.uri_len, sizeof(scheme)); i++) { + if (IN_RANGE(dfd_srv_oob_ctx.uri[i], 48, 57) || /* DIGIT */ + IN_RANGE(dfd_srv_oob_ctx.uri[i], 65, 90) || /* ALPHA UPPER CASE */ + IN_RANGE(dfd_srv_oob_ctx.uri[i], 97, 122) || /* ALPHA LOWER CASE */ + dfd_srv_oob_ctx.uri[i] == '.' || + dfd_srv_oob_ctx.uri[i] == '+' || + dfd_srv_oob_ctx.uri[i] == '-') { + scheme[i] = dfd_srv_oob_ctx.uri[i]; + } else { + break; + } + } + + if (i == dfd_srv_oob_ctx.uri_len || dfd_srv_oob_ctx.uri[i] != ':') { + status = BT_MESH_DFD_ERR_URI_MALFORMED; + } else if (i != strlen(SUPPORTED_SCHEME) || + memcmp(scheme, SUPPORTED_SCHEME, strlen(SUPPORTED_SCHEME))) { + status = BT_MESH_DFD_ERR_URI_NOT_SUPPORTED; + } else { + status = BT_MESH_DFD_SUCCESS; + } + + err = bt_mesh_dfd_srv_oob_check_complete(&dfd_srv, dfd_srv_oob_ctx.slot, status, + dfd_srv_oob_ctx.fwid, dfd_srv_oob_ctx.fwid_len); + LOG_DBG("OOB check completed (err %d)", err); +} + +static void oob_store_handler(struct k_work *work) +{ + int err; + + err = bt_mesh_dfd_srv_oob_store_complete(&dfd_srv, dfd_srv_oob_ctx.slot, true, + 10000, "metadata", 8); + LOG_DBG("OOB store completed (err %d)", err); +} +#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */ +#endif + +#if defined(CONFIG_BT_MESH_BLOB_CLI) && !defined(CONFIG_BT_MESH_DFD_SRV) +static struct { + struct bt_mesh_blob_cli_inputs inputs; + struct bt_mesh_blob_target targets[32]; + struct bt_mesh_blob_target_pull pull[32]; + uint8_t target_count; + struct bt_mesh_blob_xfer xfer; +} blob_cli_xfer; + +static void blob_cli_lost_target(struct bt_mesh_blob_cli *cli, + struct bt_mesh_blob_target *target, + enum bt_mesh_blob_status reason) +{ + LOG_DBG("Mesh Blob: Lost target 0x%04x (reason: %u)", target->addr, + reason); + tester_event(BTP_SERVICE_ID_MESH, MESH_EV_BLOB_LOST_TARGET, NULL, 0); +} + +static void blob_cli_caps(struct bt_mesh_blob_cli *cli, + const struct bt_mesh_blob_cli_caps *caps) +{ + const char *const modes[] = { + "none", + "push", + "pull", + "all", + }; + + if (!caps) { + LOG_DBG("None of the targets can be used for BLOB transfer"); + return; + } + + LOG_DBG("Mesh BLOB: capabilities:"); + LOG_DBG("\tMax BLOB size: %u bytes", caps->max_size); + LOG_DBG("\tBlock size: %u-%u (%u-%u bytes)", caps->min_block_size_log, + caps->max_block_size_log, 1 << caps->min_block_size_log, + 1 << caps->max_block_size_log); + LOG_DBG("\tMax chunks: %u", caps->max_chunks); + LOG_DBG("\tChunk size: %u", caps->max_chunk_size); + LOG_DBG("\tMTU size: %u", caps->mtu_size); + LOG_DBG("\tModes: %s", modes[caps->modes]); +} + +static void blob_cli_end(struct bt_mesh_blob_cli *cli, + const struct bt_mesh_blob_xfer *xfer, bool success) +{ + if (success) { + LOG_DBG("Mesh BLOB transfer complete."); + } else { + LOG_DBG("Mesh BLOB transfer failed."); + } +} + +static const struct bt_mesh_blob_cli_cb blob_cli_handlers = { + .lost_target = blob_cli_lost_target, + .caps = blob_cli_caps, + .end = blob_cli_end, +}; + +static struct bt_mesh_blob_cli blob_cli = { .cb = &blob_cli_handlers }; +#endif + +#if defined(CONFIG_BT_MESH_DFU_SRV) +const char *metadata_data = "1100000000000011"; + +static uint8_t dfu_fwid[CONFIG_BT_MESH_DFU_FWID_MAXLEN] = { + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static struct bt_mesh_dfu_img dfu_imgs[] = { { + .fwid = &dfu_fwid, + .fwid_len = sizeof(dfu_fwid), +} }; + +static int dfu_meta_check(struct bt_mesh_dfu_srv *srv, + const struct bt_mesh_dfu_img *img, + struct net_buf_simple *metadata, + enum bt_mesh_dfu_effect *effect) +{ + char string[2 * CONFIG_BT_MESH_DFU_METADATA_MAXLEN + 1]; + int i; + size_t len; + + len = bin2hex(metadata->data, metadata->len, string, sizeof(string)); + string[len] = '\0'; + + for (i = 0; i <= len; i++) { + if (string[i] != metadata_data[i]) { + LOG_ERR("Wrong Firmware Metadata"); + return -EINVAL; + } + } + + return 0; +} + +static int dfu_start(struct bt_mesh_dfu_srv *srv, + const struct bt_mesh_dfu_img *img, + struct net_buf_simple *metadata, + const struct bt_mesh_blob_io **io) +{ + LOG_DBG("DFU setup"); + + *io = &dummy_blob_io; + + return 0; +} + +static void dfu_end(struct bt_mesh_dfu_srv *srv, + const struct bt_mesh_dfu_img *img, bool success) +{ + if (!success) { + LOG_ERR("DFU failed"); + return; + } + + if (!blob_valid) { + bt_mesh_dfu_srv_rejected(srv); + return; + } + + bt_mesh_dfu_srv_verified(srv); +} + +static int dfu_apply(struct bt_mesh_dfu_srv *srv, + const struct bt_mesh_dfu_img *img) +{ + if (!blob_valid) { + return -EINVAL; + } + + LOG_DBG("Applying DFU transfer..."); + +#if defined(CONFIG_BT_MESH_DFD_SRV) + if (is_self_update(&dfd_srv) && dfu_self_update_slot != NULL) { + LOG_DBG("Swapping fwid for self-update"); + /* Simulate self-update by swapping fwid. */ + memcpy(&dfu_fwid[0], dfu_self_update_slot->fwid, dfu_self_update_slot->fwid_len); + dfu_imgs[0].fwid_len = dfu_self_update_slot->fwid_len; + } + +#endif + + return 0; +} + +static const struct bt_mesh_dfu_srv_cb dfu_handlers = { + .check = dfu_meta_check, + .start = dfu_start, + .end = dfu_end, + .apply = dfu_apply, +}; + +static struct bt_mesh_dfu_srv dfu_srv = + BT_MESH_DFU_SRV_INIT(&dfu_handlers, dfu_imgs, ARRAY_SIZE(dfu_imgs)); +#endif /* CONFIG_BT_MESH_DFU_SRV */ /* Model Authentication Method */ #define AUTH_METHOD_STATIC 0x01 @@ -73,7 +468,6 @@ static struct { .dst = BT_MESH_ADDR_UNASSIGNED, }; - static uint8_t supported_commands(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len) { @@ -254,15 +648,6 @@ static const struct bt_mesh_health_srv_cb health_srv_cb = { .fault_test = fault_test, }; -static struct bt_mesh_health_srv health_srv = { - .cb = &health_srv_cb, -}; - -BT_MESH_HEALTH_PUB_DEFINE(health_pub, CUR_FAULTS_MAX); - -static struct bt_mesh_cfg_cli cfg_cli = { -}; - static void show_faults(uint8_t test_id, uint16_t cid, uint8_t *faults, size_t fault_count) { size_t i; @@ -289,82 +674,494 @@ static void health_current_status(struct bt_mesh_health_cli *cli, uint16_t addr, show_faults(test_id, cid, faults, fault_count); } +#if defined(CONFIG_BT_MESH_LARGE_COMP_DATA_CLI) +static struct bt_mesh_large_comp_data_cli lcd_cli = { +}; +#endif + static struct bt_mesh_health_cli health_cli = { .current_status = health_current_status, }; -static struct bt_mesh_model root_models[] = { - BT_MESH_MODEL_CFG_SRV, - BT_MESH_MODEL_CFG_CLI(&cfg_cli), - BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub), - BT_MESH_MODEL_HEALTH_CLI(&health_cli), + +#ifdef CONFIG_BT_MESH_LARGE_COMP_DATA_SRV +static uint8_t health_tests[] = { + BT_MESH_HEALTH_TEST_INFO(COMPANY_ID_LF, 6, 0x01, 0x02, 0x03, 0x04, 0x34, + 0x15), + BT_MESH_HEALTH_TEST_INFO(COMPANY_ID_NORDIC_SEMI, 3, 0x01, 0x02, 0x03), }; -static struct bt_mesh_model vnd_models[] = { - BT_MESH_MODEL_VND(CID_LOCAL, VND_MODEL_ID_1, BT_MESH_MODEL_NO_OPS, NULL, - NULL), +static uint8_t zero_metadata[100]; + +static struct bt_mesh_models_metadata_entry health_srv_meta[] = { + BT_MESH_HEALTH_TEST_INFO_METADATA(health_tests), + { + .len = ARRAY_SIZE(zero_metadata), + .id = 0xABCD, + .data = zero_metadata, + }, + BT_MESH_MODELS_METADATA_END, }; -static struct bt_mesh_elem elements[] = { - BT_MESH_ELEM(0, root_models, vnd_models), +static uint8_t health_tests_alt[] = { + BT_MESH_HEALTH_TEST_INFO(COMPANY_ID_LF, 6, 0x11, 0x22, 0x33, 0x44, 0x55, + 0x66), + BT_MESH_HEALTH_TEST_INFO(COMPANY_ID_NORDIC_SEMI, 3, 0x11, 0x22, 0x33), }; -static void link_open(bt_mesh_prov_bearer_t bearer) -{ - struct btp_mesh_prov_link_open_ev ev; +static struct bt_mesh_models_metadata_entry health_srv_meta_alt[] = { + BT_MESH_HEALTH_TEST_INFO_METADATA(health_tests_alt), + { + .len = ARRAY_SIZE(zero_metadata), + .id = 0xFEED, + .data = zero_metadata, + }, + BT_MESH_MODELS_METADATA_END, +}; +#endif - LOG_DBG("bearer 0x%02x", bearer); +static struct bt_mesh_health_srv health_srv = { + .cb = &health_srv_cb, +#ifdef CONFIG_BT_MESH_LARGE_COMP_DATA_SRV + .metadata = health_srv_meta, +#endif +}; - switch (bearer) { - case BT_MESH_PROV_ADV: - ev.bearer = BTP_MESH_PROV_BEARER_PB_ADV; - break; - case BT_MESH_PROV_GATT: - ev.bearer = BTP_MESH_PROV_BEARER_PB_GATT; - break; - default: - LOG_ERR("Invalid bearer"); +BT_MESH_HEALTH_PUB_DEFINE(health_pub, CUR_FAULTS_MAX); - return; - } +static struct bt_mesh_cfg_cli cfg_cli = { +}; - tester_event(BTP_SERVICE_ID_MESH, BTP_MESH_EV_PROV_LINK_OPEN, &ev, sizeof(ev)); +#if defined(CONFIG_BT_MESH_SAR_CFG_CLI) +static struct bt_mesh_sar_cfg_cli sar_cfg_cli; +#endif + +#if defined(CONFIG_BT_MESH_RPR_CLI) +static void rpr_scan_report(struct bt_mesh_rpr_cli *cli, + const struct bt_mesh_rpr_node *srv, + struct bt_mesh_rpr_unprov *unprov, + struct net_buf_simple *adv_data) +{ + char uuid_hex_str[32 + 1]; + + bin2hex(unprov->uuid, 16, uuid_hex_str, sizeof(uuid_hex_str)); + + LOG_DBG("Server 0x%04x:\n" + "\tuuid: %s\n" + "\tOOB: 0x%04x", + srv->addr, uuid_hex_str, unprov->oob); + + while (adv_data && adv_data->len > 2) { + uint8_t len, type; + uint8_t data[31]; + + len = net_buf_simple_pull_u8(adv_data) - 1; + type = net_buf_simple_pull_u8(adv_data); + memcpy(data, net_buf_simple_pull_mem(adv_data, len), len); + data[len] = '\0'; + + if (type == BT_DATA_URI) { + LOG_DBG("\tURI: \"\\x%02x%s\"", + data[0], &data[1]); + } else if (type == BT_DATA_NAME_COMPLETE) { + LOG_DBG("\tName: \"%s\"", data); + } else { + char string[64 + 1]; + + bin2hex(data, len, string, sizeof(string)); + LOG_DBG("\t0x%02x: %s", type, string); + } + } } -static void link_close(bt_mesh_prov_bearer_t bearer) +static struct bt_mesh_rpr_cli rpr_cli = { + .scan_report = rpr_scan_report, +}; +#endif + +#if defined(CONFIG_BT_MESH_DFU_SRV) +static uint8_t dfu_srv_apply(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) { - struct btp_mesh_prov_link_closed_ev ev; + LOG_DBG("Applying image on server"); + bt_mesh_dfu_srv_applied(&dfu_srv); + return BTP_STATUS_SUCCESS; +} +#endif - LOG_DBG("bearer 0x%02x", bearer); +#ifdef CONFIG_BT_MESH_PRIV_BEACON_CLI +static struct bt_mesh_priv_beacon_cli priv_beacon_cli; - switch (bearer) { - case BT_MESH_PROV_ADV: - ev.bearer = BTP_MESH_PROV_BEARER_PB_ADV; - break; - case BT_MESH_PROV_GATT: - ev.bearer = BTP_MESH_PROV_BEARER_PB_GATT; - break; - default: - LOG_ERR("Invalid bearer"); +static uint8_t priv_beacon_get(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + const struct btp_priv_beacon_get_cmd *cp = cmd; - return; + struct bt_mesh_priv_beacon val; + int err; + + err = bt_mesh_priv_beacon_cli_get(net.net_idx, cp->dst, &val); + if (err) { + LOG_ERR("Failed to send Private Beacon Get (err %d)", err); + return BTP_STATUS_FAILED; } - tester_event(BTP_SERVICE_ID_MESH, BTP_MESH_EV_PROV_LINK_CLOSED, &ev, sizeof(ev)); + LOG_DBG("Private Beacon state: %u, %u", val.enabled, val.rand_interval); + return BTP_STATUS_SUCCESS; } -static int output_number(bt_mesh_output_action_t action, uint32_t number) +static uint8_t priv_beacon_set(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) { - struct btp_mesh_out_number_action_ev ev; - - LOG_DBG("action 0x%04x number 0x%08x", action, number); + const struct btp_priv_beacon_set_cmd *cp = cmd; + struct bt_mesh_priv_beacon val; + int err; - ev.action = sys_cpu_to_le16(action); - ev.number = sys_cpu_to_le32(number); + val.enabled = cp->enabled; + val.rand_interval = cp->rand_interval; - tester_event(BTP_SERVICE_ID_MESH, BTP_MESH_EV_OUT_NUMBER_ACTION, &ev, sizeof(ev)); + err = bt_mesh_priv_beacon_cli_set(net.net_idx, cp->dst, &val); + if (err) { + LOG_ERR("Failed to send Private Beacon Set (err %d)", err); + return BTP_STATUS_FAILED; + } - return 0; + return BTP_STATUS_SUCCESS; +} + +static uint8_t priv_gatt_proxy_get(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + const struct btp_priv_gatt_proxy_get_cmd *cp = cmd; + + uint8_t state; + int err; + + err = bt_mesh_priv_beacon_cli_gatt_proxy_get(net.net_idx, cp->dst, &state); + if (err) { + LOG_ERR("Failed to send Private GATT Proxy Get (err %d)", err); + return BTP_STATUS_FAILED; + } + + LOG_DBG("Private GATT Proxy state: %u", state); + return BTP_STATUS_SUCCESS; +} + +static uint8_t priv_gatt_proxy_set(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + const struct btp_priv_gatt_proxy_set_cmd *cp = cmd; + + uint8_t state; + int err; + + state = cp->state; + + err = bt_mesh_priv_beacon_cli_gatt_proxy_set(net.net_idx, cp->dst, &state); + if (err) { + LOG_ERR("Failed to send Private GATT Proxy Set (err %d)", err); + return BTP_STATUS_FAILED; + } + + return BTP_STATUS_SUCCESS; +} + +static uint8_t priv_node_id_get(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + const struct btp_priv_node_id_get_cmd *cp = cmd; + struct bt_mesh_priv_node_id val; + uint16_t key_net_idx; + int err; + + key_net_idx = cp->key_net_idx; + + err = bt_mesh_priv_beacon_cli_node_id_get(net.net_idx, cp->dst, key_net_idx, &val); + if (err) { + LOG_ERR("Failed to send Private Node Identity Get (err %d)", err); + return BTP_STATUS_FAILED; + } + + LOG_DBG("Private Node Identity state: %u %u %u", val.net_idx, val.state, val.status); + return BTP_STATUS_SUCCESS; +} + +static uint8_t priv_node_id_set(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + const struct btp_priv_node_id_set_cmd *cp = cmd; + struct bt_mesh_priv_node_id val; + int err; + + val.net_idx = cp->net_idx; + val.state = cp->state; + + err = bt_mesh_priv_beacon_cli_node_id_set(net.net_idx, cp->dst, &val); + if (err) { + LOG_ERR("Failed to send Private Node Identity Set (err %d)", err); + return BTP_STATUS_FAILED; + } + + return BTP_STATUS_SUCCESS; +} + +static uint8_t proxy_private_identity_enable(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + int err; + + LOG_DBG(""); + + err = bt_mesh_proxy_private_identity_enable(); + if (err) { + LOG_ERR("Failed to enable proxy private identity (err %d)", err); + return BTP_STATUS_FAILED; + } + + return BTP_STATUS_SUCCESS; +} +#endif + +#if defined(CONFIG_BT_MESH_SOL_PDU_RPL_CLI) +static struct bt_mesh_sol_pdu_rpl_cli srpl_cli; +#endif + + +#if defined(CONFIG_BT_MESH_OD_PRIV_PROXY_CLI) +static struct bt_mesh_od_priv_proxy_cli od_priv_proxy_cli; + +static uint8_t od_priv_proxy_get(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + const struct btp_od_priv_proxy_get_cmd *cp = cmd; + uint8_t val_rsp; + int err; + + LOG_DBG(""); + + err = bt_mesh_od_priv_proxy_cli_get(net.net_idx, cp->dst, &val_rsp); + if (err) { + LOG_ERR("Failed to get On-Demand Private Proxy state (err %d)", err); + return BTP_STATUS_FAILED; + } + + LOG_DBG("On-Demand Private Proxy state: %u", val_rsp); + + return BTP_STATUS_SUCCESS; +} + +static uint8_t od_priv_proxy_set(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + const struct btp_od_priv_proxy_set_cmd *cp = cmd; + uint8_t val_rsp; + int err; + + LOG_DBG(""); + + err = bt_mesh_od_priv_proxy_cli_set(net.net_idx, cp->dst, cp->val, &val_rsp); + if (err) { + LOG_ERR("Failed to set On-Demand Private Proxy state (err %d)", err); + return BTP_STATUS_FAILED; + } + + LOG_DBG("On-Demand Private Proxy set state: %u", val_rsp); + + return BTP_STATUS_SUCCESS; +} + +#endif + +#if defined(CONFIG_BT_MESH_SOL_PDU_RPL_CLI) +static uint8_t srpl_clear(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + const struct btp_srpl_clear_cmd *cp = cmd; + uint16_t app_idx = BT_MESH_KEY_UNUSED; + uint16_t start_rsp; + uint8_t len_rsp; + int err; + + /* Lookup source address */ + for (int i = 0; i < ARRAY_SIZE(model_bound); i++) { + if (model_bound[i].model->id == BT_MESH_MODEL_ID_SOL_PDU_RPL_CLI) { + app_idx = model_bound[i].appkey_idx; + break; + } + } + + struct bt_mesh_msg_ctx ctx = BT_MESH_MSG_CTX_INIT_APP(app_idx, cp->dst); + + if (cp->acked) { + err = bt_mesh_sol_pdu_rpl_clear(&ctx, cp->range_start, cp->range_len, &start_rsp, + &len_rsp); + } else { + err = bt_mesh_sol_pdu_rpl_clear_unack(&ctx, cp->range_start, cp->range_len); + } + if (err) { + LOG_ERR("Failed to clear SRPL (err %d)", err); + return BTP_STATUS_FAILED; + } + + return BTP_STATUS_SUCCESS; +} +#endif + +#if defined(CONFIG_BT_MESH_PROXY_SOLICITATION) +static uint8_t proxy_solicit(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + const struct btp_proxy_solicit_cmd *cp = cmd; + int err; + + err = bt_mesh_proxy_solicit(cp->net_idx); + if (err) { + LOG_ERR("Failed to advertise solicitation PDU (err %d)", err); + return BTP_STATUS_FAILED; + } + + return BTP_STATUS_SUCCESS; +} +#endif /* CONFIG_BT_MESH_PROXY_SOLICITATION */ + +static struct bt_mesh_model root_models[] = { + BT_MESH_MODEL_CFG_SRV, + BT_MESH_MODEL_CFG_CLI(&cfg_cli), + BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub), + BT_MESH_MODEL_HEALTH_CLI(&health_cli), +#if defined(CONFIG_BT_MESH_SAR_CFG_SRV) + BT_MESH_MODEL_SAR_CFG_SRV, +#endif +#if defined(CONFIG_BT_MESH_SAR_CFG_CLI) + BT_MESH_MODEL_SAR_CFG_CLI(&sar_cfg_cli), +#endif +#if defined(CONFIG_BT_MESH_LARGE_COMP_DATA_SRV) + BT_MESH_MODEL_LARGE_COMP_DATA_SRV, +#endif +#if defined(CONFIG_BT_MESH_LARGE_COMP_DATA_CLI) + BT_MESH_MODEL_LARGE_COMP_DATA_CLI(&lcd_cli), +#endif +#if defined(CONFIG_BT_MESH_OP_AGG_SRV) + BT_MESH_MODEL_OP_AGG_SRV, +#endif +#if defined(CONFIG_BT_MESH_OP_AGG_CLI) + BT_MESH_MODEL_OP_AGG_CLI, +#endif +#if defined(CONFIG_BT_MESH_RPR_CLI) + BT_MESH_MODEL_RPR_CLI(&rpr_cli), +#endif +#if defined(CONFIG_BT_MESH_RPR_SRV) + BT_MESH_MODEL_RPR_SRV, +#endif +#if defined(CONFIG_BT_MESH_DFD_SRV) + BT_MESH_MODEL_DFD_SRV(&dfd_srv), +#endif +#if defined(CONFIG_BT_MESH_DFU_SRV) + BT_MESH_MODEL_DFU_SRV(&dfu_srv), +#endif +#if defined(CONFIG_BT_MESH_BLOB_CLI) && !defined(CONFIG_BT_MESH_DFD_SRV) + BT_MESH_MODEL_BLOB_CLI(&blob_cli), +#endif +#if defined(CONFIG_BT_MESH_PRIV_BEACON_SRV) + BT_MESH_MODEL_PRIV_BEACON_SRV, +#endif +#if defined(CONFIG_BT_MESH_PRIV_BEACON_CLI) + BT_MESH_MODEL_PRIV_BEACON_CLI(&priv_beacon_cli), +#endif +#if defined(CONFIG_BT_MESH_OD_PRIV_PROXY_CLI) + BT_MESH_MODEL_OD_PRIV_PROXY_CLI(&od_priv_proxy_cli), +#endif +#if defined(CONFIG_BT_MESH_SOL_PDU_RPL_CLI) + BT_MESH_MODEL_SOL_PDU_RPL_CLI(&srpl_cli), +#endif +#if defined(CONFIG_BT_MESH_OD_PRIV_PROXY_SRV) + BT_MESH_MODEL_OD_PRIV_PROXY_SRV, +#endif + +}; +struct model_data *lookup_model_bound(uint16_t id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(model_bound); i++) { + if (model_bound[i].model && model_bound[i].model->id == id) { + return &model_bound[i]; + } + } + + return NULL; +} +static struct bt_mesh_model vnd_models[] = { + BT_MESH_MODEL_VND(CID_LOCAL, VND_MODEL_ID_1, BT_MESH_MODEL_NO_OPS, NULL, + NULL), +}; + +static struct bt_mesh_elem elements[] = { + BT_MESH_ELEM(0, root_models, vnd_models), +}; + +static void link_open(bt_mesh_prov_bearer_t bearer) +{ + struct btp_mesh_prov_link_open_ev ev; + + LOG_DBG("bearer 0x%02x", bearer); + + switch (bearer) { + case BT_MESH_PROV_ADV: + ev.bearer = BTP_MESH_PROV_BEARER_PB_ADV; + break; + case BT_MESH_PROV_GATT: + ev.bearer = BTP_MESH_PROV_BEARER_PB_GATT; + break; + case BT_MESH_PROV_REMOTE: + ev.bearer = BTP_MESH_PROV_BEARER_REMOTE; + break; + default: + LOG_ERR("Invalid bearer"); + + return; + } + + tester_event(BTP_SERVICE_ID_MESH, BTP_MESH_EV_PROV_LINK_OPEN, &ev, sizeof(ev)); +} + +static void link_close(bt_mesh_prov_bearer_t bearer) +{ + struct btp_mesh_prov_link_closed_ev ev; + + LOG_DBG("bearer 0x%02x", bearer); + + switch (bearer) { + case BT_MESH_PROV_ADV: + ev.bearer = BTP_MESH_PROV_BEARER_PB_ADV; + break; + case BT_MESH_PROV_GATT: + ev.bearer = BTP_MESH_PROV_BEARER_PB_GATT; + break; + case BT_MESH_PROV_REMOTE: + ev.bearer = BTP_MESH_PROV_BEARER_REMOTE; + break; + default: + LOG_ERR("Invalid bearer"); + + return; + } + + tester_event(BTP_SERVICE_ID_MESH, BTP_MESH_EV_PROV_LINK_CLOSED, &ev, sizeof(ev)); +} + +static int output_number(bt_mesh_output_action_t action, uint32_t number) +{ + struct btp_mesh_out_number_action_ev ev; + + LOG_DBG("action 0x%04x number 0x%08x", action, number); + + ev.action = sys_cpu_to_le16(action); + ev.number = sys_cpu_to_le32(number); + + tester_event(BTP_SERVICE_ID_MESH, BTP_MESH_EV_OUT_NUMBER_ACTION, &ev, sizeof(ev)); + + return 0; } static int output_string(const char *str) @@ -435,14 +1232,42 @@ static void prov_reset(void) LOG_DBG(""); bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT); + + if (IS_ENABLED(CONFIG_BT_MESH_RPR_SRV)) { + bt_mesh_prov_enable(BT_MESH_PROV_REMOTE); + } } static const struct bt_mesh_comp comp = { .cid = CID_LOCAL, .elem = elements, .elem_count = ARRAY_SIZE(elements), + .vid = 1, +}; + +static const struct bt_mesh_comp comp_alt = { + .cid = CID_LOCAL, + .elem = elements, + .elem_count = ARRAY_SIZE(elements), + .vid = 2, +}; + +#if defined(CONFIG_BT_MESH_COMP_PAGE_2) +static const uint8_t cmp2_elem_offset[1] = {0}; + +static const struct bt_mesh_comp2_record comp_rec = { + .id = 0x1600, + .version.x = 1, + .version.y = 0, + .version.z = 0, + .elem_offset_cnt = 1, + .elem_offset = cmp2_elem_offset, + .data_len = 0 }; +static const struct bt_mesh_comp2 comp_p2 = {.record_cnt = 1, .record = &comp_rec}; +#endif + static struct bt_mesh_prov prov = { .uuid = dev_uuid, .static_val = static_auth, @@ -455,6 +1280,7 @@ static struct bt_mesh_prov prov = { .complete = prov_complete, .node_added = prov_node_added, .reset = prov_reset, + .uri = "Tester", }; static uint8_t config_prov(const void *cmd, uint16_t cmd_len, @@ -466,13 +1292,21 @@ static uint8_t config_prov(const void *cmd, uint16_t cmd_len, /* TODO consider fix BTP commands to avoid this */ if (cmd_len != sizeof(*cp) && cmd_len != (sizeof(*cp2))) { + LOG_DBG("wrong cmd size"); return BTP_STATUS_FAILED; } LOG_DBG(""); + static_auth_size = cp->static_auth_size; + + if (static_auth_size > BTP_MESH_PROV_AUTH_MAX_LEN || static_auth_size == 0) { + LOG_DBG("wrong static auth length"); + return BTP_STATUS_FAILED; + } + memcpy(dev_uuid, cp->uuid, sizeof(dev_uuid)); - memcpy(static_auth, cp->static_auth, sizeof(static_auth)); + memcpy(static_auth, cp->static_auth, cp->static_auth_size); prov.output_size = cp->out_size; prov.output_actions = sys_le16_to_cpu(cp->out_actions); @@ -491,7 +1325,7 @@ static uint8_t config_prov(const void *cmd, uint16_t cmd_len, } else if (cp->auth_method == AUTH_METHOD_INPUT) { err = bt_mesh_auth_method_set_input(prov.input_actions, prov.input_size); } else if (cp->auth_method == AUTH_METHOD_STATIC) { - err = bt_mesh_auth_method_set_static(static_auth, sizeof(static_auth)); + err = bt_mesh_auth_method_set_static(static_auth, static_auth_size); } if (err) { @@ -572,24 +1406,55 @@ static uint8_t provision_adv(const void *cmd, uint16_t cmd_len, static uint8_t init(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len) { + const struct btp_mesh_init_cmd *cp = cmd; int err; - LOG_DBG(""); + if (cp->comp == 0) { + LOG_WRN("Loading default comp data"); + err = bt_mesh_init(&prov, &comp); + } else { + LOG_WRN("Loading alternative comp data"); +#ifdef CONFIG_BT_MESH_LARGE_COMP_DATA_SRV + health_srv.metadata = health_srv_meta_alt; +#endif + err = bt_mesh_init(&prov, &comp_alt); + } - err = bt_mesh_init(&prov, &comp); if (err) { return BTP_STATUS_FAILED; } - if (addr) { - err = bt_mesh_provision(net_key, net_key_idx, flags, iv_index, - addr, dev_key); - if (err) { - return BTP_STATUS_FAILED; - } - } else { - err = bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT); - if (err) { + return BTP_STATUS_SUCCESS; +} + +static uint8_t start(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + int err; + + LOG_DBG(""); + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + printk("Loading stored settings\n"); + settings_load(); + } + + if (addr) { + err = bt_mesh_provision(net_key, net_key_idx, flags, iv_index, + addr, dev_key); + if (err && err != -EALREADY) { + return BTP_STATUS_FAILED; + } + } else { + err = bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT); + if (err && err != -EALREADY) { + return BTP_STATUS_FAILED; + } + } + + if (IS_ENABLED(CONFIG_BT_MESH_RPR_SRV)) { + err = bt_mesh_prov_enable(BT_MESH_PROV_REMOTE); + if (err) { return BTP_STATUS_FAILED; } } @@ -688,6 +1553,7 @@ static uint8_t ivu_toggle_state(const void *cmd, uint16_t cmd_len, return BTP_STATUS_SUCCESS; } +#if defined(CONFIG_BT_MESH_LOW_POWER) static uint8_t lpn(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len) { @@ -719,6 +1585,7 @@ static uint8_t lpn_poll(const void *cmd, uint16_t cmd_len, return BTP_STATUS_SUCCESS; } +#endif /* CONFIG_BT_MESH_LOW_POWER */ static uint8_t net_send(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len) @@ -861,7 +1728,7 @@ static uint8_t model_send(const void *cmd, uint16_t cmd_len, .net_idx = net.net_idx, .app_idx = BT_MESH_KEY_DEV, .addr = sys_le16_to_cpu(cp->dst), - .send_ttl = BT_MESH_TTL_DEFAULT, + .send_ttl = cp->ttl, }; if (BT_MESH_ADDR_IS_VIRTUAL(ctx.addr)) { @@ -900,6 +1767,7 @@ static uint8_t model_send(const void *cmd, uint16_t cmd_len, } #if defined(CONFIG_BT_TESTING) +#if defined(CONFIG_BT_MESH_LOW_POWER) static uint8_t lpn_subscribe(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len) { @@ -935,6 +1803,7 @@ static uint8_t lpn_unsubscribe(const void *cmd, uint16_t cmd_len, return BTP_STATUS_SUCCESS; } +#endif /* CONFIG_BT_MESH_LOW_POWER */ static uint8_t rpl_clear(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len) @@ -986,6 +1855,165 @@ static uint8_t proxy_connect(const void *cmd, uint16_t cmd_len, } #endif +#if defined(CONFIG_BT_MESH_SAR_CFG_CLI) +static uint8_t sar_transmitter_get(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + const struct btp_mesh_sar_transmitter_get_cmd *cp = cmd; + struct bt_mesh_sar_tx tx_rsp; + int err; + + LOG_DBG(""); + + bt_mesh_sar_cfg_cli_timeout_set(5000); + + err = bt_mesh_sar_cfg_cli_transmitter_get( + net_key_idx, sys_le16_to_cpu(cp->dst), &tx_rsp); + if (err) { + LOG_ERR("err=%d", err); + return BTP_STATUS_FAILED; + } + + return BTP_STATUS_SUCCESS; +} + +static uint8_t sar_transmitter_set(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + const struct btp_mesh_sar_transmitter_set_cmd *cp = cmd; + struct bt_mesh_sar_tx set, tx_rsp; + int err; + + LOG_DBG(""); + + bt_mesh_sar_cfg_cli_timeout_set(5000); + + set.seg_int_step = cp->tx.seg_int_step; + set.unicast_retrans_count = cp->tx.unicast_retrans_count; + set.unicast_retrans_int_inc = cp->tx.unicast_retrans_int_inc; + set.unicast_retrans_int_step = cp->tx.unicast_retrans_int_step; + set.unicast_retrans_without_prog_count = + cp->tx.unicast_retrans_without_prog_count; + set.multicast_retrans_count = cp->tx.multicast_retrans_count; + set.multicast_retrans_int = cp->tx.multicast_retrans_int; + + err = bt_mesh_sar_cfg_cli_transmitter_set(net_key_idx, + sys_le16_to_cpu(cp->dst), + &set, &tx_rsp); + if (err) { + LOG_ERR("err=%d", err); + return BTP_STATUS_FAILED; + } + + return BTP_STATUS_SUCCESS; +} + +static uint8_t sar_receiver_get(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + const struct btp_mesh_sar_receiver_get_cmd *cp = cmd; + struct bt_mesh_sar_rx rx_rsp; + int err; + + LOG_DBG(""); + + err = bt_mesh_sar_cfg_cli_receiver_get(net_key_idx, + sys_le16_to_cpu(cp->dst), &rx_rsp); + if (err) { + LOG_ERR("err=%d", err); + return BTP_STATUS_FAILED; + } + + return BTP_STATUS_SUCCESS; +} + +static uint8_t sar_receiver_set(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + const struct btp_mesh_sar_receiver_set_cmd *cp = cmd; + struct bt_mesh_sar_rx set, rx_rsp; + int err; + + LOG_DBG(""); + + set.ack_delay_inc = cp->rx.ack_delay_inc; + set.ack_retrans_count = cp->rx.ack_retrans_count; + set.discard_timeout = cp->rx.discard_timeout; + set.seg_thresh = cp->rx.seg_thresh; + set.rx_seg_int_step = cp->rx.rx_seg_int_step; + + err = bt_mesh_sar_cfg_cli_receiver_set(net_key_idx, + sys_le16_to_cpu(cp->dst), &set, + &rx_rsp); + if (err) { + LOG_ERR("err=%d", err); + return BTP_STATUS_FAILED; + } + + return BTP_STATUS_SUCCESS; +} +#endif + +#if defined(CONFIG_BT_MESH_LARGE_COMP_DATA_CLI) +static uint8_t large_comp_data_get(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + const struct btp_mesh_large_comp_data_get_cmd *cp = cmd; + struct btp_mesh_large_comp_data_get_rp *rp = rsp; + int err; + + NET_BUF_SIMPLE_DEFINE(data, 500); + net_buf_simple_init(&data, 0); + + struct bt_mesh_large_comp_data_rsp comp = { + .data = &data, + }; + + err = bt_mesh_large_comp_data_get(sys_le16_to_cpu(cp->net_idx), + sys_le16_to_cpu(cp->addr), cp->page, + sys_le16_to_cpu(cp->offset), &comp); + if (err) { + LOG_ERR("Large Composition Data Get failed (err %d)", err); + + return BTP_STATUS_FAILED; + } + + memcpy(rp->data, comp.data->data, comp.data->len); + *rsp_len = comp.data->len; + + return BTP_STATUS_SUCCESS; +} + +static uint8_t models_metadata_get(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + const struct btp_mesh_models_metadata_get_cmd *cp = cmd; + struct btp_mesh_models_metadata_get_rp *rp = rsp; + int err; + + NET_BUF_SIMPLE_DEFINE(data, 500); + net_buf_simple_init(&data, 0); + + struct bt_mesh_large_comp_data_rsp metadata = { + .data = &data, + }; + + err = bt_mesh_models_metadata_get(sys_le16_to_cpu(cp->net_idx), + sys_le16_to_cpu(cp->addr), cp->page, + sys_le16_to_cpu(cp->offset), &metadata); + + if (err) { + LOG_ERR("Models Metadata Get failed (err %d)", err); + return BTP_STATUS_FAILED; + } + + memcpy(rp->data, metadata.data->data, metadata.data->len); + *rsp_len = metadata.data->len; + + return BTP_STATUS_SUCCESS; +} +#endif + static uint8_t composition_data_get(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len) { @@ -1009,8 +2037,31 @@ static uint8_t composition_data_get(const void *cmd, uint16_t cmd_len, return BTP_STATUS_FAILED; } - memcpy(rp->data, comp->data, comp->len); - *rsp_len = comp->len; + rp->data[0] = page; + memcpy(rp->data + 1, comp->data, comp->len); + *rsp_len = comp->len + 1; + + return BTP_STATUS_SUCCESS; +} + +static uint8_t change_prepare(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + int err; + + LOG_DBG(""); + + err = bt_mesh_comp_change_prepare(); + if (err < 0) { + return BTP_STATUS_FAILED; + } + +#if CONFIG_BT_MESH_LARGE_COMP_DATA_SRV + err = bt_mesh_models_metadata_change_prepare(); + if (err < 0) { + return BTP_STATUS_FAILED; + } +#endif return BTP_STATUS_SUCCESS; } @@ -1898,6 +2949,8 @@ static uint8_t config_model_app_bind(const void *cmd, uint16_t cmd_len, LOG_DBG(""); + bt_mesh_cfg_cli_timeout_set(5000); + err = bt_mesh_cfg_cli_mod_app_bind(sys_le16_to_cpu(cp->net_idx), sys_le16_to_cpu(cp->address), sys_le16_to_cpu(cp->elem_address), @@ -2341,7 +3394,7 @@ static uint8_t health_fault_clear(const void *cmd, uint16_t cmd_len, .addr = sys_le16_to_cpu(cp->address), .app_idx = sys_le16_to_cpu(cp->app_idx), }; - uint8_t test_id; + uint8_t test_id = 0; size_t fault_count = 16; uint8_t faults[fault_count]; int err; @@ -2351,7 +3404,20 @@ static uint8_t health_fault_clear(const void *cmd, uint16_t cmd_len, if (cp->ack) { err = bt_mesh_health_cli_fault_clear(&health_cli, &ctx, sys_le16_to_cpu(cp->cid), - &test_id, faults, +#if defined(CONFIG_BT_MESH_OP_AGG_CLI) + bt_mesh_op_agg_cli_seq_is_started() ? + NULL : +#endif + &test_id, +#if defined(CONFIG_BT_MESH_OP_AGG_CLI) + bt_mesh_op_agg_cli_seq_is_started() ? + NULL : +#endif + faults, +#if defined(CONFIG_BT_MESH_OP_AGG_CLI) + bt_mesh_op_agg_cli_seq_is_started() ? + NULL : +#endif &fault_count); } else { err = bt_mesh_health_cli_fault_clear_unack(&health_cli, &ctx, @@ -2384,161 +3450,1091 @@ static uint8_t health_fault_test(const void *cmd, uint16_t cmd_len, }; size_t fault_count = 16; uint8_t faults[fault_count]; - uint8_t test_id; - uint16_t cid; int err; LOG_DBG(""); - test_id = cp->test_id; - cid = sys_le16_to_cpu(cp->cid); + if (cp->ack) { + err = bt_mesh_health_cli_fault_test(&health_cli, &ctx, + sys_le16_to_cpu(cp->cid), +#if defined(CONFIG_BT_MESH_OP_AGG_CLI) + bt_mesh_op_agg_cli_seq_is_started() ? + 0 : +#endif + cp->test_id, +#if defined(CONFIG_BT_MESH_OP_AGG_CLI) + bt_mesh_op_agg_cli_seq_is_started() ? + NULL : +#endif + faults, +#if defined(CONFIG_BT_MESH_OP_AGG_CLI) + bt_mesh_op_agg_cli_seq_is_started() ? + NULL : +#endif + &fault_count); +#if defined(CONFIG_BT_MESH_OP_AGG_CLI) + if (bt_mesh_op_agg_cli_seq_is_started()) { + fault_count = 0; + } +#endif + } else { + err = bt_mesh_health_cli_fault_test_unack(&health_cli, &ctx, + sys_le16_to_cpu(cp->cid), + cp->test_id); + } + + if (err) { + LOG_ERR("err %d", err); + return BTP_STATUS_FAILED; + } + + if (cp->ack) { + struct btp_mesh_health_fault_test_rp *rp = rsp; + + rp->test_id = cp->test_id; + rp->cid = cp->cid; + (void)memcpy(rp->faults, faults, fault_count); + + *rsp_len = sizeof(*rp) + fault_count; + } + + return BTP_STATUS_SUCCESS; +} + +static uint8_t health_period_get(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + const struct btp_mesh_health_period_get_cmd *cp = cmd; + struct bt_mesh_msg_ctx ctx = { + .net_idx = net.net_idx, + .addr = sys_le16_to_cpu(cp->address), + .app_idx = sys_le16_to_cpu(cp->app_idx), + }; + uint8_t divisor; + int err; + + LOG_DBG(""); + + err = bt_mesh_health_cli_period_get(&health_cli, &ctx, &divisor); + + if (err) { + LOG_ERR("err %d", err); + return BTP_STATUS_FAILED; + } + + return BTP_STATUS_SUCCESS; +} + +static uint8_t health_period_set(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + const struct btp_mesh_health_period_set_cmd *cp = cmd; + struct bt_mesh_msg_ctx ctx = { + .net_idx = net.net_idx, + .addr = sys_le16_to_cpu(cp->address), + .app_idx = sys_le16_to_cpu(cp->app_idx), + }; + uint8_t updated_divisor; + int err; + + LOG_DBG(""); + + if (cp->ack) { + err = bt_mesh_health_cli_period_set(&health_cli, &ctx, cp->divisor, +#if defined(CONFIG_BT_MESH_OP_AGG_CLI) + bt_mesh_op_agg_cli_seq_is_started() ? + NULL : +#endif + &updated_divisor); + } else { + err = bt_mesh_health_cli_period_set_unack(&health_cli, &ctx, cp->divisor); + } + + if (err) { + LOG_ERR("err %d", err); + return BTP_STATUS_FAILED; + } + + if (cp->ack) { + struct btp_mesh_health_period_set_rp *rp = rsp; + + rp->divisor = updated_divisor; + + *rsp_len = sizeof(*rp); + } + + return BTP_STATUS_SUCCESS; +} + +static uint8_t health_attention_get(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + const struct btp_mesh_health_attention_get_cmd *cp = cmd; + struct bt_mesh_msg_ctx ctx = { + .net_idx = net.net_idx, + .addr = sys_le16_to_cpu(cp->address), + .app_idx = sys_le16_to_cpu(cp->app_idx), + }; + uint8_t attention; + int err; + + LOG_DBG(""); + + err = bt_mesh_health_cli_attention_get(&health_cli, &ctx, &attention); + + if (err) { + LOG_ERR("err %d", err); + return BTP_STATUS_FAILED; + } + + return BTP_STATUS_SUCCESS; +} + +static uint8_t health_attention_set(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + const struct btp_mesh_health_attention_set_cmd *cp = cmd; + struct bt_mesh_msg_ctx ctx = { + .net_idx = net.net_idx, + .addr = sys_le16_to_cpu(cp->address), + .app_idx = sys_le16_to_cpu(cp->app_idx), + }; + uint8_t updated_attention; + int err; + + LOG_DBG(""); + + if (cp->ack) { + err = bt_mesh_health_cli_attention_set(&health_cli, &ctx, cp->attention, +#if defined(CONFIG_BT_MESH_OP_AGG_CLI) + bt_mesh_op_agg_cli_seq_is_started() ? + NULL : +#endif + &updated_attention); + } else { + err = bt_mesh_health_cli_attention_set_unack(&health_cli, &ctx, cp->attention); + } + + if (err) { + LOG_ERR("err %d", err); + return BTP_STATUS_FAILED; + } + + if (cp->ack) { + struct btp_mesh_health_attention_set_rp *rp = rsp; + + rp->attention = updated_attention; + + *rsp_len = sizeof(*rp); + } + + return BTP_STATUS_SUCCESS; +} + +#if defined(CONFIG_BT_MESH_OP_AGG_CLI) +static uint8_t opcodes_aggregator_init(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + const struct btp_mesh_opcodes_aggregator_init_cmd *cp = cmd; + int err; + + LOG_DBG(""); + + err = bt_mesh_op_agg_cli_seq_start(cp->net_idx, cp->app_idx, cp->dst, cp->elem_addr); + if (err) { + LOG_ERR("Failed to init Opcodes Aggregator Context (err %d)", err); + return BTP_STATUS_FAILED; + } + + return BTP_STATUS_SUCCESS; +} + +static uint8_t opcodes_aggregator_send(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + int err; + + LOG_DBG(""); + + err = bt_mesh_op_agg_cli_seq_send(); + if (err) { + LOG_ERR("Failed to send Opcodes Aggregator message (err %d)", err); + return BTP_STATUS_FAILED; + } + + return BTP_STATUS_SUCCESS; +} +#endif + +#if defined(CONFIG_BT_MESH_RPR_CLI) +static uint8_t rpr_scan_start(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + const struct btp_rpr_scan_start_cmd *cp = cmd; + + struct bt_mesh_rpr_scan_status status; + const struct bt_mesh_rpr_node srv = { + .addr = cp->dst, + .net_idx = net.net_idx, + .ttl = BT_MESH_TTL_DEFAULT, + }; + uint8_t uuid[16] = {0}; + int err; + + err = bt_mesh_rpr_scan_start(&rpr_cli, &srv, + memcmp(uuid, cp->uuid, 16) ? cp->uuid : NULL, + cp->timeout, + BT_MESH_RPR_SCAN_MAX_DEVS_ANY, &status); + + if (err) { + LOG_ERR("Scan start failed: %d", err); + return BTP_STATUS_FAILED; + } + + return BTP_STATUS_SUCCESS; +} + +static uint8_t rpr_ext_scan_start(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + const struct btp_rpr_ext_scan_start_cmd *cp = cmd; + const struct bt_mesh_rpr_node srv = { + .addr = cp->dst, + .net_idx = net.net_idx, + .ttl = BT_MESH_TTL_DEFAULT, + }; + int err; + + err = bt_mesh_rpr_scan_start_ext(&rpr_cli, &srv, cp->uuid, + cp->timeout, cp->ad_types, + cp->ad_count); + if (err) { + LOG_ERR("Scan start failed: %d", err); + return BTP_STATUS_FAILED; + } + + return BTP_STATUS_SUCCESS; +} + +static uint8_t rpr_scan_caps_get(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + const struct btp_rpr_scan_caps_get_cmd *cp = cmd; + struct bt_mesh_rpr_caps caps; + const struct bt_mesh_rpr_node srv = { + .addr = cp->dst, + .net_idx = net.net_idx, + .ttl = BT_MESH_TTL_DEFAULT, + }; + int err; + + err = bt_mesh_rpr_scan_caps_get(&rpr_cli, &srv, &caps); + if (err) { + LOG_ERR("Scan capabilities get failed: %d", err); + return BTP_STATUS_FAILED; + } + + LOG_DBG("Remote Provisioning scan capabilities of 0x%04x:", + net.dst); + LOG_DBG("\tMax devices: %u", caps.max_devs); + LOG_DBG("\tActive scanning: %s", + caps.active_scan ? "true" : "false"); + + return BTP_STATUS_SUCCESS; +} + +static uint8_t rpr_scan_get(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + const struct btp_rpr_scan_get_cmd *cp = cmd; + struct bt_mesh_rpr_scan_status status; + const struct bt_mesh_rpr_node srv = { + .addr = cp->dst, + .net_idx = net.net_idx, + .ttl = BT_MESH_TTL_DEFAULT, + }; + int err; + + err = bt_mesh_rpr_scan_get(&rpr_cli, &srv, &status); + if (err) { + LOG_ERR("Scan get failed: %d", err); + return BTP_STATUS_FAILED; + } + + LOG_DBG("Remote Provisioning scan on 0x%04x:", cp->dst); + LOG_DBG("\tStatus: %u", status.status); + LOG_DBG("\tScan type: %u", status.scan); + LOG_DBG("\tMax devices: %u", status.max_devs); + LOG_DBG("\tRemaining time: %u", status.timeout); + + return BTP_STATUS_SUCCESS; +} + +static uint8_t rpr_scan_stop(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + const struct btp_rpr_scan_stop_cmd *cp = cmd; + struct bt_mesh_rpr_scan_status status; + const struct bt_mesh_rpr_node srv = { + .addr = cp->dst, + .net_idx = net.net_idx, + .ttl = BT_MESH_TTL_DEFAULT, + }; + int err; + + err = bt_mesh_rpr_scan_stop(&rpr_cli, &srv, &status); + if (err || status.status) { + LOG_DBG("Scan stop failed: %d %u", err, status.status); + return BTP_STATUS_FAILED; + } + + LOG_DBG("Remote Provisioning scan on 0x%04x stopped.", + net.dst); + + return BTP_STATUS_SUCCESS; +} + +static uint8_t rpr_link_get(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + const struct btp_rpr_link_get_cmd *cp = cmd; + struct bt_mesh_rpr_link link; + const struct bt_mesh_rpr_node srv = { + .addr = cp->dst, + .net_idx = net.net_idx, + .ttl = BT_MESH_TTL_DEFAULT, + }; + int err; + + err = bt_mesh_rpr_link_get(&rpr_cli, &srv, &link); + if (err) { + LOG_ERR("Link get failed: %d %u", err, link.status); + return BTP_STATUS_FAILED; + } + + LOG_DBG("Remote Provisioning Link on 0x%04x:", cp->dst); + LOG_DBG("\tStatus: %u", link.status); + LOG_DBG("\tState: %u", link.state); + + return BTP_STATUS_SUCCESS; +} + +static uint8_t rpr_link_close(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + const struct btp_rpr_link_close_cmd *cp = cmd; + struct bt_mesh_rpr_link link; + const struct bt_mesh_rpr_node srv = { + .addr = cp->dst, + .net_idx = net.net_idx, + .ttl = BT_MESH_TTL_DEFAULT, + }; + int err; + + err = bt_mesh_rpr_link_close(&rpr_cli, &srv, &link); + if (err) { + LOG_ERR("Link close failed: %d %u", err, link.status); + return BTP_STATUS_FAILED; + } + + LOG_DBG("Remote Provisioning Link on 0x%04x:", cp->dst); + LOG_DBG("\tStatus: %u", link.status); + LOG_DBG("\tState: %u", link.state); + + return BTP_STATUS_SUCCESS; +} + +static uint8_t rpr_prov_remote(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + const struct btp_rpr_prov_remote_cmd *cp = cmd; + struct bt_mesh_rpr_node srv = { + .addr = cp->dst, + .net_idx = net.net_idx, + .ttl = BT_MESH_TTL_DEFAULT, + }; + int err; + + err = bt_mesh_provision_remote(&rpr_cli, &srv, cp->uuid, + cp->net_idx, cp->addr); + if (err) { + LOG_ERR("Prov remote start failed: %d", err); + return BTP_STATUS_FAILED; + } + + return BTP_STATUS_SUCCESS; +} + +static uint8_t rpr_reprov_remote(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + const struct btp_rpr_reprov_remote_cmd *cp = cmd; + struct bt_mesh_rpr_node srv = { + .addr = cp->dst, + .net_idx = net.net_idx, + .ttl = BT_MESH_TTL_DEFAULT, + }; + int err; + + if (!BT_MESH_ADDR_IS_UNICAST(cp->addr)) { + LOG_ERR("Must be a valid unicast address"); + err = -EINVAL; + return BTP_STATUS_FAILED; + } + + err = bt_mesh_reprovision_remote(&rpr_cli, &srv, cp->addr, + cp->comp_change); + if (err) { + LOG_ERR("Reprovisioning failed: %d", err); + return BTP_STATUS_FAILED; + } + + return BTP_STATUS_SUCCESS; +} +#endif + +#if defined(CONFIG_BT_MESH_DFD_SRV) +static struct { + struct bt_mesh_dfu_target targets[32]; + struct bt_mesh_blob_target_pull pull[32]; + size_t target_cnt; + struct bt_mesh_blob_cli_inputs inputs; +} dfu_tx; + +static void dfu_tx_prepare(void) +{ + sys_slist_init(&dfu_tx.inputs.targets); + + for (int i = 0; i < dfu_tx.target_cnt; i++) { + /* Reset target context. */ + uint16_t addr = dfu_tx.targets[i].blob.addr; + + memset(&dfu_tx.targets[i].blob, 0, + sizeof(struct bt_mesh_blob_target)); + memset(&dfu_tx.pull[i], 0, + sizeof(struct bt_mesh_blob_target_pull)); + dfu_tx.targets[i].blob.addr = addr; + dfu_tx.targets[i].blob.pull = &dfu_tx.pull[i]; + + sys_slist_append(&dfu_tx.inputs.targets, + &dfu_tx.targets[i].blob.n); + } +} + +static void dfu_target(uint8_t img_idx, uint16_t addr) +{ + if (dfu_tx.target_cnt == ARRAY_SIZE(dfu_tx.targets)) { + LOG_ERR("No room."); + return; + } + + for (int i = 0; i < dfu_tx.target_cnt; i++) { + if (dfu_tx.targets[i].blob.addr == addr) { + LOG_ERR("Target 0x%04x already exists", addr); + return; + } + } + + dfu_tx.targets[dfu_tx.target_cnt].blob.addr = addr; + dfu_tx.targets[dfu_tx.target_cnt].img_idx = img_idx; + sys_slist_append(&dfu_tx.inputs.targets, + &dfu_tx.targets[dfu_tx.target_cnt].blob.n); + dfu_tx.target_cnt++; + + LOG_DBG("Added target 0x%04x", addr); +} +static void dfu_slot_add(size_t size, uint8_t *fwid, size_t fwid_len, + uint8_t *metadata, size_t metadata_len) +{ + struct bt_mesh_dfu_slot *slot; + int err; + + slot = bt_mesh_dfu_slot_reserve(); + err = bt_mesh_dfu_slot_info_set(slot, size, metadata, metadata_len); + if (err) { + LOG_ERR("Failed to set slot info: %d", err); + return; + } + + err = bt_mesh_dfu_slot_fwid_set(slot, fwid, fwid_len); + if (err) { + LOG_ERR("Failed to set slot fwid: %d", err); + return; + } + + err = bt_mesh_dfu_slot_commit(slot); + if (err) { + LOG_ERR("Failed to commit slot: %d", err); + return; + } + + LOG_DBG("Slot added."); +} +static enum bt_mesh_dfu_iter dfu_img_cb(struct bt_mesh_dfu_cli *cli, + struct bt_mesh_msg_ctx *ctx, + uint8_t idx, uint8_t total, + const struct bt_mesh_dfu_img *img, + void *cb_data) +{ + char fwid[2 * CONFIG_BT_MESH_DFU_FWID_MAXLEN + 1]; + size_t len; + + idx = 0xff; + + if (img->fwid_len <= sizeof(fwid)) { + len = bin2hex(img->fwid, img->fwid_len, fwid, sizeof(fwid)); + } else { + LOG_ERR("FWID is too big"); + return BT_MESH_DFU_ITER_STOP; + } + + fwid[len] = '\0'; + + LOG_DBG("Image %u:", idx); + LOG_DBG("\tFWID: "); + if (img->uri) { + LOG_DBG("\tURI: "); + } + + return BT_MESH_DFU_ITER_CONTINUE; +} + +static uint8_t dfu_info_get(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + const struct btp_mmdl_dfu_info_get_cmd *cp = cmd; + struct model_data *model_bound; + struct bt_mesh_msg_ctx ctx = { + .net_idx = net.net_idx, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + uint8_t max_count; + int err = 0; + + LOG_DBG(""); + + model_bound = lookup_model_bound(BT_MESH_MODEL_ID_DFU_CLI); + if (!model_bound) { + LOG_ERR("Model not found"); + return BTP_STATUS_FAILED; + } + ctx.addr = model_bound->addr; + ctx.app_idx = model_bound->appkey_idx; + + max_count = cp->limit; + + err = bt_mesh_dfu_cli_imgs_get(&dfd_srv.dfu, &ctx, dfu_img_cb, NULL, + max_count); + if (err) { + return BTP_STATUS_FAILED; + } + + return BTP_STATUS_SUCCESS; +} + +static uint8_t dfu_update_metadata_check(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + const struct btp_mmdl_dfu_metadata_check_cmd *cp = cmd; + struct btp_mmdl_dfu_metadata_check_rp *rp = rsp; + const struct bt_mesh_dfu_slot *slot; + struct model_data *model_bound; + struct bt_mesh_msg_ctx ctx = { + .net_idx = net.net_idx, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct bt_mesh_dfu_metadata_status rsp_data; + uint8_t img_idx, slot_idx; + size_t size; + size_t fwid_len; + size_t metadata_len; + uint8_t fwid[CONFIG_BT_MESH_DFU_FWID_MAXLEN]; + uint8_t metadata[CONFIG_BT_MESH_DFU_METADATA_MAXLEN]; + int err; + + LOG_DBG(""); + + model_bound = lookup_model_bound(BT_MESH_MODEL_ID_DFU_CLI); + if (!model_bound) { + LOG_ERR("Model not found"); + return BTP_STATUS_FAILED; + } + + ctx.addr = model_bound->addr; + ctx.app_idx = model_bound->appkey_idx; + img_idx = cp->index; + slot_idx = cp->slot_idx; + size = cp->slot_size; + fwid_len = cp->fwid_len; + metadata_len = cp->metadata_len; + + if ((metadata_len > 0) && + (metadata_len < CONFIG_BT_MESH_DFU_METADATA_MAXLEN)) { + memcpy(&metadata, cp->data, metadata_len); + } + + dfu_slot_add(size, fwid, fwid_len, metadata, metadata_len); + + slot = bt_mesh_dfu_slot_at(slot_idx); + if (!slot) { + LOG_ERR("No image in slot %u", slot_idx); + return BTP_STATUS_FAILED; + } + + err = bt_mesh_dfu_cli_metadata_check(&dfd_srv.dfu, &ctx, img_idx, slot, + &rsp_data); + + if (err) { + LOG_ERR("ERR %d", err); + return BTP_STATUS_FAILED; + } + + rp->idx = rsp_data.idx; + rp->status = rsp_data.status; + rp->effect = rsp_data.effect; + + *rsp_len = sizeof(struct btp_mmdl_dfu_metadata_check_rp); + + return BTP_STATUS_SUCCESS; +} + +static uint8_t dfu_firmware_update_get(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + struct model_data *model_bound; + struct bt_mesh_msg_ctx ctx = { + .net_idx = net.net_idx, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct bt_mesh_dfu_target_status rsp_data; + struct btp_mmdl_dfu_firmware_update_rp *rp = rsp; + int err; + + LOG_DBG(""); + + model_bound = lookup_model_bound(BT_MESH_MODEL_ID_DFU_CLI); + if (!model_bound) { + LOG_ERR("Model not found"); + return BTP_STATUS_FAILED; + } + + ctx.addr = model_bound->addr; + ctx.app_idx = model_bound->appkey_idx; + + err = bt_mesh_dfu_cli_status_get(&dfd_srv.dfu, &ctx, &rsp_data); + if (err) { + LOG_ERR("err %d", err); + return BTP_STATUS_FAILED; + } + + rp->status = rsp_data.status; + *rsp_len = sizeof(struct btp_mmdl_dfu_firmware_update_rp); + return BTP_STATUS_SUCCESS; +} + +static uint8_t dfu_firmware_update_cancel(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + struct model_data *model_bound; + struct bt_mesh_msg_ctx ctx = { + .net_idx = net.net_idx, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + int err; + + LOG_DBG(""); + + model_bound = lookup_model_bound(BT_MESH_MODEL_ID_DFU_CLI); + if (!model_bound) { + LOG_ERR("Model not found"); + return BTP_STATUS_FAILED; + } + + ctx.addr = model_bound->addr; + ctx.app_idx = model_bound->appkey_idx; + + err = bt_mesh_dfu_cli_cancel(&dfd_srv.dfu, &ctx); + if (err) { + LOG_ERR("err %d", err); + return BTP_STATUS_FAILED; + } + + return BTP_STATUS_SUCCESS; +} + +static uint8_t dfu_firmware_update_start(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + const struct btp_mmdl_dfu_firmware_update_cmd *cp = cmd; + struct model_data *model_bound; + struct bt_mesh_dfu_cli_xfer xfer; + uint8_t addr_cnt; + uint16_t addr = BT_MESH_ADDR_UNASSIGNED; + uint8_t slot_idx; + size_t size; + size_t fwid_len; + size_t metadata_len; + uint8_t fwid[CONFIG_BT_MESH_DFU_FWID_MAXLEN]; + uint8_t metadata[CONFIG_BT_MESH_DFU_METADATA_MAXLEN]; + int err = 0; + int i = 0; + + LOG_DBG(""); + + model_bound = lookup_model_bound(BT_MESH_MODEL_ID_DFU_CLI); + if (!model_bound) { + LOG_ERR("Model not found"); + return BTP_STATUS_FAILED; + } + + struct bt_mesh_dfu_cli_xfer_blob_params blob = { + .block_size_log = cp->block_size, + .chunk_size = cp->chunk_size, + }; + + addr_cnt = cp->addr_cnt; + slot_idx = cp->slot_idx; + size = cp->slot_size; + fwid_len = cp->fwid_len; + metadata_len = cp->metadata_len; + xfer.mode = BT_MESH_BLOB_XFER_MODE_PUSH; + xfer.blob_params = &blob; + + if ((metadata_len > 0) && + (metadata_len < CONFIG_BT_MESH_DFU_METADATA_MAXLEN)) { + memcpy(&metadata, cp->data, metadata_len); + } + + bt_mesh_dfu_slot_del_all(); + + dfu_slot_add(size, fwid, fwid_len, metadata, metadata_len); + + xfer.slot = bt_mesh_dfu_slot_at(slot_idx); + if (!xfer.slot) { + LOG_ERR("No image in slot %u", slot_idx); + return BTP_STATUS_FAILED; + } + + for (i = 0; i < addr_cnt; i++) { + addr = cp->data[metadata_len + 1 + i * sizeof(uint16_t)] | + (cp->data[metadata_len + i * sizeof(uint16_t)] << 8); + dfu_target(slot_idx, addr); + } + + dfu_tx_prepare(); + + if (!dfu_tx.target_cnt) { + LOG_ERR("No targets."); + return BTP_STATUS_FAILED; + } + + if (addr_cnt > 1) { + dfu_tx.inputs.group = BT_MESH_ADDR_UNASSIGNED; + } else { + dfu_tx.inputs.group = addr; + } + + dfu_tx.inputs.app_idx = model_bound->appkey_idx; + dfu_tx.inputs.ttl = BT_MESH_TTL_DEFAULT; + + err = bt_mesh_dfu_cli_send(&dfd_srv.dfu, &dfu_tx.inputs, &dummy_blob_io, &xfer); + + if (err) { + LOG_ERR("err %d", err); + return BTP_STATUS_FAILED; + } + + return BTP_STATUS_SUCCESS; +} + +static uint8_t dfu_firmware_update_apply(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + struct model_data *model_bound; + int err; + + LOG_DBG(""); + + model_bound = lookup_model_bound(BT_MESH_MODEL_ID_DFU_CLI); + if (!model_bound) { + LOG_ERR("Model not found"); + return BTP_STATUS_FAILED; + } + + err = bt_mesh_dfu_cli_apply(&dfd_srv.dfu); + if (err) { + LOG_ERR("err %d", err); + return BTP_STATUS_FAILED; + } + + return BTP_STATUS_SUCCESS; +} +#endif + +#if defined(CONFIG_BT_MESH_BLOB_CLI) && !defined(CONFIG_BT_MESH_DFD_SRV) +static void blob_cli_inputs_prepare(uint16_t group, uint16_t app_idx) +{ + int i; + + blob_cli_xfer.inputs.ttl = BT_MESH_TTL_DEFAULT; + blob_cli_xfer.inputs.group = group; + blob_cli_xfer.inputs.app_idx = app_idx; + sys_slist_init(&blob_cli_xfer.inputs.targets); + + for (i = 0; i < blob_cli_xfer.target_count; ++i) { + /* Reset target context. */ + uint16_t addr = blob_cli_xfer.targets[i].addr; + + memset(&blob_cli_xfer.targets[i], 0, + sizeof(struct bt_mesh_blob_target)); + memset(&blob_cli_xfer.pull[i], 0, + sizeof(struct bt_mesh_blob_target_pull)); + blob_cli_xfer.targets[i].addr = addr; + blob_cli_xfer.targets[i].pull = &blob_cli_xfer.pull[i]; + + sys_slist_append(&blob_cli_xfer.inputs.targets, + &blob_cli_xfer.targets[i].n); + } +} + +static int cmd_blob_target(uint16_t addr) +{ + struct bt_mesh_blob_target *t; + + if (blob_cli_xfer.target_count == ARRAY_SIZE(blob_cli_xfer.targets)) { + LOG_ERR("No more room"); + return 0; + } + + t = &blob_cli_xfer.targets[blob_cli_xfer.target_count]; + + t->addr = addr; + + LOG_DBG("Added target 0x%04x", t->addr); + + blob_cli_xfer.target_count++; + return 0; +} + +static uint8_t blob_info_get(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + const struct btp_mmdl_blob_info_get_cmd *cp = cmd; + struct model_data *model_bound; + uint16_t addr = BT_MESH_ADDR_UNASSIGNED; + uint16_t group = BT_MESH_ADDR_UNASSIGNED; + int err; + + LOG_DBG(""); + + model_bound = lookup_model_bound(BT_MESH_MODEL_ID_BLOB_CLI); + if (!model_bound) { + LOG_ERR("Model not found"); + return BTP_STATUS_FAILED; + } + + for (int i = 0; i < cp->addr_cnt; i++) { + addr = cp->addr[1 + i * sizeof(uint16_t)] | + (cp->addr[i * sizeof(uint16_t)] << 8); + err = cmd_blob_target(addr); + if (err) { + LOG_ERR("err target %d", err); + return BTP_STATUS_FAILED; + } + } + + if (cp->addr_cnt > 1) { + group = BT_MESH_ADDR_UNASSIGNED; + } else { + group = addr; + } + + if (!blob_cli_xfer.target_count) { + LOG_ERR("Failed: No targets"); + return BTP_STATUS_FAILED; + } + + blob_cli_inputs_prepare(group, model_bound->appkey_idx); + + err = bt_mesh_blob_cli_caps_get(&blob_cli, &blob_cli_xfer.inputs); + + if (err) { + return BTP_STATUS_FAILED; + } + + return BTP_STATUS_SUCCESS; +} + +static uint8_t blob_transfer_start(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + const struct btp_mmdl_blob_transfer_start_cmd *cp = cmd; + struct model_data *model_bound; + int err = 0; + + LOG_DBG(""); + + model_bound = lookup_model_bound(BT_MESH_MODEL_ID_BLOB_CLI); + if (!model_bound) { + LOG_ERR("Model not found"); + return BTP_STATUS_FAILED; + } + + if (!blob_cli_xfer.target_count) { + LOG_ERR("Failed: No targets"); + return BTP_STATUS_FAILED; + } + blob_cli_xfer.xfer.id = cp->id; + blob_cli_xfer.xfer.size = cp->size; + blob_cli_xfer.xfer.block_size_log = cp->block_size; + blob_cli_xfer.xfer.chunk_size = cp->chunk_size; - if (cp->ack) { - err = bt_mesh_health_cli_fault_test(&health_cli, &ctx, cid, test_id, faults, - &fault_count); + if (blob_cli.caps.modes) { + blob_cli_xfer.xfer.mode = blob_cli.caps.modes; } else { - err = bt_mesh_health_cli_fault_test_unack(&health_cli, &ctx, cid, test_id); + blob_cli_xfer.xfer.mode = BT_MESH_BLOB_XFER_MODE_PUSH; } - if (err) { - LOG_ERR("err %d", err); - return BTP_STATUS_FAILED; + if (cp->timeout) { + blob_cli_xfer.inputs.timeout_base = cp->timeout; } - if (cp->ack) { - struct btp_mesh_health_fault_test_rp *rp = rsp; + if (cp->ttl) { + blob_cli_xfer.inputs.ttl = cp->ttl; + } - rp->test_id = test_id; - rp->cid = sys_cpu_to_le16(cid); - (void)memcpy(rp->faults, faults, fault_count); + err = bt_mesh_blob_cli_send(&blob_cli, &blob_cli_xfer.inputs, + &blob_cli_xfer.xfer, &dummy_blob_io); - *rsp_len = sizeof(*rp) + fault_count; + if (err) { + return BTP_STATUS_FAILED; } return BTP_STATUS_SUCCESS; } -static uint8_t health_period_get(const void *cmd, uint16_t cmd_len, - void *rsp, uint16_t *rsp_len) +static uint8_t blob_transfer_cancel(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) { - const struct btp_mesh_health_period_get_cmd *cp = cmd; - struct bt_mesh_msg_ctx ctx = { - .net_idx = net.net_idx, - .addr = sys_le16_to_cpu(cp->address), - .app_idx = sys_le16_to_cpu(cp->app_idx), - }; - uint8_t divisor; - int err; - LOG_DBG(""); - err = bt_mesh_health_cli_period_get(&health_cli, &ctx, &divisor); - - if (err) { - LOG_ERR("err %d", err); - return BTP_STATUS_FAILED; - } + bt_mesh_blob_cli_cancel(&blob_cli); return BTP_STATUS_SUCCESS; } -static uint8_t health_period_set(const void *cmd, uint16_t cmd_len, +static uint8_t blob_transfer_get(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len) { - const struct btp_mesh_health_period_set_cmd *cp = cmd; - struct bt_mesh_msg_ctx ctx = { - .net_idx = net.net_idx, - .addr = sys_le16_to_cpu(cp->address), - .app_idx = sys_le16_to_cpu(cp->app_idx), - }; - uint8_t updated_divisor; + struct model_data *model_bound; + uint16_t group; int err; LOG_DBG(""); - if (cp->ack) { - err = bt_mesh_health_cli_period_set(&health_cli, &ctx, cp->divisor, - &updated_divisor); - } else { - err = bt_mesh_health_cli_period_set_unack(&health_cli, &ctx, cp->divisor); + model_bound = lookup_model_bound(BT_MESH_MODEL_ID_BLOB_CLI); + if (!model_bound) { + LOG_ERR("Model not found"); + return BTP_STATUS_FAILED; } + group = model_bound->addr; + + err = cmd_blob_target(group); if (err) { - LOG_ERR("err %d", err); + LOG_ERR("err target %d", err); return BTP_STATUS_FAILED; } - if (cp->ack) { - struct btp_mesh_health_period_set_rp *rp = rsp; + if (!blob_cli_xfer.target_count) { + LOG_ERR("Failed: No targets"); + return BTP_STATUS_FAILED; + } - rp->divisor = updated_divisor; + blob_cli_inputs_prepare(group, model_bound->appkey_idx); - *rsp_len = sizeof(*rp); + err = bt_mesh_blob_cli_xfer_progress_get(&blob_cli, &blob_cli_xfer.inputs); + + if (err) { + LOG_ERR("ERR %d", err); + return BTP_STATUS_FAILED; } return BTP_STATUS_SUCCESS; } +#endif /* CONFIG_BT_MESH_BLOB_CLI */ -static uint8_t health_attention_get(const void *cmd, uint16_t cmd_len, - void *rsp, uint16_t *rsp_len) +#if defined(CONFIG_BT_MESH_BLOB_SRV) +static uint8_t blob_srv_recv(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) { - const struct btp_mesh_health_attention_get_cmd *cp = cmd; - struct bt_mesh_msg_ctx ctx = { - .net_idx = net.net_idx, - .addr = sys_le16_to_cpu(cp->address), - .app_idx = sys_le16_to_cpu(cp->app_idx), - }; - uint8_t attention; + const struct btp_mmdl_blob_srv_recv_cmd *cp = cmd; + struct model_data *model_bound; int err; +#if defined(CONFIG_BT_MESH_DFD_SRV) + struct bt_mesh_blob_srv *srv = &dfd_srv.upload.blob; +#elif defined(CONFIG_BT_MESH_DFU_SRV) + struct bt_mesh_blob_srv *srv = &dfu_srv.blob; +#endif + + model_bound = lookup_model_bound(BT_MESH_MODEL_ID_BLOB_SRV); + if (!model_bound) { + LOG_ERR("Model not found"); + return BTP_STATUS_FAILED; + } + + uint16_t timeout_base; + uint64_t id; + uint8_t ttl; + LOG_DBG(""); - err = bt_mesh_health_cli_attention_get(&health_cli, &ctx, &attention); + id = cp->id; + timeout_base = cp->timeout; + ttl = cp->ttl; + + err = bt_mesh_blob_srv_recv(srv, id, &dummy_blob_io, ttl, + timeout_base); if (err) { - LOG_ERR("err %d", err); + LOG_ERR("ERR %d", err); return BTP_STATUS_FAILED; } return BTP_STATUS_SUCCESS; } -static uint8_t health_attention_set(const void *cmd, uint16_t cmd_len, - void *rsp, uint16_t *rsp_len) +static uint8_t blob_srv_cancel(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) { - const struct btp_mesh_health_attention_set_cmd *cp = cmd; - struct bt_mesh_msg_ctx ctx = { - .net_idx = net.net_idx, - .addr = sys_le16_to_cpu(cp->address), - .app_idx = sys_le16_to_cpu(cp->app_idx), - }; - uint8_t updated_attention; + struct model_data *model_bound; int err; - LOG_DBG(""); - - if (cp->ack) { - err = bt_mesh_health_cli_attention_set(&health_cli, &ctx, cp->attention, - &updated_attention); - } else { - err = bt_mesh_health_cli_attention_set_unack(&health_cli, &ctx, cp->attention); - } +#if defined(CONFIG_BT_MESH_DFU_SRV) + struct bt_mesh_blob_srv *srv = &dfu_srv.blob; +#elif defined(CONFIG_BT_MESH_DFD_SRV) + struct bt_mesh_blob_srv *srv = &dfd_srv.upload.blob; +#endif - if (err) { - LOG_ERR("err %d", err); + model_bound = lookup_model_bound(BT_MESH_MODEL_ID_BLOB_SRV); + if (!model_bound) { + LOG_ERR("Model not found"); return BTP_STATUS_FAILED; } - if (cp->ack) { - struct btp_mesh_health_attention_set_rp *rp = rsp; + LOG_DBG(""); - rp->attention = updated_attention; + err = bt_mesh_blob_srv_cancel(srv); - *rsp_len = sizeof(*rp); + if (err) { + LOG_ERR("ERR %d", err); + return BTP_STATUS_FAILED; } return BTP_STATUS_SUCCESS; } +#endif static const struct btp_handler handlers[] = { { @@ -2559,7 +4555,7 @@ static const struct btp_handler handlers[] = { }, { .opcode = BTP_MESH_INIT, - .expect_len = 0, + .expect_len = sizeof(struct btp_mesh_init_cmd), .func = init, }, { @@ -2587,6 +4583,7 @@ static const struct btp_handler handlers[] = { .expect_len = 0, .func = ivu_toggle_state, }, +#if defined(CONFIG_BT_MESH_LOW_POWER) { .opcode = BTP_MESH_LPN, .expect_len = sizeof(struct btp_mesh_lpn_set_cmd), @@ -2597,6 +4594,7 @@ static const struct btp_handler handlers[] = { .expect_len = 0, .func = lpn_poll, }, +#endif /* CONFIG_BT_MESH_LOW_POWER */ { .opcode = BTP_MESH_NET_SEND, .expect_len = BTP_HANDLER_LENGTH_VARIABLE, @@ -2908,6 +4906,7 @@ static const struct btp_handler handlers[] = { .func = va_del, }, #if defined(CONFIG_BT_TESTING) +#if defined(CONFIG_BT_MESH_LOW_POWER) { .opcode = BTP_MESH_LPN_SUBSCRIBE, .expect_len = sizeof(struct btp_mesh_lpn_subscribe_cmd), @@ -2918,6 +4917,7 @@ static const struct btp_handler handlers[] = { .expect_len = sizeof(struct btp_mesh_lpn_unsubscribe_cmd), .func = lpn_unsubscribe, }, +#endif /* CONFIG_BT_MESH_LOW_POWER */ { .opcode = BTP_MESH_RPL_CLEAR, .expect_len = 0, @@ -2936,6 +4936,249 @@ static const struct btp_handler handlers[] = { .func = proxy_connect }, #endif +#if defined(CONFIG_BT_MESH_SAR_CFG_CLI) + { + .opcode = BTP_MESH_SAR_TRANSMITTER_GET, + .expect_len = sizeof(struct btp_mesh_sar_transmitter_get_cmd), + .func = sar_transmitter_get + }, + { + .opcode = BTP_MESH_SAR_TRANSMITTER_SET, + .expect_len = sizeof(struct btp_mesh_sar_transmitter_set_cmd), + .func = sar_transmitter_set + }, + { + .opcode = BTP_MESH_SAR_RECEIVER_GET, + .expect_len = sizeof(struct btp_mesh_sar_receiver_get_cmd), + .func = sar_receiver_get + }, + { + .opcode = BTP_MESH_SAR_RECEIVER_SET, + .expect_len = sizeof(struct btp_mesh_sar_receiver_set_cmd), + .func = sar_receiver_set + }, +#endif +#if defined(CONFIG_BT_MESH_LARGE_COMP_DATA_CLI) + { + .opcode = BTP_MESH_LARGE_COMP_DATA_GET, + .expect_len = sizeof(struct btp_mesh_large_comp_data_get_cmd), + .func = large_comp_data_get + }, + { + .opcode = BTP_MESH_MODELS_METADATA_GET, + .expect_len = sizeof(struct btp_mesh_models_metadata_get_cmd), + .func = models_metadata_get + }, +#endif +#if defined(CONFIG_BT_MESH_OP_AGG_CLI) + { + .opcode = BTP_MESH_OPCODES_AGGREGATOR_INIT, + .expect_len = sizeof(struct btp_mesh_opcodes_aggregator_init_cmd), + .func = opcodes_aggregator_init + }, + { + .opcode = BTP_MESH_OPCODES_AGGREGATOR_SEND, + .expect_len = 0, + .func = opcodes_aggregator_send + }, +#endif + { + .opcode = BTP_MESH_COMP_CHANGE_PREPARE, + .expect_len = 0, + .func = change_prepare + }, +#if defined(CONFIG_BT_MESH_RPR_CLI) + { + .opcode = BTP_MESH_RPR_SCAN_START, + .expect_len = sizeof(struct btp_rpr_scan_start_cmd), + .func = rpr_scan_start + }, + { + .opcode = BTP_MESH_RPR_EXT_SCAN_START, + .expect_len = BTP_HANDLER_LENGTH_VARIABLE, + .func = rpr_ext_scan_start + }, + { + .opcode = BTP_MESH_RPR_SCAN_CAPS_GET, + .expect_len = sizeof(struct btp_rpr_scan_caps_get_cmd), + .func = rpr_scan_caps_get + }, + { + .opcode = BTP_MESH_RPR_SCAN_GET, + .expect_len = sizeof(struct btp_rpr_scan_get_cmd), + .func = rpr_scan_get + }, + { + .opcode = BTP_MESH_RPR_SCAN_STOP, + .expect_len = sizeof(struct btp_rpr_scan_stop_cmd), + .func = rpr_scan_stop + }, + { + .opcode = BTP_MESH_RPR_LINK_GET, + .expect_len = sizeof(struct btp_rpr_link_get_cmd), + .func = rpr_link_get + }, + { + .opcode = BTP_MESH_RPR_LINK_CLOSE, + .expect_len = sizeof(struct btp_rpr_link_close_cmd), + .func = rpr_link_close + }, + { + .opcode = BTP_MESH_RPR_PROV_REMOTE, + .expect_len = sizeof(struct btp_rpr_prov_remote_cmd), + .func = rpr_prov_remote + }, + { + .opcode = BTP_MESH_RPR_REPROV_REMOTE, + .expect_len = sizeof(struct btp_rpr_reprov_remote_cmd), + .func = rpr_reprov_remote + }, +#endif +#if defined(CONFIG_BT_MESH_PRIV_BEACON_CLI) + { + .opcode = BTP_MESH_PRIV_BEACON_GET, + .expect_len = sizeof(struct btp_priv_beacon_get_cmd), + .func = priv_beacon_get + }, + { + .opcode = BTP_MESH_PRIV_BEACON_SET, + .expect_len = sizeof(struct btp_priv_beacon_set_cmd), + .func = priv_beacon_set + }, + { + .opcode = BTP_MESH_PRIV_GATT_PROXY_GET, + .expect_len = sizeof(struct btp_priv_gatt_proxy_get_cmd), + .func = priv_gatt_proxy_get + }, + { + .opcode = BTP_MESH_PRIV_GATT_PROXY_SET, + .expect_len = sizeof(struct btp_priv_gatt_proxy_set_cmd), + .func = priv_gatt_proxy_set + }, + { + .opcode = BTP_MESH_PRIV_NODE_ID_GET, + .expect_len = sizeof(struct btp_priv_node_id_get_cmd), + .func = priv_node_id_get + }, + { + .opcode = BTP_MESH_PRIV_NODE_ID_SET, + .expect_len = sizeof(struct btp_priv_node_id_set_cmd), + .func = priv_node_id_set + }, + { + .opcode = BTP_MESH_PROXY_PRIVATE_IDENTITY, + .expect_len = 0, + .func = proxy_private_identity_enable + }, +#endif +#if defined(CONFIG_BT_MESH_OD_PRIV_PROXY_CLI) + { + .opcode = BTP_MESH_OD_PRIV_PROXY_GET, + .expect_len = sizeof(struct btp_od_priv_proxy_get_cmd), + .func = od_priv_proxy_get + }, + { + .opcode = BTP_MESH_OD_PRIV_PROXY_SET, + .expect_len = sizeof(struct btp_od_priv_proxy_set_cmd), + .func = od_priv_proxy_set + }, +#endif +#if defined(CONFIG_BT_MESH_SOL_PDU_RPL_CLI) + { + .opcode = BTP_MESH_SRPL_CLEAR, + .expect_len = sizeof(struct btp_srpl_clear_cmd), + .func = srpl_clear + }, +#endif +#if defined(CONFIG_BT_MESH_PROXY_SOLICITATION) + { + .opcode = BTP_MESH_PROXY_SOLICIT, + .expect_len = sizeof(struct btp_proxy_solicit_cmd), + .func = proxy_solicit + }, +#endif + { + .opcode = BTP_MESH_START, + .expect_len = 0, + .func = start + }, +}; + + +static const struct btp_handler mdl_handlers[] = { +#if defined(CONFIG_BT_MESH_DFD_SRV) + { + .opcode = BTP_MMDL_DFU_INFO_GET, + .expect_len = sizeof(struct btp_mmdl_dfu_info_get_cmd), + .func = dfu_info_get, + }, + { + .opcode = BTP_MMDL_DFU_UPDATE_METADATA_CHECK, + .expect_len = BTP_HANDLER_LENGTH_VARIABLE, + .func = dfu_update_metadata_check, + }, + { + .opcode = BTP_MMDL_DFU_FIRMWARE_UPDATE_GET, + .expect_len = 0, + .func = dfu_firmware_update_get, + }, + { + .opcode = BTP_MMDL_DFU_FIRMWARE_UPDATE_CANCEL, + .expect_len = 0, + .func = dfu_firmware_update_cancel, + }, + { + .opcode = BTP_MMDL_DFU_FIRMWARE_UPDATE_START, + .expect_len = BTP_HANDLER_LENGTH_VARIABLE, + .func = dfu_firmware_update_start, + }, + { + .opcode = BTP_MMDL_DFU_FIRMWARE_UPDATE_APPLY, + .expect_len = 0, + .func = dfu_firmware_update_apply, + }, +#endif +#if defined(CONFIG_BT_MESH_BLOB_CLI) && !defined(CONFIG_BT_MESH_DFD_SRV) + { + .opcode = BTP_MMDL_BLOB_INFO_GET, + .expect_len = BTP_HANDLER_LENGTH_VARIABLE, + .func = blob_info_get, + }, + { + .opcode = BTP_MMDL_BLOB_TRANSFER_START, + .expect_len = sizeof(struct btp_mmdl_blob_transfer_start_cmd), + .func = blob_transfer_start, + }, + { + .opcode = BTP_MMDL_BLOB_TRANSFER_CANCEL, + .expect_len = 0, + .func = blob_transfer_cancel, + }, + { + .opcode = BTP_MMDL_BLOB_TRANSFER_GET, + .expect_len = 0, + .func = blob_transfer_get, + }, +#endif +#if defined(CONFIG_BT_MESH_BLOB_SRV) + { + .opcode = BTP_MMDL_BLOB_SRV_RECV, + .expect_len = sizeof(struct btp_mmdl_blob_srv_recv_cmd), + .func = blob_srv_recv + }, + { + .opcode = BTP_MMDL_BLOB_SRV_CANCEL, + .expect_len = 0, + .func = blob_srv_cancel + }, +#endif +#if defined(CONFIG_BT_MESH_DFU_SRV) + { + .opcode = BTP_MMDL_DFU_SRV_APPLY, + .expect_len = 0, + .func = dfu_srv_apply + }, +#endif }; void net_recv_ev(uint8_t ttl, uint8_t ctl, uint16_t src, uint16_t dst, const void *payload, @@ -2963,6 +5206,28 @@ void net_recv_ev(uint8_t ttl, uint8_t ctl, uint16_t src, uint16_t dst, const voi tester_event(BTP_SERVICE_ID_MESH, BTP_MESH_EV_NET_RECV, buf.data, buf.len); } +void model_recv_ev(uint16_t src, uint16_t dst, const void *payload, + size_t payload_len) +{ + NET_BUF_SIMPLE_DEFINE(buf, UINT8_MAX); + struct btp_mesh_model_recv_ev *ev; + + LOG_DBG("src 0x%04x dst 0x%04x payload_len %zu", src, dst, payload_len); + + if (payload_len > net_buf_simple_tailroom(&buf)) { + LOG_ERR("Payload size exceeds buffer size"); + return; + } + + ev = net_buf_simple_add(&buf, sizeof(*ev)); + ev->src = sys_cpu_to_le16(src); + ev->dst = sys_cpu_to_le16(dst); + ev->payload_len = payload_len; + net_buf_simple_add_mem(&buf, payload, payload_len); + + tester_event(BTP_SERVICE_ID_MESH, BTP_MESH_EV_MODEL_RECV, buf.data, buf.len); +} + static void model_bound_cb(uint16_t addr, struct bt_mesh_model *model, uint16_t key_idx) { @@ -3023,6 +5288,7 @@ static void incomp_timer_exp_cb(void) static struct bt_test_cb bt_test_cb = { .mesh_net_recv = net_recv_ev, + .mesh_model_recv = model_recv_ev, .mesh_model_bound = model_bound_cb, .mesh_model_unbound = model_unbound_cb, .mesh_prov_invalid_bearer = invalid_bearer_cb, @@ -3102,6 +5368,10 @@ uint8_t tester_init_mesh(void) bt_test_cb_register(&bt_test_cb); } +#if defined(CONFIG_BT_MESH_COMP_PAGE_2) + bt_mesh_comp2_register(&comp_p2); +#endif + tester_register_command_handlers(BTP_SERVICE_ID_MESH, handlers, ARRAY_SIZE(handlers)); @@ -3112,3 +5382,15 @@ uint8_t tester_unregister_mesh(void) { return BTP_STATUS_SUCCESS; } + +uint8_t tester_init_mmdl(void) +{ + tester_register_command_handlers(BTP_SERVICE_ID_MESH_MDL, mdl_handlers, + ARRAY_SIZE(mdl_handlers)); + return BTP_STATUS_SUCCESS; +} + +uint8_t tester_unregister_mmdl(void) +{ + return BTP_STATUS_SUCCESS; +} diff --git a/tests/bluetooth/tester/testcase.yaml b/tests/bluetooth/tester/testcase.yaml index 65588568994..dba67716af1 100644 --- a/tests/bluetooth/tester/testcase.yaml +++ b/tests/bluetooth/tester/testcase.yaml @@ -25,3 +25,12 @@ tests: extra_args: OVERLAY_CONFIG="overlay-mesh.conf" tags: bluetooth harness: bluetooth + bluetooth.general.tester_mesh_v1d1: + build_only: true + platform_allow: + - qemu_x86 + - native_posix + - nrf52840dk_nrf52840 + extra_args: OVERLAY_CONFIG="overlay-mesh.conf;overlay-mesh-v1d1.conf" + tags: bluetooth + harness: bluetooth diff --git a/tests/bsim/bluetooth/audio/prj.conf b/tests/bsim/bluetooth/audio/prj.conf index 2077143452b..8f2d6a585a9 100644 --- a/tests/bsim/bluetooth/audio/prj.conf +++ b/tests/bsim/bluetooth/audio/prj.conf @@ -9,8 +9,9 @@ CONFIG_BT_DEVICE_NAME="bsim_test_audio" # TBS Client may require up to 12 buffers CONFIG_BT_L2CAP_TX_BUF_COUNT=12 CONFIG_BT_ATT_PREPARE_COUNT=5 -CONFIG_BT_MAX_CONN=5 -CONFIG_BT_MAX_PAIRED=5 +CONFIG_BT_MAX_CONN=3 +CONFIG_BT_MAX_PAIRED=3 +CONFIG_BT_EXT_ADV_MAX_ADV_SET=3 CONFIG_BT_GATT_DYNAMIC_DB=y CONFIG_BT_SMP=y CONFIG_BT_L2CAP_TX_MTU=100 diff --git a/tests/bsim/bluetooth/audio/src/bap_broadcast_assistant_test.c b/tests/bsim/bluetooth/audio/src/bap_broadcast_assistant_test.c index 381810d68db..9964bd29c0a 100644 --- a/tests/bsim/bluetooth/audio/src/bap_broadcast_assistant_test.c +++ b/tests/bsim/bluetooth/audio/src/bap_broadcast_assistant_test.c @@ -490,9 +490,10 @@ static int common_init(void) bt_gatt_cb_register(&gatt_callbacks); bt_bap_broadcast_assistant_register_cb(&broadcast_assistant_cbs); bt_le_per_adv_sync_cb_register(&sync_callbacks); + bt_le_scan_cb_register(&common_scan_cb); printk("Starting scan\n"); - err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found); + err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL); if (err != 0) { FAIL("Scanning failed to start (err %d)\n", err); return err; diff --git a/tests/bsim/bluetooth/audio/src/bap_unicast_client_test.c b/tests/bsim/bluetooth/audio/src/bap_unicast_client_test.c index e31c092684a..3fbd70458b9 100644 --- a/tests/bsim/bluetooth/audio/src/bap_unicast_client_test.c +++ b/tests/bsim/bluetooth/audio/src/bap_unicast_client_test.c @@ -306,6 +306,92 @@ static struct bt_gatt_cb gatt_callbacks = { .att_mtu_updated = att_mtu_updated, }; +static bool parse_ascs_ad_data(struct bt_data *data, void *user_data) +{ + const struct bt_le_scan_recv_info *info = user_data; + uint16_t available_source_context; + uint16_t available_sink_context; + struct net_buf_simple net_buf; + struct bt_uuid_16 adv_uuid; + uint8_t announcement_type; + void *uuid; + int err; + + const size_t min_data_len = BT_UUID_SIZE_16 + sizeof(announcement_type) + + sizeof(available_sink_context) + + sizeof(available_source_context); + + if (data->type != BT_DATA_SVC_DATA16) { + return true; + } + + if (data->data_len < min_data_len) { + + return true; + } + + net_buf_simple_init_with_data(&net_buf, (void *)data->data, data->data_len); + + uuid = net_buf_simple_pull_mem(&net_buf, BT_UUID_SIZE_16); + if (!bt_uuid_create(&adv_uuid.uuid, uuid, BT_UUID_SIZE_16)) { + return true; + } + + if (bt_uuid_cmp(&adv_uuid.uuid, BT_UUID_ASCS)) { + return true; + } + + announcement_type = net_buf_simple_pull_u8(&net_buf); + available_sink_context = net_buf_simple_pull_le16(&net_buf); + available_source_context = net_buf_simple_pull_le16(&net_buf); + + printk("Found ASCS with announcement type 0x%02X, sink ctx 0x%04X, source ctx 0x%04X\n", + announcement_type, available_sink_context, available_source_context); + + printk("Stopping scan\n"); + if (bt_le_scan_stop()) { + FAIL("Could not stop scan"); + return false; + } + + err = bt_conn_le_create(info->addr, BT_CONN_LE_CREATE_CONN, BT_LE_CONN_PARAM_DEFAULT, + &default_conn); + if (err) { + FAIL("Could not connect to peer: %d", err); + return false; + } + + /* Stop parsing */ + return false; +} + +static void broadcast_scan_recv(const struct bt_le_scan_recv_info *info, struct net_buf_simple *ad) +{ + char addr_str[BT_ADDR_LE_STR_LEN]; + + if (default_conn) { + return; + } + + /* We're only interested in connectable events */ + if ((info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) == 0) { + return; + } + /* connect only to devices in close proximity */ + if (info->rssi < -70) { + return; + } + + bt_addr_le_to_str(info->addr, addr_str, sizeof(addr_str)); + printk("Device found: %s (RSSI %d)\n", addr_str, info->rssi); + + bt_data_parse(ad, parse_ascs_ad_data, (void *)info); +} + +static struct bt_le_scan_cb bap_scan_cb = { + .recv = broadcast_scan_recv, +}; + static void init(void) { int err; @@ -320,6 +406,7 @@ static void init(void) g_streams[i].ops = &stream_ops; } + bt_le_scan_cb_register(&bap_scan_cb); bt_gatt_cb_register(&gatt_callbacks); err = bt_bap_unicast_client_register_cb(&unicast_client_cbs); @@ -333,7 +420,7 @@ static void scan_and_connect(void) { int err; - err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found); + err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL); if (err != 0) { FAIL("Scanning failed to start (err %d)\n", err); return; @@ -343,6 +430,19 @@ static void scan_and_connect(void) WAIT_FOR_FLAG(flag_connected); } +static void disconnect_acl(void) +{ + int err; + + err = bt_conn_disconnect(default_conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + if (err != 0) { + FAIL("Failed to disconnect (err %d)\n", err); + return; + } + + WAIT_FOR_UNSET_FLAG(flag_connected); +} + static void exchange_mtu(void) { WAIT_FOR_FLAG(flag_mtu_exchanged); @@ -748,18 +848,76 @@ static void test_main(void) unicast_group = NULL; } + disconnect_acl(); PASS("Unicast client passed\n"); } +static void test_main_acl_disconnect(void) +{ + struct bt_bap_unicast_group *unicast_group; + size_t stream_cnt; + + init(); + + scan_and_connect(); + + exchange_mtu(); + + discover_sinks(); + + discover_sources(); + + /* Run the stream setup multiple time to ensure states are properly + * set and reset + */ + + printk("Creating unicast group\n"); + stream_cnt = create_unicast_group(&unicast_group); + + printk("Codec configuring streams\n"); + codec_configure_streams(stream_cnt); + + printk("QoS configuring streams\n"); + qos_configure_streams(unicast_group, stream_cnt); + + printk("Enabling streams\n"); + enable_streams(stream_cnt); + + printk("Metadata update streams\n"); + metadata_update_streams(stream_cnt); + + printk("Starting streams\n"); + start_streams(); + + disconnect_acl(); + + printk("Deleting unicast group\n"); + delete_unicast_group(unicast_group); + unicast_group = NULL; + + /* Reconnect */ + scan_and_connect(); + + disconnect_acl(); + + PASS("Unicast client ACL disconnect passed\n"); +} + static const struct bst_test_instance test_unicast_client[] = { { .test_id = "unicast_client", .test_post_init_f = test_init, .test_tick_f = test_tick, - .test_main_f = test_main + .test_main_f = test_main, + }, + { + .test_id = "unicast_client_acl_disconnect", + .test_post_init_f = test_init, + .test_tick_f = test_tick, + .test_main_f = test_main_acl_disconnect, }, - BSTEST_END_MARKER + BSTEST_END_MARKER, }; struct bst_test_list *test_unicast_client_install(struct bst_test_list *tests) diff --git a/tests/bsim/bluetooth/audio/src/bap_unicast_server_test.c b/tests/bsim/bluetooth/audio/src/bap_unicast_server_test.c index c302ec320ab..58acdc1726c 100644 --- a/tests/bsim/bluetooth/audio/src/bap_unicast_server_test.c +++ b/tests/bsim/bluetooth/audio/src/bap_unicast_server_test.c @@ -48,11 +48,20 @@ static struct bt_bap_stream streams[CONFIG_BT_ASCS_ASE_SNK_COUNT + CONFIG_BT_ASC static const struct bt_audio_codec_qos_pref qos_pref = BT_AUDIO_CODEC_QOS_PREF(true, BT_GAP_LE_PHY_2M, 0x02, 10, 40000, 40000, 40000, 40000); -/* TODO: Expand with BAP data */ +static uint8_t unicast_server_addata[] = { + BT_UUID_16_ENCODE(BT_UUID_ASCS_VAL), /* ASCS UUID */ + BT_AUDIO_UNICAST_ANNOUNCEMENT_TARGETED, /* Target Announcement */ + BT_BYTES_LIST_LE16(PREF_CONTEXT), + BT_BYTES_LIST_LE16(PREF_CONTEXT), + 0x00, /* Metadata length */ +}; + static const struct bt_data unicast_server_ad[] = { BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), BT_DATA_BYTES(BT_DATA_UUID16_ALL, BT_UUID_16_ENCODE(BT_UUID_ASCS_VAL)), + BT_DATA(BT_DATA_SVC_DATA16, unicast_server_addata, ARRAY_SIZE(unicast_server_addata)), }; +static struct bt_le_ext_adv *ext_adv; CREATE_FLAG(flag_stream_configured); @@ -230,47 +239,6 @@ static struct bt_bap_stream_ops stream_ops = { .recv = stream_recv }; -static void init(void) -{ - static struct bt_pacs_cap cap = { - .codec_cap = &lc3_codec_cap, - }; - int err; - - err = bt_enable(NULL); - if (err != 0) { - FAIL("Bluetooth enable failed (err %d)\n", err); - return; - } - - printk("Bluetooth initialized\n"); - - bt_bap_unicast_server_register_cb(&unicast_server_cb); - - err = bt_pacs_cap_register(BT_AUDIO_DIR_SINK, &cap); - if (err != 0) { - FAIL("Failed to register capabilities: %d", err); - return; - } - - err = bt_pacs_cap_register(BT_AUDIO_DIR_SOURCE, &cap); - if (err != 0) { - FAIL("Failed to register capabilities: %d", err); - return; - } - - for (size_t i = 0; i < ARRAY_SIZE(streams); i++) { - bt_bap_stream_cb_register(&streams[i], &stream_ops); - } - - err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, unicast_server_ad, ARRAY_SIZE(unicast_server_ad), - NULL, 0); - if (err != 0) { - FAIL("Advertising failed to start (err %d)\n", err); - return; - } -} - static void set_location(void) { int err; @@ -332,25 +300,160 @@ static void set_available_contexts(void) printk("Available contexts successfully set\n"); } -static void test_main(void) +static void init(void) { - init(); + static struct bt_pacs_cap cap = { + .codec_cap = &lc3_codec_cap, + }; + int err; + + err = bt_enable(NULL); + if (err != 0) { + FAIL("Bluetooth enable failed (err %d)\n", err); + return; + } + + printk("Bluetooth initialized\n"); + + bt_bap_unicast_server_register_cb(&unicast_server_cb); + + err = bt_pacs_cap_register(BT_AUDIO_DIR_SINK, &cap); + if (err != 0) { + FAIL("Failed to register capabilities: %d", err); + return; + } + + err = bt_pacs_cap_register(BT_AUDIO_DIR_SOURCE, &cap); + if (err != 0) { + FAIL("Failed to register capabilities: %d", err); + return; + } set_location(); set_available_contexts(); + for (size_t i = 0; i < ARRAY_SIZE(streams); i++) { + bt_bap_stream_cb_register(&streams[i], &stream_ops); + } + + /* Create a non-connectable non-scannable advertising set */ + err = bt_le_ext_adv_create(BT_LE_EXT_ADV_CONN_NAME, NULL, &ext_adv); + if (err != 0) { + FAIL("Failed to create advertising set (err %d)\n", err); + return; + } + + err = bt_le_ext_adv_set_data(ext_adv, unicast_server_ad, ARRAY_SIZE(unicast_server_ad), + NULL, 0); + if (err != 0) { + FAIL("Failed to set advertising data (err %d)\n", err); + return; + } + + err = bt_le_ext_adv_start(ext_adv, BT_LE_EXT_ADV_START_DEFAULT); + if (err != 0) { + FAIL("Failed to start advertising set (err %d)\n", err); + return; + } + printk("Advertising started\n"); +} + +static void test_main(void) +{ + init(); + /* TODO: When babblesim supports ISO, wait for audio stream to pass */ WAIT_FOR_FLAG(flag_connected); WAIT_FOR_FLAG(flag_stream_configured); + + WAIT_FOR_UNSET_FLAG(flag_connected); + PASS("Unicast server passed\n"); } -static const struct bst_test_instance test_unicast_server[] = {{.test_id = "unicast_server", - .test_post_init_f = test_init, - .test_tick_f = test_tick, - .test_main_f = test_main}, - BSTEST_END_MARKER}; +static void restart_adv_cb(struct k_work *work) +{ + int err; + + printk("Restarting ext_adv after disconnect\n"); + + err = bt_le_ext_adv_start(ext_adv, BT_LE_EXT_ADV_START_DEFAULT); + if (err != 0) { + FAIL("Failed to start advertising set (err %d)\n", err); + return; + } +} + +static K_WORK_DEFINE(restart_adv_work, restart_adv_cb); + +static void acl_disconnected(struct bt_conn *conn, uint8_t reason) +{ + if (conn != default_conn) { + return; + } + + k_work_submit(&restart_adv_work); +} + +static void test_main_acl_disconnect(void) +{ + struct bt_le_ext_adv *dummy_ext_adv[CONFIG_BT_MAX_CONN - 1]; + static struct bt_conn_cb conn_callbacks = { + .disconnected = acl_disconnected, + }; + + init(); + + /* Create CONFIG_BT_MAX_CONN - 1 dummy advertising sets, to ensure that we only have 1 free + * connection when attempting to restart advertising, which should ensure that the + * bt_conn object is properly unref'ed by the stack + */ + for (size_t i = 0U; i < ARRAY_SIZE(dummy_ext_adv); i++) { + const struct bt_le_adv_param param = BT_LE_ADV_PARAM_INIT( + (BT_LE_ADV_OPT_EXT_ADV | BT_LE_ADV_OPT_CONNECTABLE), + BT_GAP_ADV_SLOW_INT_MAX, BT_GAP_ADV_SLOW_INT_MAX, NULL); + int err; + + err = bt_le_ext_adv_create(¶m, NULL, &dummy_ext_adv[i]); + if (err != 0) { + FAIL("Failed to create advertising set[%zu] (err %d)\n", i, err); + return; + } + + err = bt_le_ext_adv_start(dummy_ext_adv[i], BT_LE_EXT_ADV_START_DEFAULT); + if (err != 0) { + FAIL("Failed to start advertising set[%zu] (err %d)\n", i, err); + return; + } + } + + bt_conn_cb_register(&conn_callbacks); + + WAIT_FOR_FLAG(flag_connected); + WAIT_FOR_FLAG(flag_stream_configured); + + /* The client will reconnect */ + WAIT_FOR_UNSET_FLAG(flag_connected); + WAIT_FOR_FLAG(flag_connected); + PASS("Unicast server ACL disconnect passed\n"); +} + +static const struct bst_test_instance test_unicast_server[] = { + { + .test_id = "unicast_server", + .test_post_init_f = test_init, + .test_tick_f = test_tick, + .test_main_f = test_main, + }, + { + .test_id = "unicast_server_acl_disconnect", + .test_post_init_f = test_init, + .test_tick_f = test_tick, + .test_main_f = test_main_acl_disconnect, + }, + BSTEST_END_MARKER, +}; struct bst_test_list *test_unicast_server_install(struct bst_test_list *tests) { diff --git a/tests/bsim/bluetooth/audio/src/common.c b/tests/bsim/bluetooth/audio/src/common.c index 30758e01732..90a8cb98efa 100644 --- a/tests/bsim/bluetooth/audio/src/common.c +++ b/tests/bsim/bluetooth/audio/src/common.c @@ -16,8 +16,7 @@ const struct bt_data ad[AD_SIZE] = { BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)) }; -void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, - struct net_buf_simple *ad) +static void device_found(const struct bt_le_scan_recv_info *info, struct net_buf_simple *ad) { char addr_str[BT_ADDR_LE_STR_LEN]; int err; @@ -27,15 +26,15 @@ void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, } /* We're only interested in connectable events */ - if (type != BT_HCI_ADV_IND && type != BT_HCI_ADV_DIRECT_IND) { + if ((info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) == 0) { return; } - bt_addr_le_to_str(addr, addr_str, sizeof(addr_str)); - printk("Device found: %s (RSSI %d)\n", addr_str, rssi); + bt_addr_le_to_str(info->addr, addr_str, sizeof(addr_str)); + printk("Device found: %s (RSSI %d)\n", addr_str, info->rssi); /* connect only to devices in close proximity */ - if (rssi < -70) { + if (info->rssi < -70) { FAIL("RSSI too low"); return; } @@ -46,13 +45,17 @@ void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, return; } - err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, - BT_LE_CONN_PARAM_DEFAULT, &default_conn); + err = bt_conn_le_create(info->addr, BT_CONN_LE_CREATE_CONN, BT_LE_CONN_PARAM_DEFAULT, + &default_conn); if (err) { FAIL("Could not connect to peer: %d", err); } } +struct bt_le_scan_cb common_scan_cb = { + .recv = device_found, +}; + static void connected(struct bt_conn *conn, uint8_t err) { char addr[BT_ADDR_LE_STR_LEN]; diff --git a/tests/bsim/bluetooth/audio/src/common.h b/tests/bsim/bluetooth/audio/src/common.h index 917a024b724..14d6dbbc092 100644 --- a/tests/bsim/bluetooth/audio/src/common.h +++ b/tests/bsim/bluetooth/audio/src/common.h @@ -65,13 +65,12 @@ #define SYNC_RETRY_COUNT 6 /* similar to retries for connections */ #define PA_SYNC_SKIP 5 +extern struct bt_le_scan_cb common_scan_cb; extern const struct bt_data ad[AD_SIZE]; extern struct bt_conn *default_conn; extern atomic_t flag_connected; extern atomic_t flag_conn_updated; -void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, - struct net_buf_simple *ad); void disconnected(struct bt_conn *conn, uint8_t reason); void test_tick(bs_time_t HW_device_time); void test_init(void); diff --git a/tests/bsim/bluetooth/audio/src/has_client_test.c b/tests/bsim/bluetooth/audio/src/has_client_test.c index 9d63463e0ce..6690c6b032e 100644 --- a/tests/bsim/bluetooth/audio/src/has_client_test.c +++ b/tests/bsim/bluetooth/audio/src/has_client_test.c @@ -157,7 +157,9 @@ static void test_main(void) return; } - err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found); + bt_le_scan_cb_register(&common_scan_cb); + + err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL); if (err < 0) { FAIL("Scanning failed to start (err %d)\n", err); return; diff --git a/tests/bsim/bluetooth/audio/src/ias_client_test.c b/tests/bsim/bluetooth/audio/src/ias_client_test.c index 2c5dbd40bd0..7c24b1a172a 100644 --- a/tests/bsim/bluetooth/audio/src/ias_client_test.c +++ b/tests/bsim/bluetooth/audio/src/ias_client_test.c @@ -83,7 +83,9 @@ static void test_main(void) return; } - err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found); + bt_le_scan_cb_register(&common_scan_cb); + + err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL); if (err < 0) { FAIL("Scanning failed to start (err %d)\n", err); return; diff --git a/tests/bsim/bluetooth/audio/src/mcc_test.c b/tests/bsim/bluetooth/audio/src/mcc_test.c index 8a32e33bf16..9e8cd95806b 100644 --- a/tests/bsim/bluetooth/audio/src/mcc_test.c +++ b/tests/bsim/bluetooth/audio/src/mcc_test.c @@ -2396,6 +2396,8 @@ void test_main(void) printk("Bluetooth initialized\n"); + bt_le_scan_cb_register(&common_scan_cb); + /* Initialize MCC ********************************************/ err = do_mcc_init(); if (err != 0) { @@ -2414,7 +2416,7 @@ void test_main(void) printk("\n########### Running iteration #%u\n\n", i); UNSET_FLAG(flag_connected); - err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found); + err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL); if (err != 0) { FAIL("Failed to start scanning (err %d\n)", err); } else { diff --git a/tests/bsim/bluetooth/audio/src/media_controller_test.c b/tests/bsim/bluetooth/audio/src/media_controller_test.c index 9c1df656fd1..ad91cffb939 100644 --- a/tests/bsim/bluetooth/audio/src/media_controller_test.c +++ b/tests/bsim/bluetooth/audio/src/media_controller_test.c @@ -1585,6 +1585,8 @@ void initialize_bluetooth(void) WAIT_FOR_FLAG(ble_is_initialized); printk("Bluetooth initialized\n"); + + bt_le_scan_cb_register(&common_scan_cb); } void scan_and_connect(void) @@ -1592,7 +1594,7 @@ void scan_and_connect(void) char addr[BT_ADDR_LE_STR_LEN]; int err; - err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found); + err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL); if (err) { FAIL("Failed to start scanning (err %d\n)", err); return; diff --git a/tests/bsim/bluetooth/audio/src/micp_mic_ctlr_test.c b/tests/bsim/bluetooth/audio/src/micp_mic_ctlr_test.c index 1074c8c2cef..5cefd31d099 100644 --- a/tests/bsim/bluetooth/audio/src/micp_mic_ctlr_test.c +++ b/tests/bsim/bluetooth/audio/src/micp_mic_ctlr_test.c @@ -357,11 +357,13 @@ static void test_main(void) return; } + bt_le_scan_cb_register(&common_scan_cb); + bt_micp_mic_ctlr_cb_register(&micp_mic_ctlr_cbs); WAIT_FOR_COND(g_bt_init); - err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found); + err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL); if (err != 0) { FAIL("Scanning failed to start (err %d)\n", err); return; diff --git a/tests/bsim/bluetooth/audio/src/pacs_notify_client_test.c b/tests/bsim/bluetooth/audio/src/pacs_notify_client_test.c index 7cbe5feaa22..9910f6f2c8c 100644 --- a/tests/bsim/bluetooth/audio/src/pacs_notify_client_test.c +++ b/tests/bsim/bluetooth/audio/src/pacs_notify_client_test.c @@ -490,8 +490,10 @@ static void test_main(void) return; } + bt_le_scan_cb_register(&common_scan_cb); + printk("Starting scan\n"); - err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found); + err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL); if (err != 0) { FAIL("Could not start scanning (err %d)\n", err); return; @@ -536,7 +538,7 @@ static void test_main(void) WAIT_FOR_UNSET_FLAG(flag_connected); printk("Starting scan\n"); - err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found); + err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL); if (err != 0) { FAIL("Could not start scanning (err %d)\n", err); return; diff --git a/tests/bsim/bluetooth/audio/src/tbs_test.c b/tests/bsim/bluetooth/audio/src/tbs_test.c index b20dca26608..a1bf1e035c0 100644 --- a/tests/bsim/bluetooth/audio/src/tbs_test.c +++ b/tests/bsim/bluetooth/audio/src/tbs_test.c @@ -331,9 +331,10 @@ static void test_main(void) printk("Audio Client: Bluetooth initialized\n"); bt_conn_cb_register(&conn_callbacks); + bt_le_scan_cb_register(&common_scan_cb); bt_tbs_register_cb(&tbs_cbs); - err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found); + err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL); if (err != 0) { FAIL("Scanning failed to start (err %d)\n", err); return; diff --git a/tests/bsim/bluetooth/audio/src/vcp_vol_ctlr_test.c b/tests/bsim/bluetooth/audio/src/vcp_vol_ctlr_test.c index 52ba2f072ab..77feaa4bfb5 100644 --- a/tests/bsim/bluetooth/audio/src/vcp_vol_ctlr_test.c +++ b/tests/bsim/bluetooth/audio/src/vcp_vol_ctlr_test.c @@ -1153,9 +1153,10 @@ static void test_main(void) return; } + bt_le_scan_cb_register(&common_scan_cb); test_cb_register(); - err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found); + err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL); if (err != 0) { FAIL("Scanning failed to start (err %d)\n", err); return; diff --git a/tests/bsim/bluetooth/audio/test_scripts/bap_unicast_audio_acl_disconnect.sh b/tests/bsim/bluetooth/audio/test_scripts/bap_unicast_audio_acl_disconnect.sh new file mode 100755 index 00000000000..875a2b752dc --- /dev/null +++ b/tests/bsim/bluetooth/audio/test_scripts/bap_unicast_audio_acl_disconnect.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# +# Copyright (c) 2023 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 + +SIMULATION_ID="unicast_audio_acl_disconnect" +VERBOSITY_LEVEL=2 +EXECUTE_TIMEOUT=20 + +source ${ZEPHYR_BASE}/tests/bsim/sh_common.source + +cd ${BSIM_OUT_PATH}/bin + +printf "\n\n======== Unicast Audio ACL Disconnect test =========\n\n" + +Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \ + -v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=0 -testid=unicast_client_acl_disconnect -rs=23 + +Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \ + -v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=1 -testid=unicast_server_acl_disconnect -rs=28 + +# Simulation time should be larger than the WAIT_TIME in common.h +Execute ./bs_2G4_phy_v1 -v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} \ + -D=2 -sim_length=60e6 $@ + +wait_for_background_jobs diff --git a/tests/bsim/bluetooth/host/adv/chain/prj.conf b/tests/bsim/bluetooth/host/adv/chain/prj.conf index 3e7f11009c2..82eb16e5db0 100644 --- a/tests/bsim/bluetooth/host/adv/chain/prj.conf +++ b/tests/bsim/bluetooth/host/adv/chain/prj.conf @@ -30,8 +30,9 @@ CONFIG_BT_EXT_SCAN_BUF_SIZE=1650 # Set maximum scan data length for Extended Scanning in Bluetooth LE Controller CONFIG_BT_CTLR_SCAN_DATA_LEN_MAX=1650 -# Zephyr Bluetooth LE Controller needs 16 event buffers to generate Extended -# Advertising Report for receiving the complete 1650 bytes of data +# The Zephyr Controller does not combine all the 1650 bytes before +# fragmenting into 8 HCI reports, if a PDU has 255 bytes, +# it will generate 2 HCI reports and so we need to reserve 16 buffers CONFIG_BT_BUF_EVT_RX_COUNT=16 # Increase Zephyr Bluetooth LE Controller Rx buffer to receive complete chain diff --git a/tests/bsim/bluetooth/host/adv/chain/src/main.c b/tests/bsim/bluetooth/host/adv/chain/src/main.c index 4475a069535..d58d2a3890f 100644 --- a/tests/bsim/bluetooth/host/adv/chain/src/main.c +++ b/tests/bsim/bluetooth/host/adv/chain/src/main.c @@ -34,8 +34,12 @@ #define NAME_LEN 30 #define BT_AD_DATA_NAME_SIZE (sizeof(CONFIG_BT_DEVICE_NAME) - 1U + 2U) #define BT_AD_DATA_MFG_DATA_SIZE (254U + 2U) +/* + * for testing chaining the manufacturing data is duplicated, hence DATA_LEN needs to + * add twice the size for this element + */ #define DATA_LEN MIN((BT_AD_DATA_NAME_SIZE + \ - BT_AD_DATA_MFG_DATA_SIZE), \ + BT_AD_DATA_MFG_DATA_SIZE + BT_AD_DATA_MFG_DATA_SIZE), \ CONFIG_BT_CTLR_ADV_DATA_LEN_MAX) static K_SEM_DEFINE(sem_recv, 0, 1); @@ -94,6 +98,7 @@ static void scan_recv(const struct bt_le_scan_recv_info *info, data_len = buf->len; if (data_len != DATA_LEN) { + printk("Received datalength: %d\n", data_len); return; } @@ -101,11 +106,13 @@ static void scan_recv(const struct bt_le_scan_recv_info *info, bt_data_parse(buf, data_cb, name); if (strcmp(name, CONFIG_BT_DEVICE_NAME)) { + printk("Wrong name %s\n", name); return; } for (uint8_t i = 0; i < sid_count; i++) { if (sid[i] == info->sid) { + printk("Received SID %d\n", info->sid); return; } } @@ -113,6 +120,7 @@ static void scan_recv(const struct bt_le_scan_recv_info *info, sid[sid_count++] = info->sid; if (sid_count < CONFIG_BT_EXT_ADV_MAX_ADV_SET) { + printk("Received advertising sets: %d\n", sid_count); return; } diff --git a/tests/bsim/bluetooth/host/compile.sh b/tests/bsim/bluetooth/host/compile.sh index a54dbd26507..98d6c79e3ef 100755 --- a/tests/bsim/bluetooth/host/compile.sh +++ b/tests/bsim/bluetooth/host/compile.sh @@ -44,6 +44,7 @@ app=tests/bsim/bluetooth/host/gatt/settings compile app=tests/bsim/bluetooth/host/gatt/settings conf_file=prj_2.conf compile app=tests/bsim/bluetooth/host/gatt/ccc_store compile app=tests/bsim/bluetooth/host/gatt/ccc_store conf_file=prj_2.conf compile +app=tests/bsim/bluetooth/host/gatt/sc_indicate compile app=tests/bsim/bluetooth/host/l2cap/general compile app=tests/bsim/bluetooth/host/l2cap/userdata compile diff --git a/tests/bsim/bluetooth/host/gatt/sc_indicate/CMakeLists.txt b/tests/bsim/bluetooth/host/gatt/sc_indicate/CMakeLists.txt new file mode 100644 index 00000000000..d734b3c054e --- /dev/null +++ b/tests/bsim/bluetooth/host/gatt/sc_indicate/CMakeLists.txt @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +if (NOT DEFINED ENV{BSIM_COMPONENTS_PATH}) + message(FATAL_ERROR "This test requires the BabbleSim simulator. Please set \ + the environment variable BSIM_COMPONENTS_PATH to point to its \ + components folder. More information can be found in \ + https://babblesim.github.io/folder_structure_and_env.html") +endif() + +find_package(Zephyr HINTS $ENV{ZEPHYR_BASE}) +project(bsim_test_auto_seq_req) + +target_sources(app PRIVATE + src/main.c + src/central.c + src/peripheral.c + src/bs_bt_utils.c +) + +zephyr_include_directories( + $ENV{BSIM_COMPONENTS_PATH}/libUtilv1/src/ + $ENV{BSIM_COMPONENTS_PATH}/libPhyComv1/src/ +) diff --git a/tests/bsim/bluetooth/host/gatt/sc_indicate/prj.conf b/tests/bsim/bluetooth/host/gatt/sc_indicate/prj.conf new file mode 100644 index 00000000000..7edaf36d10e --- /dev/null +++ b/tests/bsim/bluetooth/host/gatt/sc_indicate/prj.conf @@ -0,0 +1,20 @@ +CONFIG_BT=y +CONFIG_BT_CENTRAL=y +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_DEVICE_NAME="SC Indication Test" + +CONFIG_LOG=y +CONFIG_BT_EXT_ADV=y + +CONFIG_BT_SMP=y +CONFIG_BT_GATT_CLIENT=y + +CONFIG_SETTINGS=y +CONFIG_BT_SETTINGS=y +CONFIG_FLASH=y +CONFIG_FLASH_PAGE_LAYOUT=y +CONFIG_NVS=y +CONFIG_FLASH_MAP=y +CONFIG_SETTINGS_NVS=y + +CONFIG_BT_GATT_DYNAMIC_DB=y diff --git a/tests/bsim/bluetooth/host/gatt/sc_indicate/src/bs_bt_utils.c b/tests/bsim/bluetooth/host/gatt/sc_indicate/src/bs_bt_utils.c new file mode 100644 index 00000000000..31952cbe330 --- /dev/null +++ b/tests/bsim/bluetooth/host/gatt/sc_indicate/src/bs_bt_utils.c @@ -0,0 +1,182 @@ +/** + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include +#include + +#include +LOG_MODULE_REGISTER(test_utils, LOG_LEVEL_DBG); + +#include "bs_bt_utils.h" + +struct bt_conn *g_conn; +DEFINE_FLAG(flag_is_connected); + +void wait_connected(void) +{ + LOG_DBG("Wait for connection..."); + WAIT_FOR_FLAG(flag_is_connected); +} + +void wait_disconnected(void) +{ + LOG_DBG("Wait for disconnection..."); + WAIT_FOR_FLAG_UNSET(flag_is_connected); +} + +static void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err) +{ + LOG_DBG("security changed"); +} + +static void disconnected(struct bt_conn *conn, uint8_t reason) +{ + UNSET_FLAG(flag_is_connected); +} + +struct bt_conn *get_g_conn(void) +{ + return g_conn; +} + +void clear_g_conn(void) +{ + struct bt_conn *conn; + + conn = g_conn; + g_conn = NULL; + BSIM_ASSERT(conn, "Test error: no g_conn!\n"); + bt_conn_unref(conn); +} + +static void connected(struct bt_conn *conn, uint8_t err) +{ + BSIM_ASSERT((!g_conn || (conn == g_conn)), "Unexpected new connection."); + + if (!g_conn) { + g_conn = bt_conn_ref(conn); + } + + if (err != 0) { + clear_g_conn(); + return; + } + + SET_FLAG(flag_is_connected); +} + +BT_CONN_CB_DEFINE(conn_callbacks) = { + .connected = connected, + .disconnected = disconnected, + .security_changed = security_changed, +}; + +static void stop_scan_and_connect(const bt_addr_le_t *addr, + int8_t rssi, + uint8_t type, + struct net_buf_simple *ad) +{ + char addr_str[BT_ADDR_LE_STR_LEN]; + int err; + + if (g_conn != NULL) { + return; + } + + bt_addr_le_to_str(addr, addr_str, sizeof(addr_str)); + printk("Got scan result, connecting.. dst %s, RSSI %d\n", addr_str, rssi); + + err = bt_le_scan_stop(); + BSIM_ASSERT(!err, "Err bt_le_scan_stop %d", err); + + err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, BT_LE_CONN_PARAM_DEFAULT, &g_conn); + BSIM_ASSERT(!err, "Err bt_conn_le_create %d", err); +} + +void scan_connect_to_first_result(void) +{ + int err; + + err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, stop_scan_and_connect); + BSIM_ASSERT(!err, "Err bt_le_scan_start %d", err); +} + +void disconnect(void) +{ + int err; + + err = bt_conn_disconnect(g_conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + BSIM_ASSERT(!err, "bt_conn_disconnect failed (%d)\n", err); +} + +void set_security(bt_security_t sec) +{ + int err; + + err = bt_conn_set_security(g_conn, sec); + BSIM_ASSERT(!err, "Err bt_conn_set_security %d", err); +} + +void create_adv(struct bt_le_ext_adv **adv) +{ + int err; + struct bt_le_adv_param params = {}; + + params.options |= BT_LE_ADV_OPT_CONNECTABLE; + params.options |= BT_LE_ADV_OPT_EXT_ADV; + + params.id = BT_ID_DEFAULT; + params.sid = 0; + params.interval_min = BT_GAP_ADV_FAST_INT_MIN_2; + params.interval_max = BT_GAP_ADV_FAST_INT_MAX_2; + + err = bt_le_ext_adv_create(¶ms, NULL, adv); + BSIM_ASSERT(!err, "bt_le_ext_adv_create failed (%d)\n", err); +} + +void start_adv(struct bt_le_ext_adv *adv) +{ + int err; + + err = bt_le_ext_adv_start(adv, BT_LE_EXT_ADV_START_DEFAULT); + BSIM_ASSERT(!err, "bt_le_ext_adv_start failed (%d)\n", err); +} + +void stop_adv(struct bt_le_ext_adv *adv) +{ + int err; + + err = bt_le_ext_adv_stop(adv); + BSIM_ASSERT(!err, "bt_le_ext_adv_stop failed (%d)\n", err); +} + +/* The following flags are raised by events and lowered by test code. */ +DEFINE_FLAG(flag_pairing_complete); +DEFINE_FLAG(flag_bonded); +DEFINE_FLAG(flag_not_bonded); + +void pairing_complete(struct bt_conn *conn, bool bonded) +{ + LOG_DBG("pairing complete"); + SET_FLAG(flag_pairing_complete); + + if (bonded) { + SET_FLAG(flag_bonded); + LOG_DBG("Bonded status: true"); + } else { + SET_FLAG(flag_not_bonded); + LOG_DBG("Bonded status: false"); + } +} + +void pairing_failed(struct bt_conn *conn, enum bt_security_err err) +{ + FAIL("Pairing failed\n"); +} diff --git a/tests/bsim/bluetooth/host/gatt/sc_indicate/src/bs_bt_utils.h b/tests/bsim/bluetooth/host/gatt/sc_indicate/src/bs_bt_utils.h new file mode 100644 index 00000000000..5ec34005e9f --- /dev/null +++ b/tests/bsim/bluetooth/host/gatt/sc_indicate/src/bs_bt_utils.h @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "bstests.h" +#include "bs_tracing.h" + +#include +#include + +extern enum bst_result_t bst_result; + +#define DECLARE_FLAG(flag) extern atomic_t flag +#define DEFINE_FLAG(flag) atomic_t flag = (atomic_t) false +#define SET_FLAG(flag) (void)atomic_set(&flag, (atomic_t) true) +#define UNSET_FLAG(flag) (void)atomic_set(&flag, (atomic_t) false) +#define WAIT_FOR_FLAG(flag) \ + while (!(bool)atomic_get(&flag)) { \ + (void)k_sleep(K_MSEC(1)); \ + } +#define WAIT_FOR_FLAG_UNSET(flag) \ + while ((bool)atomic_get(&flag)) { \ + (void)k_sleep(K_MSEC(1)); \ + } +#define TAKE_FLAG(flag) \ + while (!(bool)atomic_cas(&flag, true, false)) { \ + (void)k_sleep(K_MSEC(1)); \ + } +#define GET_FLAG(flag) (bool)atomic_get(&flag) + +#define BSIM_ASSERT(expr, ...) \ + do { \ + if (!(expr)) { \ + FAIL(__VA_ARGS__); \ + } \ + } while (0) + +#define FAIL(...) \ + do { \ + bst_result = Failed; \ + bs_trace_error_time_line(__VA_ARGS__); \ + } while (0) + +#define PASS(...) \ + do { \ + bst_result = Passed; \ + bs_trace_info_time(1, __VA_ARGS__); \ + } while (0) + +DECLARE_FLAG(flag_pairing_complete); +DECLARE_FLAG(flag_bonded); +DECLARE_FLAG(flag_not_bonded); + +void scan_connect_to_first_result(void); +struct bt_conn *get_g_conn(void); +void clear_g_conn(void); +void disconnect(void); +void wait_connected(void); +void wait_disconnected(void); +void create_adv(struct bt_le_ext_adv **adv); +void start_adv(struct bt_le_ext_adv *adv); +void stop_adv(struct bt_le_ext_adv *adv); +void set_security(bt_security_t sec); +void pairing_complete(struct bt_conn *conn, bool bonded); +void pairing_failed(struct bt_conn *conn, enum bt_security_err err); diff --git a/tests/bsim/bluetooth/host/gatt/sc_indicate/src/central.c b/tests/bsim/bluetooth/host/gatt/sc_indicate/src/central.c new file mode 100644 index 00000000000..ca45874e9cb --- /dev/null +++ b/tests/bsim/bluetooth/host/gatt/sc_indicate/src/central.c @@ -0,0 +1,195 @@ +/** + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +LOG_MODULE_REGISTER(test_central, LOG_LEVEL_DBG); + +#include "bs_bt_utils.h" + +DEFINE_FLAG(flag_discovered); +DEFINE_FLAG(flag_subscribed); +DEFINE_FLAG(flag_indicated); + +enum GATT_HANDLES { + SC, + CCC, + NUM_HANDLES, +}; + +static uint16_t gatt_handles[NUM_HANDLES] = {0}; + +static struct bt_gatt_subscribe_params subscribe_params; + +static void sc_subscribed(struct bt_conn *conn, + uint8_t err, + struct bt_gatt_subscribe_params *params) +{ + LOG_DBG("subscribed"); + SET_FLAG(flag_subscribed); +} + +static uint8_t sc_indicated(struct bt_conn *conn, + struct bt_gatt_subscribe_params *params, + const void *data, + uint16_t length) +{ + LOG_DBG("indication received"); + + SET_FLAG(flag_indicated); + + return BT_GATT_ITER_CONTINUE; +} + +static void subscribe(void) +{ + int err; + + subscribe_params.ccc_handle = gatt_handles[CCC]; + subscribe_params.value_handle = gatt_handles[SC]; + subscribe_params.value = BT_GATT_CCC_INDICATE; + subscribe_params.subscribe = sc_subscribed; + subscribe_params.notify = sc_indicated; + + err = bt_gatt_subscribe(get_g_conn(), &subscribe_params); + BSIM_ASSERT(!err, "bt_gatt_subscribe failed (%d)\n", err); + + WAIT_FOR_FLAG(flag_subscribed); +} + +static uint8_t discover_func(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + struct bt_gatt_discover_params *params) +{ + if (attr == NULL) { + for (size_t i = 0U; i < ARRAY_SIZE(gatt_handles); i++) { + LOG_DBG("handle[%d] = 0x%x", i, gatt_handles[i]); + BSIM_ASSERT(gatt_handles[i] != 0, "did not find all handles\n"); + } + + (void)memset(params, 0, sizeof(*params)); + SET_FLAG(flag_discovered); + + return BT_GATT_ITER_STOP; + } + + if (params->type == BT_GATT_DISCOVER_CHARACTERISTIC) { + const struct bt_gatt_chrc *chrc = (struct bt_gatt_chrc *)attr->user_data; + static const struct bt_uuid_16 ccc_uuid = BT_UUID_INIT_16(BT_UUID_GATT_CCC_VAL); + + if (bt_uuid_cmp(chrc->uuid, BT_UUID_GATT_SC) == 0) { + int err; + + LOG_DBG("found sc"); + gatt_handles[SC] = chrc->value_handle; + + params->uuid = &ccc_uuid.uuid; + params->start_handle = attr->handle + 2; + params->type = BT_GATT_DISCOVER_DESCRIPTOR; + + err = bt_gatt_discover(conn, params); + BSIM_ASSERT(!err, "bt_gatt_discover failed (%d)\n", err); + + return BT_GATT_ITER_STOP; + } + + } else if (params->type == BT_GATT_DISCOVER_DESCRIPTOR && + bt_uuid_cmp(params->uuid, BT_UUID_GATT_CCC) == 0) { + LOG_DBG("found ccc"); + gatt_handles[CCC] = attr->handle; + SET_FLAG(flag_discovered); + + return BT_GATT_ITER_STOP; + } + + return BT_GATT_ITER_CONTINUE; +} + +static void gatt_discover(void) +{ + int err; + static struct bt_gatt_discover_params discover_params; + + discover_params.uuid = NULL; + discover_params.func = discover_func; + discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE; + discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE; + discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC; + + err = bt_gatt_discover(get_g_conn(), &discover_params); + BSIM_ASSERT(!err, "bt_gatt_discover failed (%d)\n", err); + + WAIT_FOR_FLAG(flag_discovered); + + LOG_DBG("sc handle: %d", gatt_handles[SC]); + LOG_DBG("ccc handle: %d", gatt_handles[CCC]); +} + +void central(void) +{ + /* + * test goal: check that service changed indication is sent on + * reconnection when the server's GATT database has been updated since + * last connection + * + * the central will connect, bond with the peripheral and then + * disconnect after doing that, the central will try to connect again, + * this time it will not elevate the security + * + * to pass the test, the central will wait to receive the service + * changed indication + */ + + int err; + struct bt_conn_auth_info_cb bt_conn_auth_info_cb = { + .pairing_failed = pairing_failed, + .pairing_complete = pairing_complete, + }; + + err = bt_enable(NULL); + BSIM_ASSERT(!err, "bt_enable failed (%d)\n", err); + + err = bt_conn_auth_info_cb_register(&bt_conn_auth_info_cb); + BSIM_ASSERT(!err, "bt_conn_auth_info_cb_register failed.\n"); + + err = settings_load(); + BSIM_ASSERT(!err, "settings_load failed (%d)\n", err); + + scan_connect_to_first_result(); + wait_connected(); + + set_security(BT_SECURITY_L2); + + TAKE_FLAG(flag_pairing_complete); + TAKE_FLAG(flag_bonded); + + /* subscribe to the service changed indication */ + gatt_discover(); + subscribe(); + + disconnect(); + wait_disconnected(); + clear_g_conn(); + + scan_connect_to_first_result(); + wait_connected(); + + /* wait for service change indication */ + WAIT_FOR_FLAG(flag_indicated); + + PASS("PASS\n"); +} diff --git a/tests/bsim/bluetooth/host/gatt/sc_indicate/src/main.c b/tests/bsim/bluetooth/host/gatt/sc_indicate/src/main.c new file mode 100644 index 00000000000..f5ffbbc4677 --- /dev/null +++ b/tests/bsim/bluetooth/host/gatt/sc_indicate/src/main.c @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "bstests.h" +#include "bs_bt_utils.h" + +#define BS_SECONDS_TO_US(dur_sec) ((bs_time_t)dur_sec * USEC_PER_SEC) +#define TEST_TIMEOUT_SIMULATED BS_SECONDS_TO_US(60) + +extern void central(void); +extern void peripheral(void); + +static void test_tick(bs_time_t HW_device_time) +{ + bs_trace_debug_time(0, "Simulation ends now.\n"); + if (bst_result != Passed) { + bst_result = Failed; + bs_trace_error("Test did not pass before simulation ended.\n"); + } +} + +static void test_init(void) +{ + bst_ticker_set_next_tick_absolute(TEST_TIMEOUT_SIMULATED); + bst_result = In_progress; +} + +static const struct bst_test_instance test_to_add[] = { + { + .test_id = "central", + .test_post_init_f = test_init, + .test_tick_f = test_tick, + .test_main_f = central, + }, + { + .test_id = "peripheral", + .test_post_init_f = test_init, + .test_tick_f = test_tick, + .test_main_f = peripheral, + }, + BSTEST_END_MARKER, +}; + +static struct bst_test_list *install(struct bst_test_list *tests) +{ + return bst_add_tests(tests, test_to_add); +}; + +bst_test_install_t test_installers[] = {install, NULL}; + +int main(void) +{ + bst_main(); + return 0; +} diff --git a/tests/bsim/bluetooth/host/gatt/sc_indicate/src/peripheral.c b/tests/bsim/bluetooth/host/gatt/sc_indicate/src/peripheral.c new file mode 100644 index 00000000000..e15c00b60d7 --- /dev/null +++ b/tests/bsim/bluetooth/host/gatt/sc_indicate/src/peripheral.c @@ -0,0 +1,86 @@ +/** + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include +LOG_MODULE_REGISTER(test_peripheral, LOG_LEVEL_DBG); + +#include "bs_bt_utils.h" + +#define UUID_1 BT_UUID_DECLARE_128(0xdb, 0x1f, 0xe2, 0x52, 0xf3, 0xc6, 0x43, 0x66, \ + 0xb3, 0x92, 0x5d, 0xc6, 0xe7, 0xc9, 0x59, 0x9d) +#define UUID_2 BT_UUID_DECLARE_128(0x3f, 0xa4, 0x7f, 0x44, 0x2e, 0x2a, 0x43, 0x05, \ + 0xab, 0x38, 0x07, 0x8d, 0x16, 0xbf, 0x99, 0xf1) + +static void new_svc_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value) +{ + ARG_UNUSED(attr); + + bool notif_enabled = (value == BT_GATT_CCC_NOTIFY); + + LOG_DBG("CCC Update: notification %s", notif_enabled ? "enabled" : "disabled"); +} + +static struct bt_gatt_attr attrs[] = { + BT_GATT_PRIMARY_SERVICE(UUID_1), + BT_GATT_CHARACTERISTIC(UUID_2, BT_GATT_CHRC_NOTIFY, BT_GATT_PERM_NONE, NULL, NULL, NULL), + BT_GATT_CCC(new_svc_ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), +}; + +static struct bt_gatt_service svc = { + .attrs = attrs, + .attr_count = ARRAY_SIZE(attrs), +}; + +void peripheral(void) +{ + /* + * test goal: check that service changed indication is sent on + * reconnection when the server's GATT database has been updated since + * last connection + * + * the peripheral will wait for connection/disconnection, when + * disconnected it will register a new service, when reconnecting, the + * central should receive an indication + */ + + int err; + struct bt_le_ext_adv *adv = NULL; + + err = bt_enable(NULL); + BSIM_ASSERT(!err, "bt_enable failed (%d)\n", err); + + err = settings_load(); + BSIM_ASSERT(!err, "settings_load failed (%d)\n", err); + + create_adv(&adv); + start_adv(adv); + wait_connected(); + + stop_adv(adv); + + wait_disconnected(); + clear_g_conn(); + + /* add a new service to trigger the service changed indication */ + err = bt_gatt_service_register(&svc); + BSIM_ASSERT(!err, "bt_gatt_service_register failed (%d)\n", err); + LOG_DBG("New service added"); + + start_adv(adv); + wait_connected(); + + PASS("Done\n"); +} diff --git a/tests/bsim/bluetooth/host/gatt/sc_indicate/test_scripts/_compile.sh b/tests/bsim/bluetooth/host/gatt/sc_indicate/test_scripts/_compile.sh new file mode 100755 index 00000000000..b07577a9878 --- /dev/null +++ b/tests/bsim/bluetooth/host/gatt/sc_indicate/test_scripts/_compile.sh @@ -0,0 +1,18 @@ +#!/bin/env bash +# Copyright 2023 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +set -eu + +# Terminate running simulations (if any) +${BSIM_COMPONENTS_PATH}/common/stop_bsim.sh + +test_name='sc_indicate' + +: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}" +bsim_bin="${BSIM_OUT_PATH}/bin" +BOARD="${BOARD:-nrf52_bsim}" +test_exe="${bsim_bin}/bs_${BOARD}_tests_bsim_bluetooth_host_gatt_${test_name}_prj_conf" + +west build -b nrf52_bsim -d build && \ + cp -v build/zephyr/zephyr.exe "${test_exe}" diff --git a/tests/bsim/bluetooth/host/gatt/sc_indicate/test_scripts/sc_indicate.sh b/tests/bsim/bluetooth/host/gatt/sc_indicate/test_scripts/sc_indicate.sh new file mode 100755 index 00000000000..b46ae13929c --- /dev/null +++ b/tests/bsim/bluetooth/host/gatt/sc_indicate/test_scripts/sc_indicate.sh @@ -0,0 +1,24 @@ +#!/bin/env bash +# Copyright 2023 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +source ${ZEPHYR_BASE}/tests/bsim/sh_common.source + +test_name='sc_indicate' +test_exe="bs_${BOARD}_tests_bsim_bluetooth_host_gatt_${test_name}_prj_conf" +simulation_id="${test_name}" +verbosity_level=2 +EXECUTE_TIMEOUT=30 + +cd ${BSIM_OUT_PATH}/bin + +Execute "./${test_exe}" \ + -v=${verbosity_level} -s=${simulation_id} -d=0 -testid=central + +Execute "./${test_exe}" \ + -v=${verbosity_level} -s=${simulation_id} -d=1 -testid=peripheral + +Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} \ + -D=2 -sim_length=60e6 + +wait_for_background_jobs diff --git a/tests/bsim/bluetooth/host/security/bond_per_connection/src/bs_bt_utils.c b/tests/bsim/bluetooth/host/security/bond_per_connection/src/bs_bt_utils.c index 1523e10d8a5..94b27a50b33 100644 --- a/tests/bsim/bluetooth/host/security/bond_per_connection/src/bs_bt_utils.c +++ b/tests/bsim/bluetooth/host/security/bond_per_connection/src/bs_bt_utils.c @@ -88,6 +88,11 @@ DEFINE_FLAG(flag_pairing_complete); DEFINE_FLAG(flag_bonded); DEFINE_FLAG(flag_not_bonded); +static void pairing_failed(struct bt_conn *conn, enum bt_security_err reason) +{ + FAIL("Pairing failed (unexpected): reason %u\n", reason); +} + static void pairing_complete(struct bt_conn *conn, bool bonded) { SET_FLAG(flag_pairing_complete); @@ -100,6 +105,7 @@ static void pairing_complete(struct bt_conn *conn, bool bonded) } static struct bt_conn_auth_info_cb bt_conn_auth_info_cb = { + .pairing_failed = pairing_failed, .pairing_complete = pairing_complete, }; diff --git a/tests/bsim/bluetooth/host/security/bond_per_connection/src/peripheral.c b/tests/bsim/bluetooth/host/security/bond_per_connection/src/peripheral.c index 5e11d6ac712..01956d251df 100644 --- a/tests/bsim/bluetooth/host/security/bond_per_connection/src/peripheral.c +++ b/tests/bsim/bluetooth/host/security/bond_per_connection/src/peripheral.c @@ -33,6 +33,8 @@ void peripheral(void) wait_connected(); /* Central should bond here and trigger a disconnect. */ wait_disconnected(); + TAKE_FLAG(flag_pairing_complete); + TAKE_FLAG(flag_bonded); unpair(id_a); clear_g_conn(); @@ -43,6 +45,8 @@ void peripheral(void) wait_connected(); /* Central should bond here and trigger a disconnect. */ wait_disconnected(); + TAKE_FLAG(flag_pairing_complete); + TAKE_FLAG(flag_bonded); clear_g_conn(); printk("== Bonding id b - bond per-connection false ==\n"); @@ -51,6 +55,8 @@ void peripheral(void) wait_connected(); /* Central should pair without bond here and trigger a disconnect. */ wait_disconnected(); + TAKE_FLAG(flag_pairing_complete); + TAKE_FLAG(flag_not_bonded); PASS("PASS\n"); } diff --git a/tests/bsim/bluetooth/host/security/ccc_update/src/central.c b/tests/bsim/bluetooth/host/security/ccc_update/src/central.c index fbdf7ee0108..8f2d2c17dd4 100644 --- a/tests/bsim/bluetooth/host/security/ccc_update/src/central.c +++ b/tests/bsim/bluetooth/host/security/ccc_update/src/central.c @@ -248,7 +248,7 @@ static void connect_unsubscribe(void) backchannel_sync_wait(SERVER_CLIENT_CHAN, SERVER_ID); } -static void connect_restore_sec_unsubscribe(void) +static void connect_restore_sec(void) { int err; @@ -270,10 +270,7 @@ static void connect_restore_sec_unsubscribe(void) /* wait for server to check that the subscribtion has been restored */ backchannel_sync_wait(SERVER_CLIENT_CHAN, SERVER_ID); - /* send unsubscribtion request */ - ccc_unsubscribe(); - - /* wait for server to check that the unsubscribtion has been well registered */ + /* wait for server to check that the subscription no longer exist */ backchannel_sync_send(SERVER_CLIENT_CHAN, SERVER_ID); } @@ -347,7 +344,7 @@ void run_central(void) backchannel_sync_send(CLIENT_CLIENT_CHAN, BAD_CLIENT_ID); backchannel_sync_wait(CLIENT_CLIENT_CHAN, BAD_CLIENT_ID); - connect_restore_sec_unsubscribe(); + connect_restore_sec(); disconnect(); PASS("Central test passed\n"); diff --git a/tests/bsim/bluetooth/host/security/ccc_update/src/peripheral.c b/tests/bsim/bluetooth/host/security/ccc_update/src/peripheral.c index b9070361577..ac6307dae6a 100644 --- a/tests/bsim/bluetooth/host/security/ccc_update/src/peripheral.c +++ b/tests/bsim/bluetooth/host/security/ccc_update/src/peripheral.c @@ -194,9 +194,9 @@ static void connect_wait_unsubscribtion(struct bt_le_ext_adv *adv) stop_adv(adv); - /* check that subscribtion is not restored for bad client */ - if (is_peer_subscribed(default_conn)) { - FAIL("Subscribtion has been restored for bad client\n"); + /* check that subscribtion is restored for bad client */ + if (!is_peer_subscribed(default_conn)) { + FAIL("Subscribtion has not been restored for bad client\n"); } /* confirm to bad client that the subscribtion had not been restored */ @@ -204,12 +204,12 @@ static void connect_wait_unsubscribtion(struct bt_le_ext_adv *adv) /* wait for confirmation that bad client requested unsubscribtion */ backchannel_sync_wait(BAD_CLIENT_CHAN, BAD_CLIENT_ID); - /* check that unsubscribtion request failed */ - if (GET_FLAG(ccc_cfg_changed_flag)) { - FAIL("Bad client updated CCC config\n"); + /* check that unsubscribtion request didn't fail */ + if (!GET_FLAG(ccc_cfg_changed_flag)) { + FAIL("Bad client didn't manage to update CCC config\n"); } - /* confirm to bad client that unsubscribtion request has been ignored */ + /* confirm to bad client that unsubscribtion request has been well registered */ backchannel_sync_send(BAD_CLIENT_CHAN, BAD_CLIENT_ID); } @@ -225,9 +225,9 @@ static void connect_restore_sec_check_subscribtion(struct bt_le_ext_adv *adv) /* wait for good client end of security update */ backchannel_sync_wait(GOOD_CLIENT_CHAN, GOOD_CLIENT_ID); - /* check that subscribtion has been restored */ - if (!is_peer_subscribed(default_conn)) { - FAIL("Good client is not subscribed\n"); + /* check that subscribtion hasn't been restored */ + if (is_peer_subscribed(default_conn)) { + FAIL("Good client is subscribed\n"); } /* confirm to good client that the subscribtion has been well restored */ @@ -285,6 +285,14 @@ static void check_ccc_handle(void) void run_peripheral(void) { + /* + * test goal: demonstrate the expected behavior of the GATT server when + * a non-bonded peer try to unsubscribe from a previously subscription + * done in a bonded context + * + * test pass if the bad client manage to unsubscribe + */ + int err; struct bt_le_ext_adv *adv = NULL; diff --git a/tests/bsim/bluetooth/ll/cis/Kconfig b/tests/bsim/bluetooth/ll/cis/Kconfig index b59a46be4fc..6076f88ae3c 100644 --- a/tests/bsim/bluetooth/ll/cis/Kconfig +++ b/tests/bsim/bluetooth/ll/cis/Kconfig @@ -16,6 +16,38 @@ config TEST_MULTIPLE_PERIPERAL_CIS help Multiple Peripheral CIS establishment. +config TEST_FT_SKIP_SUBEVENTS + bool + help + Skip central and/or peripheral subevent reception to test flush + timeout implementation. + +config TEST_FT_PER_SKIP_SUBEVENTS + bool "Skip peripheral role subevents to test Flush Timeout" + select TEST_FT_SKIP_SUBEVENTS + help + Skip peripheral role subevent reception to test flush timeout + implementation. + +config TEST_FT_PER_SKIP_EVENTS_COUNT + int "Skip peripheral ISO events count, all subevents in them" + depends on TEST_FT_PER_SKIP_SUBEVENTS + help + Skip peripheral ISO events count where all subevents are skipped. + +config TEST_FT_CEN_SKIP_SUBEVENTS + bool "Skip central role subevents to test Flush Timeout" + select TEST_FT_SKIP_SUBEVENTS + help + Skip central role subevent reception to test flush timeout + implementation. + +config TEST_FT_CEN_SKIP_EVENTS_COUNT + int "Skip central ISO events count, all subevents in them" + depends on TEST_FT_CEN_SKIP_SUBEVENTS + help + Skip central ISO events count where all subevents are skipped. + config BT_CTLR_SCAN_UNRESERVED default y if TEST_CONNECT_ACL_FIRST help diff --git a/tests/bsim/bluetooth/ll/cis/overlay-acl_first_ft_cen_skip_2_se.conf b/tests/bsim/bluetooth/ll/cis/overlay-acl_first_ft_cen_skip_2_se.conf new file mode 100644 index 00000000000..194a23ffec6 --- /dev/null +++ b/tests/bsim/bluetooth/ll/cis/overlay-acl_first_ft_cen_skip_2_se.conf @@ -0,0 +1,9 @@ +CONFIG_TEST_USE_LEGACY_ADVERTISING=n +CONFIG_TEST_CONNECT_ACL_FIRST=y +CONFIG_TEST_FT_CEN_SKIP_SUBEVENTS=y +CONFIG_TEST_FT_CEN_SKIP_EVENTS_COUNT=1 +CONFIG_BT_MAX_CONN=1 +CONFIG_BT_ISO_MAX_CHAN=1 +CONFIG_BT_CTLR_LLCP_LOCAL_PROC_CTX_BUF_NUM=9 +CONFIG_BT_CTLR_ADVANCED_FEATURES=y +CONFIG_BT_CTLR_ISOAL_PSN_IGNORE=y diff --git a/tests/bsim/bluetooth/ll/cis/overlay-acl_first_ft_per_skip_2_se.conf b/tests/bsim/bluetooth/ll/cis/overlay-acl_first_ft_per_skip_2_se.conf new file mode 100644 index 00000000000..bfbe36115f5 --- /dev/null +++ b/tests/bsim/bluetooth/ll/cis/overlay-acl_first_ft_per_skip_2_se.conf @@ -0,0 +1,9 @@ +CONFIG_TEST_USE_LEGACY_ADVERTISING=n +CONFIG_TEST_CONNECT_ACL_FIRST=y +CONFIG_TEST_FT_PER_SKIP_SUBEVENTS=y +CONFIG_TEST_FT_PER_SKIP_EVENTS_COUNT=1 +CONFIG_BT_MAX_CONN=1 +CONFIG_BT_ISO_MAX_CHAN=1 +CONFIG_BT_CTLR_LLCP_LOCAL_PROC_CTX_BUF_NUM=9 +CONFIG_BT_CTLR_ADVANCED_FEATURES=y +CONFIG_BT_CTLR_ISOAL_PSN_IGNORE=y diff --git a/tests/bsim/bluetooth/ll/cis/prj.conf b/tests/bsim/bluetooth/ll/cis/prj.conf index 0b6b764393b..a4d3c3e8c5f 100644 --- a/tests/bsim/bluetooth/ll/cis/prj.conf +++ b/tests/bsim/bluetooth/ll/cis/prj.conf @@ -13,6 +13,7 @@ CONFIG_BT_MAX_CONN=9 CONFIG_BT_ISO_MAX_CHAN=9 CONFIG_BT_ISO_TX_BUF_COUNT=18 CONFIG_BT_ISO_TX_MTU=120 +CONFIG_BT_ISO_RX_MTU=120 CONFIG_BT_BUF_CMD_TX_SIZE=255 CONFIG_BT_BUF_EVT_RX_SIZE=255 diff --git a/tests/bsim/bluetooth/ll/cis/src/main.c b/tests/bsim/bluetooth/ll/cis/src/main.c index a9538e3e9c6..b544546a404 100644 --- a/tests/bsim/bluetooth/ll/cis/src/main.c +++ b/tests/bsim/bluetooth/ll/cis/src/main.c @@ -53,6 +53,7 @@ static bt_addr_le_t peer_addr; #define ISO_INTERVAL_US 10000U #define ISO_LATENCY_MS DIV_ROUND_UP(ISO_INTERVAL_US, USEC_PER_MSEC) +#define ISO_LATENCY_FT_MS 20U #define BT_CONN_US_TO_INTERVAL(t) ((uint16_t)((t) * 4U / 5U / USEC_PER_MSEC)) @@ -103,7 +104,7 @@ static bt_addr_le_t peer_addr; #define NAME_LEN 30 -#define BUF_ALLOC_TIMEOUT (30) /* milliseconds */ +#define BUF_ALLOC_TIMEOUT (40) /* milliseconds */ NET_BUF_POOL_FIXED_DEFINE(tx_pool, CONFIG_BT_ISO_TX_BUF_COUNT, BT_ISO_SDU_BUF_SIZE(CONFIG_BT_ISO_TX_MTU), 8, NULL); @@ -295,13 +296,20 @@ static void iso_recv(struct bt_iso_chan *chan, const struct bt_iso_recv_info *in seq_num = sys_get_le32(buf->data); if (info->flags & BT_ISO_FLAGS_VALID) { if (seq_num != expected_seq_num[index]) { - FAIL("ISO data miss match, expected %u actual %u\n", - expected_seq_num[index], seq_num); + if (expected_seq_num[index]) { + FAIL("ISO data miss match, expected %u actual %u\n", + expected_seq_num[index], seq_num); + } expected_seq_num[index] = seq_num; } - expected_seq_num[index]++; + expected_seq_num[index] += 1U; +#if defined(CONFIG_TEST_FT_PER_SKIP_SUBEVENTS) + expected_seq_num[index] += ((CONFIG_TEST_FT_PER_SKIP_EVENTS_COUNT - 1U) * 2U); +#elif defined(CONFIG_TEST_FT_CEN_SKIP_SUBEVENTS) + expected_seq_num[index] += ((CONFIG_TEST_FT_CEN_SKIP_EVENTS_COUNT - 1U) * 2U); +#endif } else if (expected_seq_num[index] && expected_seq_num[index] < SEQ_NUM_MAX) { FAIL("%s: Invalid ISO data after valid ISO data reception.\n" @@ -338,12 +346,13 @@ static struct bt_iso_chan_ops iso_ops = { static void test_cis_central(void) { struct bt_iso_chan_io_qos iso_tx[CONFIG_BT_ISO_MAX_CHAN]; + struct bt_iso_chan_io_qos iso_rx[CONFIG_BT_ISO_MAX_CHAN]; struct bt_iso_chan_qos iso_qos[CONFIG_BT_ISO_MAX_CHAN]; struct bt_iso_chan *channels[CONFIG_BT_ISO_MAX_CHAN]; struct bt_conn *conn_list[CONFIG_BT_MAX_CONN]; struct bt_iso_cig_param cig_param; struct bt_iso_cig *cig; - uint8_t conn_count; + int conn_count; int err; printk("Bluetooth initializing..."); @@ -361,11 +370,34 @@ static void test_cis_central(void) for (int i = 0; i < CONFIG_BT_ISO_MAX_CHAN; i++) { iso_tx[i].sdu = CONFIG_BT_ISO_TX_MTU; iso_tx[i].phy = BT_GAP_LE_PHY_2M; - iso_tx[i].rtn = 0U; iso_tx[i].path = NULL; + if (IS_ENABLED(CONFIG_TEST_FT_SKIP_SUBEVENTS)) { + iso_tx[i].rtn = 2U; + } else { + iso_tx[i].rtn = 0U; + } + + if (!IS_ENABLED(CONFIG_TEST_FT_SKIP_SUBEVENTS) || + IS_ENABLED(CONFIG_TEST_FT_PER_SKIP_SUBEVENTS)) { + iso_qos[i].tx = &iso_tx[i]; + } else { + iso_qos[i].tx = NULL; + } - iso_qos[i].tx = &iso_tx[i]; - iso_qos[i].rx = NULL; + iso_rx[i].sdu = CONFIG_BT_ISO_RX_MTU; + iso_rx[i].phy = BT_GAP_LE_PHY_2M; + iso_rx[i].path = NULL; + if (IS_ENABLED(CONFIG_TEST_FT_SKIP_SUBEVENTS)) { + iso_rx[i].rtn = 2U; + } else { + iso_rx[i].rtn = 0U; + } + + if (IS_ENABLED(CONFIG_TEST_FT_CEN_SKIP_SUBEVENTS)) { + iso_qos[i].rx = &iso_rx[i]; + } else { + iso_qos[i].rx = NULL; + } iso_chan[i].ops = &iso_ops; iso_chan[i].qos = &iso_qos[i]; @@ -381,8 +413,12 @@ static void test_cis_central(void) cig_param.sca = BT_GAP_SCA_UNKNOWN; cig_param.packing = 0U; cig_param.framing = 0U; - cig_param.latency = ISO_LATENCY_MS; cig_param.interval = ISO_INTERVAL_US; + if (IS_ENABLED(CONFIG_TEST_FT_SKIP_SUBEVENTS)) { + cig_param.latency = ISO_LATENCY_FT_MS; + } else { + cig_param.latency = ISO_LATENCY_MS; + } printk("Create CIG..."); err = bt_iso_cig_create(&cig_param, &cig); @@ -392,7 +428,13 @@ static void test_cis_central(void) } printk("success.\n"); - conn_count = 0U; + conn_count = 0; + +#if defined(CONFIG_TEST_FT_CEN_SKIP_SUBEVENTS) + for (int chan = 0; chan < CONFIG_BT_ISO_MAX_CHAN; chan++) { + expected_seq_num[chan] = (CONFIG_TEST_FT_CEN_SKIP_EVENTS_COUNT - 1U) * 2U; + } +#endif #if !defined(CONFIG_TEST_MULTIPLE_PERIPERAL_CIS) for (int i = 0; i < CONFIG_BT_MAX_CONN; i++) { @@ -401,10 +443,10 @@ static void test_cis_central(void) #endif struct bt_conn *conn; - uint8_t conn_index; - uint8_t chan; + int conn_index; + int chan; - printk("Start scanning..."); + printk("Start scanning (%d)...", i); err = bt_le_scan_start(BT_LE_SCAN_CUSTOM, NULL); if (err) { FAIL("Could not start scan: %d\n", err); @@ -452,12 +494,12 @@ static void test_cis_central(void) #if defined(CONFIG_TEST_CONNECT_ACL_FIRST) } - for (uint8_t chan = 0U, conn_index = 0U; + for (int chan = 0, conn_index = 0; (conn_index < conn_count) && (chan < CONFIG_BT_ISO_MAX_CHAN); conn_index++, chan++) { #elif defined(CONFIG_TEST_MULTIPLE_PERIPERAL_CIS) - for (uint8_t chan = 0U, conn_index = 0U; + for (int chan = 0, conn_index = 0; (chan < CONFIG_BT_ISO_MAX_CHAN); chan++) { #endif @@ -481,66 +523,67 @@ static void test_cis_central(void) printk("connected to peer %d ISO channel.\n", chan); } - for (uint16_t seq_num = 0U; seq_num < SEQ_NUM_MAX; seq_num++) { - - for (uint8_t chan = 0U; chan < CONFIG_BT_ISO_MAX_CHAN; chan++) { - uint8_t iso_data[CONFIG_BT_ISO_TX_MTU] = { 0, }; - struct net_buf *buf; - int ret; - - buf = net_buf_alloc(&tx_pool, - K_MSEC(BUF_ALLOC_TIMEOUT)); - if (!buf) { - FAIL("Data buffer allocate timeout on channel" - " %u\n", chan); - return; - } - net_buf_reserve(buf, BT_ISO_CHAN_SEND_RESERVE); - sys_put_le32(seq_num, iso_data); - net_buf_add_mem(buf, iso_data, sizeof(iso_data)); - - ret = k_sem_take(&sem_iso_data, - K_MSEC(BUF_ALLOC_TIMEOUT)); - if (ret) { - FAIL("k_sem_take for ISO data sent failed.\n"); - return; + if (!IS_ENABLED(CONFIG_TEST_FT_SKIP_SUBEVENTS) || + IS_ENABLED(CONFIG_TEST_FT_PER_SKIP_SUBEVENTS)) { + for (uint16_t seq_num = 0U; seq_num < SEQ_NUM_MAX; seq_num++) { + + for (int chan = 0; chan < CONFIG_BT_ISO_MAX_CHAN; chan++) { + uint8_t iso_data[CONFIG_BT_ISO_TX_MTU] = { 0, }; + struct net_buf *buf; + int ret; + + buf = net_buf_alloc(&tx_pool, K_MSEC(BUF_ALLOC_TIMEOUT)); + if (!buf) { + FAIL("Data buffer allocate timeout on channel %d\n", chan); + return; + } + + net_buf_reserve(buf, BT_ISO_CHAN_SEND_RESERVE); + sys_put_le16(seq_num, iso_data); + net_buf_add_mem(buf, iso_data, sizeof(iso_data)); + + ret = k_sem_take(&sem_iso_data, K_MSEC(BUF_ALLOC_TIMEOUT)); + if (ret) { + FAIL("k_sem_take for ISO data sent failed.\n"); + return; + } + + printk("ISO send: seq_num %u, chan %d\n", seq_num, chan); + ret = bt_iso_chan_send(&iso_chan[chan], buf, + seq_num, BT_ISO_TIMESTAMP_NONE); + if (ret < 0) { + FAIL("Unable to send data on channel %d : %d\n", chan, ret); + net_buf_unref(buf); + return; + } } - printk("ISO send: seq_num %u, chan %u\n", seq_num, chan); - ret = bt_iso_chan_send(&iso_chan[chan], buf, - seq_num, BT_ISO_TIMESTAMP_NONE); - if (ret < 0) { - FAIL("Unable to broadcast data on channel %u" - " : %d\n", chan, ret); - net_buf_unref(buf); - return; + if ((seq_num % 100) == 0) { + printk("Sending value %u\n", seq_num); } } - if ((seq_num % 100) == 0) { - printk("Sending value %u\n", seq_num); - } + k_sleep(K_MSEC(1000)); + } else { + k_sleep(K_SECONDS(11)); } - k_sleep(K_MSEC(100)); - - for (uint8_t chan = 0U; chan < CONFIG_BT_ISO_MAX_CHAN; chan++) { - printk("ISO disconnect channel %u...", chan); + for (int chan = 0; chan < CONFIG_BT_ISO_MAX_CHAN; chan++) { + printk("ISO disconnect channel %d...", chan); err = bt_iso_chan_disconnect(&iso_chan[chan]); if (err) { - FAIL("Failed to disconnect channel %u (%d)\n", - chan, err); + FAIL("Failed to disconnect channel %d (%d)\n", chan, err); return; } printk("success\n"); - printk("Waiting for ISO channel disconnect %u...", chan); + printk("Waiting for ISO channel disconnect %d...", chan); err = k_sem_take(&sem_iso_disc, K_FOREVER); if (err) { FAIL("failed (err %d)\n", err); return; } - printk("disconnected to peer %u ISO channel.\n", chan); + printk("disconnected to peer %d ISO channel.\n", chan); } bt_conn_foreach(BT_CONN_TYPE_LE, disconnect, NULL); @@ -561,6 +604,16 @@ static void test_cis_central(void) } #endif + if (IS_ENABLED(CONFIG_TEST_FT_CEN_SKIP_SUBEVENTS)) { + for (int chan = 0; chan < CONFIG_BT_ISO_MAX_CHAN; chan++) { + if (expected_seq_num[chan] < SEQ_NUM_MAX) { + FAIL("ISO Data reception incomplete %u (%u).\n", + expected_seq_num[chan], SEQ_NUM_MAX); + return; + } + } + } + PASS("Central ISO tests Passed\n"); } @@ -587,6 +640,8 @@ static int iso_accept(const struct bt_iso_accept_info *info, *chan = &iso_chan_p[chan_count]; chan_count++; + printk("Accepted on channel %p\n", *chan); + return 0; } @@ -599,6 +654,7 @@ static struct bt_iso_server iso_server = { static void test_cis_peripheral(void) { + struct bt_iso_chan_io_qos iso_tx_p[CONFIG_BT_ISO_MAX_CHAN]; int err; printk("Bluetooth initializing..."); @@ -610,7 +666,18 @@ static void test_cis_peripheral(void) printk("success.\n"); for (int i = 0; i < CONFIG_BT_ISO_MAX_CHAN; i++) { - iso_rx_p[i].sdu = CONFIG_BT_ISO_TX_MTU; + iso_tx_p[i].sdu = CONFIG_BT_ISO_TX_MTU; + iso_tx_p[i].phy = BT_GAP_LE_PHY_2M; + iso_tx_p[i].path = NULL; + if (IS_ENABLED(CONFIG_TEST_FT_SKIP_SUBEVENTS)) { + iso_tx_p[i].rtn = 2U; + } else { + iso_tx_p[i].rtn = 0U; + } + + iso_qos_p[i].tx = &iso_tx_p[i]; + + iso_rx_p[i].sdu = CONFIG_BT_ISO_RX_MTU; iso_qos_p[i].rx = &iso_rx_p[i]; @@ -671,7 +738,7 @@ static void test_cis_peripheral(void) printk("connected to peer central.\n"); #if defined(CONFIG_TEST_MULTIPLE_PERIPERAL_CIS) - for (uint8_t chan = 0U; chan < CONFIG_BT_ISO_MAX_CHAN; chan++) { + for (int chan = 0; chan < CONFIG_BT_ISO_MAX_CHAN; chan++) { #endif printk("Waiting for ISO channel connection..."); @@ -684,8 +751,49 @@ static void test_cis_peripheral(void) #if defined(CONFIG_TEST_MULTIPLE_PERIPERAL_CIS) } +#endif + + if (IS_ENABLED(CONFIG_TEST_FT_CEN_SKIP_SUBEVENTS)) { + for (uint16_t seq_num = 0U; seq_num < SEQ_NUM_MAX; seq_num++) { + for (int chan = 0; chan < CONFIG_BT_ISO_MAX_CHAN; chan++) { + uint8_t iso_data[CONFIG_BT_ISO_TX_MTU] = { 0, }; + struct net_buf *buf; + int ret; + + buf = net_buf_alloc(&tx_pool, K_MSEC(BUF_ALLOC_TIMEOUT)); + if (!buf) { + FAIL("Data buffer allocate timeout on channel %d\n", chan); + return; + } + + net_buf_reserve(buf, BT_ISO_CHAN_SEND_RESERVE); + sys_put_le16(seq_num, iso_data); + net_buf_add_mem(buf, iso_data, sizeof(iso_data)); + + ret = k_sem_take(&sem_iso_data, K_MSEC(BUF_ALLOC_TIMEOUT)); + if (ret) { + FAIL("k_sem_take for ISO data sent failed.\n"); + return; + } + + printk("ISO send: seq_num %u, chan %d\n", seq_num, chan); + ret = bt_iso_chan_send(&iso_chan_p[chan], buf, seq_num, + BT_ISO_TIMESTAMP_NONE); + if (ret < 0) { + FAIL("Unable to send data on channel %d : %d\n", chan, ret); + net_buf_unref(buf); + return; + } + } - for (uint8_t chan = 0U; chan < CONFIG_BT_ISO_MAX_CHAN; chan++) { + if ((seq_num % 100) == 0) { + printk("Sending value %u\n", seq_num); + } + } + } + +#if defined(CONFIG_TEST_MULTIPLE_PERIPERAL_CIS) + for (int chan = 0; chan < CONFIG_BT_ISO_MAX_CHAN; chan++) { #endif printk("Waiting for ISO channel disconnect..."); @@ -708,18 +816,20 @@ static void test_cis_peripheral(void) } printk("disconnected from peer device.\n"); +#if !defined(CONFIG_TEST_FT_SKIP_SUBEVENTS) || defined(CONFIG_TEST_FT_PER_SKIP_SUBEVENTS) #if defined(CONFIG_TEST_MULTIPLE_PERIPERAL_CIS) - for (uint8_t chan = 0U; chan < CONFIG_BT_ISO_MAX_CHAN; chan++) { + for (int chan = 0; chan < CONFIG_BT_ISO_MAX_CHAN; chan++) { #else - uint8_t chan = 0U; + int chan = 0; #endif if (expected_seq_num[chan] < SEQ_NUM_MAX) { - FAIL("ISO Data reception incomplete %u (%u).\n", - expected_seq_num[chan], SEQ_NUM_MAX); + FAIL("ISO Data reception incomplete %u (%u).\n", expected_seq_num[chan], + SEQ_NUM_MAX); return; } #if defined(CONFIG_TEST_MULTIPLE_PERIPERAL_CIS) } +#endif #endif PASS("Peripheral ISO tests Passed\n"); diff --git a/tests/bsim/bluetooth/ll/cis/tests_scripts/connected_iso_acl_first_ft_cen_skip_2_se.sh b/tests/bsim/bluetooth/ll/cis/tests_scripts/connected_iso_acl_first_ft_cen_skip_2_se.sh new file mode 100755 index 00000000000..bb08f582574 --- /dev/null +++ b/tests/bsim/bluetooth/ll/cis/tests_scripts/connected_iso_acl_first_ft_cen_skip_2_se.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# Copyright 2023 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +source ${ZEPHYR_BASE}/tests/bsim/sh_common.source + +# Basic Connected ISO test: a Central connects to 1 Peripheral and tests RTN=2, +# FT=2, skips 2 subevents in the central +simulation_id="connected_iso_acl_first_ft_cen_skip_2_se" +verbosity_level=2 +EXECUTE_TIMEOUT=60 + +cd ${BSIM_OUT_PATH}/bin + +Execute ./bs_${BOARD}_tests_bsim_bluetooth_ll_cis_prj_conf_overlay-acl_first_ft_cen_skip_2_se_conf \ + -v=${verbosity_level} -s=${simulation_id} -d=0 -testid=central + +Execute ./bs_${BOARD}_tests_bsim_bluetooth_ll_cis_prj_conf_overlay-acl_first_ft_cen_skip_2_se_conf \ + -v=${verbosity_level} -s=${simulation_id} -d=1 -testid=peripheral + +Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} \ + -D=2 -sim_length=30e6 $@ + +wait_for_background_jobs diff --git a/tests/bsim/bluetooth/ll/cis/tests_scripts/connected_iso_acl_first_ft_per_skip_2_se.sh b/tests/bsim/bluetooth/ll/cis/tests_scripts/connected_iso_acl_first_ft_per_skip_2_se.sh new file mode 100755 index 00000000000..84d3c5c4ed3 --- /dev/null +++ b/tests/bsim/bluetooth/ll/cis/tests_scripts/connected_iso_acl_first_ft_per_skip_2_se.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# Copyright 2023 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +source ${ZEPHYR_BASE}/tests/bsim/sh_common.source + +# Basic Connected ISO test: a Central connects to 1 Peripheral and tests RTN=2, +# FT=2, skips 2 subevents in the peripheral +simulation_id="connected_iso_acl_first_ft_per_skip_2_se" +verbosity_level=2 +EXECUTE_TIMEOUT=60 + +cd ${BSIM_OUT_PATH}/bin + +Execute ./bs_${BOARD}_tests_bsim_bluetooth_ll_cis_prj_conf_overlay-acl_first_ft_per_skip_2_se_conf \ + -v=${verbosity_level} -s=${simulation_id} -d=0 -testid=central + +Execute ./bs_${BOARD}_tests_bsim_bluetooth_ll_cis_prj_conf_overlay-acl_first_ft_per_skip_2_se_conf \ + -v=${verbosity_level} -s=${simulation_id} -d=1 -testid=peripheral + +Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} \ + -D=2 -sim_length=30e6 $@ + +wait_for_background_jobs diff --git a/tests/bsim/bluetooth/ll/compile.sh b/tests/bsim/bluetooth/ll/compile.sh index 2369192e0cc..6d530d05d72 100755 --- a/tests/bsim/bluetooth/ll/compile.sh +++ b/tests/bsim/bluetooth/ll/compile.sh @@ -39,6 +39,9 @@ app=tests/bsim/bluetooth/ll/cis conf_overlay=overlay-legacy_adv_acl_first.conf c app=tests/bsim/bluetooth/ll/cis conf_overlay=overlay-acl_group.conf compile app=tests/bsim/bluetooth/ll/cis conf_overlay=overlay-acl_group_acl_first.conf compile app=tests/bsim/bluetooth/ll/cis conf_overlay=overlay-peripheral_cis.conf compile +app=tests/bsim/bluetooth/ll/cis conf_overlay=overlay-acl_first_ft_per_skip_2_se.conf compile +app=tests/bsim/bluetooth/ll/cis conf_overlay=overlay-acl_first_ft_per_skip_4_se.conf compile +app=tests/bsim/bluetooth/ll/cis conf_overlay=overlay-acl_first_ft_cen_skip_2_se.conf compile app=tests/bsim/bluetooth/ll/edtt/hci_test_app \ conf_file=prj_dut_llcp.conf compile diff --git a/tests/bsim/bluetooth/mesh/overlay_psa.conf b/tests/bsim/bluetooth/mesh/overlay_psa.conf index ba81c1e0213..1957085b7e4 100644 --- a/tests/bsim/bluetooth/mesh/overlay_psa.conf +++ b/tests/bsim/bluetooth/mesh/overlay_psa.conf @@ -1,2 +1,7 @@ +# The option adds TinyCrypt based bt_rand. +CONFIG_BT_HOST_CRYPTO=n +# The option adds GATT caching feature that is based on TinyCrypt. +CONFIG_BT_GATT_CACHING=n + # Enable mbedTLS PSA as a crypto backend CONFIG_BT_MESH_USES_MBEDTLS_PSA=y diff --git a/tests/bsim/bluetooth/mesh/prj_mesh1d1.conf b/tests/bsim/bluetooth/mesh/prj_mesh1d1.conf index e870e190481..2e08329b7ef 100644 --- a/tests/bsim/bluetooth/mesh/prj_mesh1d1.conf +++ b/tests/bsim/bluetooth/mesh/prj_mesh1d1.conf @@ -58,7 +58,8 @@ CONFIG_BT_MESH_LARGE_COMP_DATA_SRV=y CONFIG_BT_MESH_DFU_SRV=y CONFIG_BT_MESH_DFU_CLI=y CONFIG_BT_MESH_DFD_SRV=y -CONFIG_BT_MESH_DFU_SLOT_CNT=3 +CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD=y +CONFIG_BT_MESH_DFU_SLOT_CNT=4 CONFIG_BT_MESH_PRIV_BEACON_SRV=y CONFIG_BT_MESH_PRIV_BEACON_CLI=y CONFIG_BT_MESH_COMP_PAGE_1=y diff --git a/tests/bsim/bluetooth/mesh/src/test_advertiser.c b/tests/bsim/bluetooth/mesh/src/test_advertiser.c index 590eed8eebc..6a171b499bf 100644 --- a/tests/bsim/bluetooth/mesh/src/test_advertiser.c +++ b/tests/bsim/bluetooth/mesh/src/test_advertiser.c @@ -81,7 +81,7 @@ static void adv_init(void) static void allocate_all_array(struct net_buf **buf, size_t num_buf, uint8_t xmit) { for (int i = 0; i < num_buf; i++) { - *buf = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_LOCAL_ADV, + *buf = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_ADV_TAG_LOCAL, xmit, K_NO_WAIT); ASSERT_FALSE(!*buf, "Out of buffers"); @@ -94,7 +94,7 @@ static void verify_adv_queue_overflow(void) struct net_buf *dummy_buf; /* Verity Queue overflow */ - dummy_buf = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_LOCAL_ADV, + dummy_buf = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_ADV_TAG_LOCAL, BT_MESH_TRANSMIT(2, 20), K_NO_WAIT); ASSERT_TRUE(!dummy_buf, "Unexpected extra buffer"); } @@ -160,7 +160,7 @@ static void realloc_end_cb(int err, void *cb_data) struct net_buf *buf = (struct net_buf *)cb_data; ASSERT_EQUAL(0, err); - buf = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_LOCAL_ADV, + buf = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_ADV_TAG_LOCAL, BT_MESH_TRANSMIT(2, 20), K_NO_WAIT); ASSERT_FALSE(!buf, "Out of buffers"); @@ -425,7 +425,7 @@ static void test_tx_cb_single(void) bt_init(); adv_init(); - buf = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_LOCAL_ADV, + buf = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_ADV_TAG_LOCAL, BT_MESH_TRANSMIT(2, 20), K_NO_WAIT); ASSERT_FALSE(!buf, "Out of buffers"); @@ -530,7 +530,7 @@ static void test_tx_proxy_mixin(void) * Advertising the proxy service should be resumed after * finishing advertising the message. */ - struct net_buf *buf = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_LOCAL_ADV, + struct net_buf *buf = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_ADV_TAG_LOCAL, BT_MESH_TRANSMIT(5, 20), K_NO_WAIT); net_buf_add_mem(buf, txt_msg, sizeof(txt_msg)); bt_mesh_adv_send(buf, NULL, NULL); @@ -636,16 +636,16 @@ static void test_tx_random_order(void) /* Verify random order calls */ num_adv_sent = ARRAY_SIZE(buf); previous_checker = 0xff; - buf[0] = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_LOCAL_ADV, + buf[0] = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_ADV_TAG_LOCAL, xmit, K_NO_WAIT); - ASSERT_FALSE(!buf[0], "Out of buffers"); - buf[1] = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_LOCAL_ADV, + ASSERT_FALSE_MSG(!buf[0], "Out of buffers\n"); + buf[1] = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_ADV_TAG_LOCAL, xmit, K_NO_WAIT); ASSERT_FALSE(!buf[1], "Out of buffers"); send_adv_buf(buf[0], 0, 0xff); - buf[2] = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_LOCAL_ADV, + buf[2] = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_ADV_TAG_LOCAL, xmit, K_NO_WAIT); ASSERT_FALSE(!buf[2], "Out of buffers"); diff --git a/tests/bsim/bluetooth/mesh/src/test_dfu.c b/tests/bsim/bluetooth/mesh/src/test_dfu.c index f7c4ca6f4f0..46ad4cecb41 100644 --- a/tests/bsim/bluetooth/mesh/src/test_dfu.c +++ b/tests/bsim/bluetooth/mesh/src/test_dfu.c @@ -23,6 +23,7 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME, LOG_LEVEL_INF); #define TARGET_ADDR 0x0100 #define IMPOSTER_MODEL_ID 0xe000 #define TEST_BLOB_ID 0xaabbccdd +#define SEMAPHORE_TIMEOUT 250 /* seconds */ struct bind_params { uint16_t model_id; @@ -434,25 +435,50 @@ static void target_prov_and_conf_default(void) target_prov_and_conf(addr, bind_params, ARRAY_SIZE(bind_params)); } +static struct bt_mesh_dfu_slot *slot_reserve_and_set(size_t size, uint8_t *fwid, size_t fwid_len, + uint8_t *metadata, size_t metadata_len) +{ + struct bt_mesh_dfu_slot *new_slot = bt_mesh_dfu_slot_reserve(); + + if (!new_slot) { + LOG_WRN("Reserving slot failed"); + return NULL; + } + + int err = bt_mesh_dfu_slot_fwid_set(new_slot, fwid, fwid_len); + + if (err) { + return NULL; + } + + err = bt_mesh_dfu_slot_info_set(new_slot, size, metadata, metadata_len); + + if (err) { + return NULL; + } + + return new_slot; +} + static bool slot_add(const struct bt_mesh_dfu_slot **slot) { - const struct bt_mesh_dfu_slot *new_slot; + struct bt_mesh_dfu_slot *new_slot; size_t size = 100; uint8_t fwid[CONFIG_BT_MESH_DFU_FWID_MAXLEN] = { 0xAA, 0xBB, 0xCC, 0xDD }; size_t fwid_len = 4; uint8_t metadata[CONFIG_BT_MESH_DFU_METADATA_MAXLEN] = { 0xAA, 0xBB, 0xCC, 0xDD }; size_t metadata_len = 4; - const char *uri = ""; ASSERT_EQUAL(sizeof(target_fw_ver_new), fwid_len); - new_slot = bt_mesh_dfu_slot_add(size, fwid, fwid_len, metadata, metadata_len, uri, - strlen(uri)); + new_slot = slot_reserve_and_set(size, fwid, fwid_len, metadata, metadata_len); if (!new_slot) { return false; } - bt_mesh_dfu_slot_valid_set(new_slot, true); + if (bt_mesh_dfu_slot_commit(new_slot) != 0) { + return false; + } if (slot) { *slot = new_slot; @@ -568,13 +594,12 @@ static void test_dist_dfu_self_update(void) static void test_dist_dfu_slot_create(void) { - const struct bt_mesh_dfu_slot *slot[3]; + struct bt_mesh_dfu_slot *slot[CONFIG_BT_MESH_DFU_SLOT_CNT]; size_t size = 100; uint8_t fwid[CONFIG_BT_MESH_DFU_FWID_MAXLEN] = { 0 }; size_t fwid_len = 4; uint8_t metadata[CONFIG_BT_MESH_DFU_METADATA_MAXLEN] = { 0 }; size_t metadata_len = 4; - const char *uri = "test"; int err, i; ASSERT_TRUE(CONFIG_BT_MESH_DFU_SLOT_CNT >= 3, @@ -584,36 +609,26 @@ static void test_dist_dfu_slot_create(void) bt_mesh_device_setup(&prov, &dist_comp); dist_prov_and_conf(DIST_ADDR); - for (i = CONFIG_BT_MESH_DFU_SLOT_CNT - 1; i >= 0; i--) { + for (i = 0; i < CONFIG_BT_MESH_DFU_SLOT_CNT; i++) { fwid[0] = i; metadata[0] = i; - slot[i] = bt_mesh_dfu_slot_add(size, fwid, fwid_len, metadata, metadata_len, uri, - strlen(uri)); + slot[i] = slot_reserve_and_set(size, fwid, fwid_len, metadata, metadata_len); ASSERT_FALSE(slot[i] == NULL, "Failed to add slot"); - } - - /* First slot is set as valid */ - err = bt_mesh_dfu_slot_valid_set(slot[0], true); - if (err) { - FAIL("Setting slot to valid state failed (err %d)", err); - return; - } - ASSERT_TRUE(bt_mesh_dfu_slot_is_valid(slot[0])); - /* Second slot is set as invalid */ - err = bt_mesh_dfu_slot_valid_set(slot[1], false); - if (err) { - FAIL("Setting slot to invalid state failed (err %d)", err); - return; + if (i > 0) { + /* All but first slot are committed */ + err = bt_mesh_dfu_slot_commit(slot[i]); + if (err) { + FAIL("Committing slot failed (err %d)", err); + } + } } - ASSERT_TRUE(!bt_mesh_dfu_slot_is_valid(slot[1])); - /* Last slot is deleted */ - err = bt_mesh_dfu_slot_del(slot[CONFIG_BT_MESH_DFU_SLOT_CNT - 1]); + /* Second slot is deleted */ + err = bt_mesh_dfu_slot_del(slot[1]); if (err) { FAIL("Slot delete failed (err %d)", err); - return; } PASS(); @@ -626,20 +641,17 @@ enum bt_mesh_dfu_iter check_slot(const struct bt_mesh_dfu_slot *slot, void *data size_t fwid_len = 4; uint8_t metadata[CONFIG_BT_MESH_DFU_METADATA_MAXLEN] = { 0 }; size_t metadata_len = 4; - const char *uri = "test"; - int idx = bt_mesh_dfu_slot_idx_get(slot); - - ASSERT_TRUE(idx >= 0, "Failed to retrieve slot index"); + int idx = bt_mesh_dfu_slot_img_idx_get(slot); + int *i = data; + ASSERT_EQUAL(idx, (*i)++); ASSERT_EQUAL(size, slot->size); - ASSERT_TRUE(strcmp(uri, slot->uri) == 0); - - fwid[0] = idx; + fwid[0] = idx + 2; ASSERT_EQUAL(fwid_len, slot->fwid_len); ASSERT_TRUE(memcmp(fwid, slot->fwid, fwid_len) == 0); - metadata[0] = idx; + metadata[0] = idx + 2; ASSERT_EQUAL(metadata_len, slot->metadata_len); ASSERT_TRUE(memcmp(metadata, slot->metadata, metadata_len) == 0); @@ -649,13 +661,12 @@ enum bt_mesh_dfu_iter check_slot(const struct bt_mesh_dfu_slot *slot, void *data static void test_dist_dfu_slot_create_recover(void) { size_t slot_count; - const struct bt_mesh_dfu_slot *slot; + struct bt_mesh_dfu_slot *slot; size_t size = 100; uint8_t fwid[CONFIG_BT_MESH_DFU_FWID_MAXLEN] = { 0 }; size_t fwid_len = 4; uint8_t metadata[CONFIG_BT_MESH_DFU_METADATA_MAXLEN] = { 0 }; size_t metadata_len = 4; - const char *uri = "test"; int i, idx; ASSERT_TRUE(CONFIG_BT_MESH_DFU_SLOT_CNT >= 3, @@ -664,26 +675,17 @@ static void test_dist_dfu_slot_create_recover(void) bt_mesh_test_cfg_set(NULL, WAIT_TIME); bt_mesh_device_setup(&prov, &dist_comp); - slot_count = bt_mesh_dfu_slot_foreach(check_slot, NULL); - ASSERT_EQUAL(CONFIG_BT_MESH_DFU_SLOT_CNT - 1, slot_count); - - slot = bt_mesh_dfu_slot_at(0); - ASSERT_EQUAL(true, bt_mesh_dfu_slot_is_valid(slot)); + i = 0; + slot_count = bt_mesh_dfu_slot_foreach(check_slot, &i); + ASSERT_EQUAL(CONFIG_BT_MESH_DFU_SLOT_CNT - 2, slot_count); - slot = bt_mesh_dfu_slot_at(1); - ASSERT_TRUE(slot != NULL); - ASSERT_EQUAL(false, bt_mesh_dfu_slot_is_valid(slot)); - - for (i = 0; i < (CONFIG_BT_MESH_DFU_SLOT_CNT - 1); i++) { + for (i = 2; i < CONFIG_BT_MESH_DFU_SLOT_CNT; i++) { fwid[0] = i; idx = bt_mesh_dfu_slot_get(fwid, fwid_len, &slot); - ASSERT_TRUE(idx >= 0); - ASSERT_EQUAL(idx, bt_mesh_dfu_slot_idx_get(slot)); - + ASSERT_EQUAL(idx, i - 2); ASSERT_EQUAL(size, slot->size); - ASSERT_TRUE(strcmp(uri, slot->uri) == 0); - metadata[0] = idx; + metadata[0] = i; ASSERT_EQUAL(metadata_len, slot->metadata_len); ASSERT_TRUE(memcmp(metadata, slot->metadata, metadata_len) == 0); } @@ -693,7 +695,7 @@ static void test_dist_dfu_slot_create_recover(void) static void check_delete_all(void) { - int i, idx, err; + int i; const struct bt_mesh_dfu_slot *slot; size_t slot_count; @@ -706,14 +708,6 @@ static void check_delete_all(void) for (i = 0; i < CONFIG_BT_MESH_DFU_SLOT_CNT - 1; i++) { slot = bt_mesh_dfu_slot_at(i); ASSERT_TRUE(slot == NULL); - - idx = bt_mesh_dfu_slot_idx_get(slot); - ASSERT_TRUE(idx < 0); - - err = bt_mesh_dfu_slot_valid_set(slot, true); - ASSERT_EQUAL(err, -ENOENT); - - ASSERT_TRUE(!bt_mesh_dfu_slot_is_valid(slot)); } } @@ -726,7 +720,6 @@ static void test_dist_dfu_slot_delete_all(void) bt_mesh_device_setup(&prov, &dist_comp); bt_mesh_dfu_slot_del_all(); - check_delete_all(); PASS(); @@ -742,6 +735,63 @@ static void test_dist_dfu_slot_check_delete_all(void) PASS(); } +static void test_dist_dfu_slot_reservation(void) +{ + int i; + struct bt_mesh_dfu_slot *slots[CONFIG_BT_MESH_DFU_SLOT_CNT]; + + bt_mesh_test_cfg_set(NULL, WAIT_TIME); + bt_mesh_device_setup(&prov, &dist_comp); + + for (i = 0; i < CONFIG_BT_MESH_DFU_SLOT_CNT; i++) { + slots[i] = bt_mesh_dfu_slot_reserve(); + ASSERT_TRUE(slots[i] != NULL); + } + + ASSERT_EQUAL(NULL, bt_mesh_dfu_slot_reserve()); + bt_mesh_dfu_slot_release(slots[0]); + /* Release twice to check idempotency with empty pool */ + bt_mesh_dfu_slot_release(slots[0]); + ASSERT_TRUE(bt_mesh_dfu_slot_reserve() != NULL); + ASSERT_EQUAL(NULL, bt_mesh_dfu_slot_reserve()); + + PASS(); +} + +static void test_dist_dfu_slot_idempotency(void) +{ + uint8_t fwid[CONFIG_BT_MESH_DFU_FWID_MAXLEN] = { 0 }; + size_t fwid_len = 4; + struct bt_mesh_dfu_slot *slot; + + ASSERT_TRUE(CONFIG_BT_MESH_DFU_SLOT_CNT >= 1, + "CONFIG_BT_MESH_DFU_SLOT_CNT must be at least 1"); + + bt_mesh_test_cfg_set(NULL, WAIT_TIME); + bt_mesh_device_setup(&prov, &dist_comp); + dist_prov_and_conf(DIST_ADDR); + + slot = bt_mesh_dfu_slot_reserve(); + ASSERT_TRUE(slot != NULL); + + bt_mesh_dfu_slot_release(slot); + bt_mesh_dfu_slot_release(slot); + + slot = bt_mesh_dfu_slot_reserve(); + ASSERT_TRUE(slot != NULL); + + ASSERT_EQUAL(0, bt_mesh_dfu_slot_fwid_set(slot, fwid, fwid_len)); + ASSERT_EQUAL(0, bt_mesh_dfu_slot_info_set(slot, 100, NULL, 0)); + + ASSERT_EQUAL(0, bt_mesh_dfu_slot_commit(slot)); + ASSERT_EQUAL(-EINVAL, bt_mesh_dfu_slot_commit(slot)); + + ASSERT_EQUAL(0, bt_mesh_dfu_slot_del(slot)); + ASSERT_EQUAL(-EINVAL, bt_mesh_dfu_slot_del(slot)); + + PASS(); +} + static void target_test_effect(enum bt_mesh_dfu_effect effect) { dfu_target_effect = effect; @@ -921,12 +971,14 @@ static void cli_common_fail_on_init(void) static void cli_common_init_recover(void) { - const struct bt_mesh_dfu_slot *slot; + struct bt_mesh_dfu_slot *slot; + uint8_t fwid[CONFIG_BT_MESH_DFU_FWID_MAXLEN] = { 0xAA, 0xBB, 0xCC, 0xDD }; + size_t fwid_len = 4; bt_mesh_test_cfg_set(NULL, 300); bt_mesh_device_setup(&prov, &cli_comp); - ASSERT_TRUE(slot_add(&slot)); + ASSERT_TRUE(bt_mesh_dfu_slot_get(fwid, fwid_len, &slot) >= 0); dfu_cli_inputs_prepare(0); dfu_cli_xfer.xfer.mode = BT_MESH_BLOB_XFER_MODE_PUSH; @@ -965,7 +1017,7 @@ static void test_cli_fail_on_persistency(void) FAIL("DFU Client send failed (err: %d)", err); } - if (k_sem_take(&dfu_ended, K_SECONDS(200))) { + if (k_sem_take(&dfu_ended, K_SECONDS(SEMAPHORE_TIMEOUT))) { FAIL("Firmware transfer failed"); } @@ -999,7 +1051,7 @@ static void test_cli_fail_on_persistency(void) FAIL("DFU Client apply failed (err: %d)", err); } - if (k_sem_take(&dfu_cli_applied_sem, K_SECONDS(200))) { + if (k_sem_take(&dfu_cli_applied_sem, K_SECONDS(SEMAPHORE_TIMEOUT))) { FAIL("Failed to apply firmware"); } @@ -1012,7 +1064,7 @@ static void test_cli_fail_on_persistency(void) FAIL("DFU Client confirm failed (err: %d)", err); } - if (k_sem_take(&dfu_cli_confirmed_sem, K_SECONDS(200))) { + if (k_sem_take(&dfu_cli_confirmed_sem, K_SECONDS(SEMAPHORE_TIMEOUT))) { FAIL("Failed to confirm firmware"); } @@ -1045,7 +1097,7 @@ static void test_cli_all_targets_lost_common(void) FAIL("DFU Client send failed (err: %d)", err); } - if (k_sem_take(&dfu_ended, K_SECONDS(200))) { + if (k_sem_take(&dfu_ended, K_SECONDS(SEMAPHORE_TIMEOUT))) { FAIL("Firmware transfer failed"); } } @@ -1136,7 +1188,7 @@ static void test_cli_all_targets_lost_on_apply(void) FAIL("DFU Client apply failed (err: %d)", err); } - if (!k_sem_take(&dfu_cli_applied_sem, K_SECONDS(200))) { + if (!k_sem_take(&dfu_cli_applied_sem, K_SECONDS(SEMAPHORE_TIMEOUT))) { FAIL("Apply should not be successful on any target"); } @@ -1167,7 +1219,7 @@ static void test_cli_stop(void) FAIL("DFU Client send failed (err: %d)", err); } - if (k_sem_take(&dfu_started, K_SECONDS(200))) { + if (k_sem_take(&dfu_started, K_SECONDS(SEMAPHORE_TIMEOUT))) { FAIL("Firmware transfer failed"); } @@ -1183,7 +1235,7 @@ static void test_cli_stop(void) FAIL("DFU Client resume failed (err: %d)", err); } - if (k_sem_take(&dfu_verifying, K_SECONDS(200))) { + if (k_sem_take(&dfu_verifying, K_SECONDS(SEMAPHORE_TIMEOUT))) { FAIL("Firmware transfer failed"); } ASSERT_EQUAL(BT_MESH_DFU_ERR_INTERNAL, dfu_cli_xfer.targets[0].status); @@ -1202,7 +1254,7 @@ static void test_cli_stop(void) FAIL("DFU Client send failed (err: %d)", err); } - if (k_sem_take(&dfu_verify_failed, K_SECONDS(200))) { + if (k_sem_take(&dfu_verify_failed, K_SECONDS(SEMAPHORE_TIMEOUT))) { FAIL("Firmware transfer failed"); } @@ -1218,12 +1270,12 @@ static void test_cli_stop(void) if (err) { FAIL("DFU Client send failed (err: %d)", err); } - if (k_sem_take(&dfu_ended, K_SECONDS(200))) { + if (k_sem_take(&dfu_ended, K_SECONDS(SEMAPHORE_TIMEOUT))) { FAIL("Firmware transfer failed"); } bt_mesh_dfu_cli_apply(&dfu_cli); - if (k_sem_take(&dfu_cli_applied_sem, K_SECONDS(200))) { + if (k_sem_take(&dfu_cli_applied_sem, K_SECONDS(SEMAPHORE_TIMEOUT))) { /* This will time out as target will reboot before applying */ } ASSERT_EQUAL(BT_MESH_DFU_ERR_INTERNAL, dfu_cli_xfer.targets[0].status); @@ -1413,7 +1465,7 @@ static void test_target_fail_on_metadata(void) common_fail_on_target_init(&target_comp); target_prov_and_conf_default(); - if (k_sem_take(&dfu_metadata_check_sem, K_SECONDS(200))) { + if (k_sem_take(&dfu_metadata_check_sem, K_SECONDS(SEMAPHORE_TIMEOUT))) { FAIL("Metadata check CB wasn't called"); } @@ -1427,7 +1479,7 @@ static void test_target_fail_on_caps_get(void) common_fail_on_target_init(&srv_caps_broken_comp); target_prov_and_conf_with_imposer(); - if (k_sem_take(&caps_get_sem, K_SECONDS(200))) { + if (k_sem_take(&caps_get_sem, K_SECONDS(SEMAPHORE_TIMEOUT))) { FAIL("BLOB Info Get msg handler wasn't called"); } @@ -1441,11 +1493,11 @@ static void test_target_fail_on_update_get(void) common_fail_on_target_init(&srv_update_get_broken_comp); target_prov_and_conf_with_imposer(); - if (k_sem_take(&dfu_verify_sem, K_SECONDS(200))) { + if (k_sem_take(&dfu_verify_sem, K_SECONDS(SEMAPHORE_TIMEOUT))) { FAIL("Transfer end CB wasn't triggered"); } - if (k_sem_take(&update_get_sem, K_SECONDS(200))) { + if (k_sem_take(&update_get_sem, K_SECONDS(SEMAPHORE_TIMEOUT))) { FAIL("Firmware Update Get msg handler wasn't called"); } @@ -1460,7 +1512,7 @@ static void test_target_fail_on_verify(void) common_fail_on_target_init(&target_comp); target_prov_and_conf_default(); - if (k_sem_take(&dfu_verify_sem, K_SECONDS(200))) { + if (k_sem_take(&dfu_verify_sem, K_SECONDS(SEMAPHORE_TIMEOUT))) { FAIL("Transfer end CB wasn't triggered"); } @@ -1474,7 +1526,7 @@ static void test_target_fail_on_apply(void) common_fail_on_target_init(&srv_update_apply_broken_comp); target_prov_and_conf_with_imposer(); - if (k_sem_take(&update_apply_sem, K_SECONDS(200))) { + if (k_sem_take(&update_apply_sem, K_SECONDS(SEMAPHORE_TIMEOUT))) { FAIL("Firmware Update Apply msg handler wasn't called"); } @@ -1486,7 +1538,7 @@ static void test_target_fail_on_nothing(void) common_fail_on_target_init(&target_comp); target_prov_and_conf_default(); - if (k_sem_take(&dfu_ended, K_SECONDS(200))) { + if (k_sem_take(&dfu_ended, K_SECONDS(SEMAPHORE_TIMEOUT))) { FAIL("DFU failed"); } @@ -1595,6 +1647,10 @@ static const struct bst_test_instance test_dfu[] = { TEST_CASE(dist, dfu_slot_delete_all, "Distributor deletes all image slots"), TEST_CASE(dist, dfu_slot_check_delete_all, "Distributor checks if all slots are removed from persistent storage"), + TEST_CASE(dist, dfu_slot_reservation, + "Distributor checks that the correct number of slots can be reserved"), + TEST_CASE(dist, dfu_slot_idempotency, + "Distributor checks that the the DFU slot APIs are idempotent"), TEST_CASE(cli, stop, "DFU Client stops at configured point of Firmware Distribution"), TEST_CASE(cli, fail_on_persistency, "DFU Client doesn't give up DFU Transfer"), TEST_CASE(cli, all_targets_lost_on_metadata, diff --git a/tests/bsim/bluetooth/mesh/src/test_persistence.c b/tests/bsim/bluetooth/mesh/src/test_persistence.c index 6c510922330..aab9c6dd185 100644 --- a/tests/bsim/bluetooth/mesh/src/test_persistence.c +++ b/tests/bsim/bluetooth/mesh/src/test_persistence.c @@ -520,6 +520,17 @@ static void node_configure(void) uint16_t va; struct bt_mesh_cfg_cli_mod_pub pub_params; + /* Set Network Transmit Count state on the device greater than on provisioner to increase + * probability of reception responses. + */ + uint8_t net_transmit; + + net_transmit = BT_MESH_TRANSMIT(3, 50); + err = bt_mesh_cfg_cli_net_transmit_set(test_netkey_idx, TEST_ADDR, net_transmit, &status); + if (err || status != net_transmit) { + FAIL("Net transmit set failed (err %d, transmit %x)", err, status); + } + struct test_appkey_t test_appkeys[] = { { .idx = TEST_APPKEY_0_IDX, .key = TEST_APPKEY_0_KEY }, { .idx = TEST_APPKEY_1_IDX, .key = TEST_APPKEY_1_KEY }, diff --git a/tests/bsim/bluetooth/mesh/src/test_provision.c b/tests/bsim/bluetooth/mesh/src/test_provision.c index 171f8cccbbd..c78fe8dc87d 100644 --- a/tests/bsim/bluetooth/mesh/src/test_provision.c +++ b/tests/bsim/bluetooth/mesh/src/test_provision.c @@ -139,6 +139,19 @@ static const struct bt_mesh_comp rpr_srv_comp = { .elem_count = 1, }; +static const struct bt_mesh_comp rpr_cli_srv_comp = { + .elem = + (struct bt_mesh_elem[]){ + BT_MESH_ELEM(1, + MODEL_LIST(BT_MESH_MODEL_CFG_SRV, + BT_MESH_MODEL_CFG_CLI(&(struct bt_mesh_cfg_cli){}), + BT_MESH_MODEL_RPR_CLI(&rpr_cli), + BT_MESH_MODEL_RPR_SRV), + BT_MESH_MODEL_NONE), + }, + .elem_count = 1, +}; + static int mock_pdu_send(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { @@ -1043,18 +1056,127 @@ static void test_provisioner_pb_remote_client_provision_timeout(void) PASS(); } +static void reprovision_remote_devkey_client(struct bt_mesh_rpr_node *srv, + struct bt_mesh_cdb_node *node) +{ + uint8_t status; + uint8_t prev_node_dev_key[16]; + + ASSERT_OK_MSG(bt_mesh_cdb_node_key_export(node, prev_node_dev_key), + "Can't export device key from cdb"); + + bt_mesh_reprovision_remote(&rpr_cli, srv, current_dev_addr, false); + + ASSERT_OK(k_sem_take(&reprov_sem, K_SECONDS(20))); + + /* Check that CDB has updated Device Key for the node. */ + ASSERT_TRUE(bt_mesh_key_compare(prev_node_dev_key, &node->dev_key)); + ASSERT_OK_MSG(bt_mesh_cdb_node_key_export(node, prev_node_dev_key), + "Can't export device key from cdb"); + + /* Check device key by adding appkey. */ + ASSERT_OK(bt_mesh_cfg_cli_app_key_add(0, current_dev_addr, 0, 0, test_app_key, + &status)); + ASSERT_OK(status); + + /* Let RPR Server verify Device Key. */ + k_sleep(K_SECONDS(2)); +} + +static void reprovision_remote_comp_data_client(struct bt_mesh_rpr_node *srv, + struct bt_mesh_cdb_node *node, + struct net_buf_simple *dev_comp) +{ + NET_BUF_SIMPLE_DEFINE(new_dev_comp, BT_MESH_RX_SDU_MAX); + uint8_t prev_node_dev_key[16]; + uint8_t page; + + ASSERT_OK_MSG(bt_mesh_cdb_node_key_export(node, prev_node_dev_key), + "Can't export device key from cdb"); + + bt_mesh_reprovision_remote(&rpr_cli, srv, current_dev_addr, true); + + ASSERT_OK(k_sem_take(&reprov_sem, K_SECONDS(20))); + + /* Check that CDB has updated Device Key for the node. */ + ASSERT_TRUE(bt_mesh_key_compare(prev_node_dev_key, &node->dev_key)); + ASSERT_OK_MSG(bt_mesh_cdb_node_key_export(node, prev_node_dev_key), + "Can't export device key from cdb"); + + /* Check that Composition Data Page 128 is now Page 0. */ + net_buf_simple_reset(&new_dev_comp); + ASSERT_OK(bt_mesh_cfg_cli_comp_data_get(0, current_dev_addr, 0, &page, + &new_dev_comp)); + + ASSERT_EQUAL(0, page); + ASSERT_EQUAL(dev_comp->len, new_dev_comp.len); + if (memcmp(dev_comp->data, new_dev_comp.data, dev_comp->len)) { + FAIL("Wrong composition data page 0"); + } + + /* Let RPR Server verify Device Key. */ + k_sleep(K_SECONDS(2)); +} + +static void reprovision_remote_address_client(struct bt_mesh_rpr_node *srv, + struct bt_mesh_cdb_node *node) +{ + uint8_t status; + uint8_t prev_node_dev_key[16]; + + ASSERT_OK_MSG(bt_mesh_cdb_node_key_export(node, prev_node_dev_key), + "Can't export device key from cdb"); + + bt_mesh_reprovision_remote(&rpr_cli, srv, current_dev_addr + 1, false); + + ASSERT_OK(k_sem_take(&reprov_sem, K_SECONDS(20))); + + current_dev_addr++; + srv->addr++; + + /* Check that device doesn't respond to old address with old and new device key. */ + struct bt_mesh_cdb_node *prev_node; + uint8_t tmp[16]; + + prev_node = bt_mesh_cdb_node_alloc((uint8_t[16]) {}, current_dev_addr - 1, 1, 0); + ASSERT_TRUE(node); + ASSERT_OK_MSG(bt_mesh_cdb_node_key_import(prev_node, prev_node_dev_key), + "Can't import device key into cdb"); + ASSERT_EQUAL(-ETIMEDOUT, bt_mesh_cfg_cli_app_key_add(0, current_dev_addr - 1, 0, 0, + test_app_key, &status)); + ASSERT_OK_MSG(bt_mesh_cdb_node_key_export(node, tmp), + "Can't export device key from cdb"); + ASSERT_OK_MSG(bt_mesh_cdb_node_key_import(prev_node, tmp), + "Can't import device key into cdb"); + ASSERT_EQUAL(-ETIMEDOUT, bt_mesh_cfg_cli_app_key_add(0, current_dev_addr - 1, 0, 0, + test_app_key, &status)); + bt_mesh_cdb_node_del(prev_node, false); + + /* Check that CDB has updated Device Key for the node. */ + ASSERT_TRUE(bt_mesh_key_compare(prev_node_dev_key, &node->dev_key)); + ASSERT_OK_MSG(bt_mesh_cdb_node_key_export(node, prev_node_dev_key), + "Can't export device key from cdb"); + + /* Check new device address by adding appkey. */ + ASSERT_OK(bt_mesh_cfg_cli_app_key_add(0, current_dev_addr, 0, 0, test_app_key, + &status)); + ASSERT_OK(status); + + /* Let RPR Server verify Device Key. */ + k_sleep(K_SECONDS(2)); + +} + /** @brief Verify robustness of NPPI procedures on a RPR Client by running Device Key Refresh, * Node Composition Refresh and Node Address Refresh procedures. */ static void test_provisioner_pb_remote_client_nppi_robustness(void) { NET_BUF_SIMPLE_DEFINE(dev_comp, BT_MESH_RX_SDU_MAX); - NET_BUF_SIMPLE_DEFINE(new_dev_comp, BT_MESH_RX_SDU_MAX); uint8_t page; uint16_t pb_remote_server_addr; uint8_t status; struct bt_mesh_cdb_node *node; - uint8_t prev_node_dev_key[16]; provisioner_pb_remote_client_setup(); @@ -1082,96 +1204,23 @@ static void test_provisioner_pb_remote_client_nppi_robustness(void) node = bt_mesh_cdb_node_get(current_dev_addr); ASSERT_TRUE(node); - ASSERT_OK_MSG(bt_mesh_cdb_node_key_export(node, prev_node_dev_key), - "Can't export device key from cdb"); LOG_INF("Testing DevKey refresh..."); for (int i = 0; i < PROV_REPROV_COUNT; i++) { LOG_INF("Refreshing device key #%d...\n", i); - bt_mesh_reprovision_remote(&rpr_cli, &srv, current_dev_addr, false); - - ASSERT_OK(k_sem_take(&reprov_sem, K_SECONDS(20))); - - /* Check that CDB has updated Device Key for the node. */ - ASSERT_TRUE(bt_mesh_key_compare(prev_node_dev_key, &node->dev_key)); - ASSERT_OK_MSG(bt_mesh_cdb_node_key_export(node, prev_node_dev_key), - "Can't export device key from cdb"); - - /* Check device key by adding appkey. */ - ASSERT_OK(bt_mesh_cfg_cli_app_key_add(0, current_dev_addr, 0, 0, test_app_key, - &status)); - ASSERT_OK(status); - - /* Let RPR Server verify Device Key. */ - k_sleep(K_SECONDS(2)); + reprovision_remote_devkey_client(&srv, node); } LOG_INF("Testing Composition Data refresh..."); for (int i = 0; i < PROV_REPROV_COUNT; i++) { LOG_INF("Changing Composition Data #%d...\n", i); - bt_mesh_reprovision_remote(&rpr_cli, &srv, current_dev_addr, true); - - ASSERT_OK(k_sem_take(&reprov_sem, K_SECONDS(20))); - - /* Check that CDB has updated Device Key for the node. */ - ASSERT_TRUE(bt_mesh_key_compare(prev_node_dev_key, &node->dev_key)); - ASSERT_OK_MSG(bt_mesh_cdb_node_key_export(node, prev_node_dev_key), - "Can't export device key from cdb"); - - /* Check that Composition Data Page 128 is now Page 0. */ - net_buf_simple_reset(&new_dev_comp); - ASSERT_OK(bt_mesh_cfg_cli_comp_data_get(0, current_dev_addr, 0, &page, - &new_dev_comp)); - ASSERT_EQUAL(0, page); - ASSERT_EQUAL(dev_comp.len, new_dev_comp.len); - if (memcmp(dev_comp.data, new_dev_comp.data, dev_comp.len)) { - FAIL("Wrong composition data page 0"); - } - - /* Let RPR Server verify Device Key. */ - k_sleep(K_SECONDS(2)); + reprovision_remote_comp_data_client(&srv, node, &dev_comp); } LOG_INF("Testing address refresh..."); for (int i = 0; i < PROV_REPROV_COUNT; i++) { LOG_INF("Changing address #%d...\n", i); - bt_mesh_reprovision_remote(&rpr_cli, &srv, current_dev_addr + 1, false); - - ASSERT_OK(k_sem_take(&reprov_sem, K_SECONDS(20))); - - current_dev_addr++; - srv.addr++; - - /* Check that device doesn't respond to old address with old and new device key. */ - struct bt_mesh_cdb_node *prev_node; - uint8_t tmp[16]; - - prev_node = bt_mesh_cdb_node_alloc((uint8_t[16]) {}, current_dev_addr - 1, 1, 0); - ASSERT_TRUE(node); - ASSERT_OK_MSG(bt_mesh_cdb_node_key_import(prev_node, prev_node_dev_key), - "Can't import device key into cdb"); - ASSERT_EQUAL(-ETIMEDOUT, bt_mesh_cfg_cli_app_key_add(0, current_dev_addr - 1, 0, 0, - test_app_key, &status)); - ASSERT_OK_MSG(bt_mesh_cdb_node_key_export(node, tmp), - "Can't export device key from cdb"); - ASSERT_OK_MSG(bt_mesh_cdb_node_key_import(prev_node, tmp), - "Can't import device key into cdb"); - ASSERT_EQUAL(-ETIMEDOUT, bt_mesh_cfg_cli_app_key_add(0, current_dev_addr - 1, 0, 0, - test_app_key, &status)); - bt_mesh_cdb_node_del(prev_node, false); - - /* Check that CDB has updated Device Key for the node. */ - ASSERT_TRUE(bt_mesh_key_compare(prev_node_dev_key, &node->dev_key)); - ASSERT_OK_MSG(bt_mesh_cdb_node_key_export(node, prev_node_dev_key), - "Can't export device key from cdb"); - - /* Check new device address by adding appkey. */ - ASSERT_OK(bt_mesh_cfg_cli_app_key_add(0, current_dev_addr, 0, 0, test_app_key, - &status)); - ASSERT_OK(status); - - /* Let RPR Server verify Device Key. */ - k_sleep(K_SECONDS(2)); + reprovision_remote_address_client(&srv, node); } PASS(); @@ -1211,13 +1260,78 @@ static void test_device_pb_remote_server_proved(void) PASS(); } +static void reprovision_remote_devkey_server(const uint16_t initial_addr) +{ + uint8_t prev_dev_key[16]; + uint8_t dev_key[16]; + + ASSERT_OK(bt_mesh_key_export(prev_dev_key, &bt_mesh.dev_key)); + + ASSERT_OK(k_sem_take(&reprov_sem, K_SECONDS(30))); + ASSERT_EQUAL(initial_addr, bt_mesh_primary_addr()); + + /* Let Configuration Client activate the new Device Key and verify that it has + * been changed. + */ + k_sleep(K_SECONDS(2)); + ASSERT_OK(bt_mesh_key_export(dev_key, &bt_mesh.dev_key)); + ASSERT_TRUE(memcmp(&prev_dev_key, dev_key, sizeof(dev_key))); +} + +static void reprovision_remote_comp_data_server(const uint16_t initial_addr) +{ + u_int8_t prev_dev_key[16]; + u_int8_t dev_key[16]; + + /* The RPR Server won't let to run Node Composition Refresh procedure without first + * setting the BT_MESH_COMP_DIRTY flag. The flag is set on a boot if there is a + * "bt/mesh/cmp" entry in settings. The entry is added by the + * `bt_mesh_comp_change_prepare() call. The test suite is not compiled + * with CONFIG_BT_SETTINGS, so the flag will never be set. Since the purpose of the + * test is to check RPR Server behavior, but not the actual swap of the Composition + * Data, the flag is toggled directly from the test. + */ + atomic_set_bit(bt_mesh.flags, BT_MESH_COMP_DIRTY); + ASSERT_OK(bt_mesh_key_export(prev_dev_key, &bt_mesh.dev_key)); + + ASSERT_OK(k_sem_take(&reprov_sem, K_SECONDS(30))); + + /* Drop the flag manually as CONFIG_BT_SETTINGS is not enabled. */ + atomic_clear_bit(bt_mesh.flags, BT_MESH_COMP_DIRTY); + + ASSERT_EQUAL(initial_addr, bt_mesh_primary_addr()); + + /* Let Configuration Client activate the new Device Key and verify that it has + * been changed. + */ + k_sleep(K_SECONDS(2)); + ASSERT_OK(bt_mesh_key_export(dev_key, &bt_mesh.dev_key)); + ASSERT_TRUE(memcmp(prev_dev_key, dev_key, sizeof(dev_key))); +} + +static void reprovision_remote_address_server(const uint16_t initial_addr) +{ + uint8_t prev_dev_key[16]; + uint8_t dev_key[16]; + + ASSERT_OK(bt_mesh_key_export(prev_dev_key, &bt_mesh.dev_key)); + + ASSERT_OK(k_sem_take(&reprov_sem, K_SECONDS(30))); + ASSERT_EQUAL(initial_addr + 1, bt_mesh_primary_addr()); + + /* Let Configuration Client activate the new Device Key and verify that it has + * been changed. + */ + k_sleep(K_SECONDS(2)); + ASSERT_OK(bt_mesh_key_export(dev_key, &bt_mesh.dev_key)); + ASSERT_TRUE(memcmp(prev_dev_key, dev_key, sizeof(dev_key))); +} + /** @brief Verify robustness of NPPI procedures on a RPR Server by running Device Key Refresh, * Node Composition Refresh and Node Address Refresh procedures multiple times each. */ static void test_device_pb_remote_server_nppi_robustness(void) { - struct bt_mesh_key prev_dev_key; - k_sem_init(&prov_sem, 0, 1); k_sem_init(&reprov_sem, 0, 1); @@ -1230,65 +1344,25 @@ static void test_device_pb_remote_server_nppi_robustness(void) ASSERT_OK(k_sem_take(&prov_sem, K_SECONDS(20))); const uint16_t initial_addr = bt_mesh_primary_addr(); - memcpy(&prev_dev_key, &bt_mesh.dev_key, sizeof(struct bt_mesh_key)); - LOG_INF("Enabling PB-Remote server"); ASSERT_OK(bt_mesh_prov_enable(BT_MESH_PROV_REMOTE)); /* Test Device Key Refresh procedure robustness. */ for (int i = 0; i < PROV_REPROV_COUNT; i++) { LOG_INF("Devkey refresh loop #%d, waiting for being reprov ...\n", i); - ASSERT_OK(k_sem_take(&reprov_sem, K_SECONDS(30))); - ASSERT_EQUAL(initial_addr, bt_mesh_primary_addr()); - - /* Let Configuration Client activate the new Device Key and verify that it has - * been changed. - */ - k_sleep(K_SECONDS(2)); - ASSERT_TRUE(memcmp(&prev_dev_key, &bt_mesh.dev_key, sizeof(struct bt_mesh_key))); - memcpy(&prev_dev_key, &bt_mesh.dev_key, sizeof(struct bt_mesh_key)); + reprovision_remote_devkey_server(initial_addr); } /* Test Node Composition Refresh procedure robustness. */ for (int i = 0; i < PROV_REPROV_COUNT; i++) { - /* The RPR Server won't let to run Node Composition Refresh procedure without first - * setting the BT_MESH_COMP_DIRTY flag. The flag is set on a boot if there is a - * "bt/mesh/cmp" entry in settings. The entry is added by the - * `bt_mesh_comp_change_prepare() call. The test suite is not compiled - * with CONFIG_BT_SETTINGS, so the flag will never be set. Since the purpose of the - * test is to check RPR Server behavior, but not the actual swap of the Composition - * Data, the flag is toggled directly from the test. - */ - atomic_set_bit(bt_mesh.flags, BT_MESH_COMP_DIRTY); - LOG_INF("Composition data refresh loop #%d, waiting for being reprov ...\n", i); - ASSERT_OK(k_sem_take(&reprov_sem, K_SECONDS(30))); - - /* Drop the flag manually as CONFIG_BT_SETTINGS is not enabled. */ - atomic_clear_bit(bt_mesh.flags, BT_MESH_COMP_DIRTY); - - ASSERT_EQUAL(initial_addr, bt_mesh_primary_addr()); - - /* Let Configuration Client activate the new Device Key and verify that it has - * been changed. - */ - k_sleep(K_SECONDS(2)); - ASSERT_TRUE(memcmp(&prev_dev_key, &bt_mesh.dev_key, sizeof(struct bt_mesh_key))); - memcpy(&prev_dev_key, &bt_mesh.dev_key, sizeof(struct bt_mesh_key)); + reprovision_remote_comp_data_server(initial_addr); } /* Node Address Refresh robustness. */ for (int i = 0; i < PROV_REPROV_COUNT; i++) { LOG_INF("Address refresh loop #%d, waiting for being reprov ...\n", i); - ASSERT_OK(k_sem_take(&reprov_sem, K_SECONDS(30))); - ASSERT_EQUAL(initial_addr + 1 + i, bt_mesh_primary_addr()); - - /* Let Configuration Client activate the new Device Key and verify that it has - * been changed. - */ - k_sleep(K_SECONDS(2)); - ASSERT_TRUE(memcmp(&prev_dev_key, &bt_mesh.dev_key, sizeof(struct bt_mesh_key))); - memcpy(&prev_dev_key, &bt_mesh.dev_key, sizeof(struct bt_mesh_key)); + reprovision_remote_address_server(initial_addr+i); } PASS(); @@ -1325,6 +1399,172 @@ static void test_provisioner_pb_remote_client_ncrp_provision(void) PASS(); } +/** @brief A device running a Remote Provisioning client and server that is used to reprovision + * another device and it self with the client. + */ +static void test_device_pb_remote_client_server_same_dev(void) +{ + NET_BUF_SIMPLE_DEFINE(dev_comp, BT_MESH_RX_SDU_MAX); + uint8_t status; + struct bt_mesh_cdb_node *node; + uint8_t page; + uint8_t prev_dev_key[16]; + uint16_t test_vector[] = { 0x0002, 0x0001 }; + + k_sem_init(&prov_sem, 0, 1); + k_sem_init(&reprov_sem, 0, 1); + + bt_mesh_device_setup(&prov, &rpr_cli_srv_comp); + + ASSERT_OK(bt_mesh_cdb_create(test_net_key)); + ASSERT_OK(bt_mesh_provision(test_net_key, 0, 0, 0, 0x0001, dev_key)); + + LOG_INF("Enabling PB-Remote server"); + ASSERT_OK(bt_mesh_prov_enable(BT_MESH_PROV_REMOTE)); + + /* Provision a remote device with RPR Client and Server with local RPR Server. */ + current_dev_addr = 0x0001; + struct bt_mesh_rpr_node srv = { + .addr = current_dev_addr, + .net_idx = 0, + .ttl = 3, + }; + + LOG_INF("Provisioner prov, waiting for prov ...\n"); + ASSERT_OK(provision_remote(&srv, 1, &srv.addr)); + + ASSERT_OK(k_sem_take(&prov_sem, K_SECONDS(20))); + + /* Check device key by adding bt_mesh_reprovision_remote appkey. */ + ASSERT_OK(bt_mesh_cfg_cli_app_key_add(0, current_dev_addr, 0, 0, test_app_key, &status)); + ASSERT_OK(status); + + /* Swap callback to catch when device reprovisioned. */ + prov.node_added = prov_node_added_rpr; + + /* Reprovision a device with both RPR Client and Server. */ + for (int i = 0; i < ARRAY_SIZE(test_vector); i++) { + current_dev_addr = test_vector[i]; + srv.addr = current_dev_addr; + bool self_reprov = (bool)(current_dev_addr == bt_mesh_primary_addr()); + + /* Store initial Composition Data Page 0. */ + net_buf_simple_reset(&dev_comp); + ASSERT_OK(bt_mesh_cfg_cli_comp_data_get(0, current_dev_addr, 0, &page, &dev_comp)); + + node = bt_mesh_cdb_node_get(current_dev_addr); + ASSERT_TRUE(node); + + LOG_INF("Refreshing 0x%04x device key ...\n", srv.addr); + ASSERT_OK(bt_mesh_key_export(prev_dev_key, &bt_mesh.dev_key)); + reprovision_remote_devkey_client(&srv, node); + if (self_reprov) { + uint8_t dev_key[16]; + + ASSERT_EQUAL(current_dev_addr, bt_mesh_primary_addr()); + + /* Let Configuration Client activate the new Device Key + * and verify that it has been changed. + */ + ASSERT_OK(bt_mesh_key_export(dev_key, &bt_mesh.dev_key)); + ASSERT_TRUE(memcmp(prev_dev_key, dev_key, sizeof(dev_key))); + } + + LOG_INF("Changing 0x%04x Composition Data ...\n", srv.addr); + ASSERT_OK(bt_mesh_key_export(prev_dev_key, &bt_mesh.dev_key)); + reprovision_remote_comp_data_client(&srv, node, &dev_comp); + if (self_reprov) { + uint8_t dev_key[16]; + + ASSERT_EQUAL(current_dev_addr, bt_mesh_primary_addr()); + + /* Let Configuration Client activate the new Device Key + * and verify that it has been changed. + */ + ASSERT_OK(bt_mesh_key_export(dev_key, &bt_mesh.dev_key)); + ASSERT_TRUE(memcmp(prev_dev_key, dev_key, sizeof(struct bt_mesh_key))); + } + + LOG_INF("Changing 0x%04x address ...\n", srv.addr); + ASSERT_OK(bt_mesh_key_export(prev_dev_key, &bt_mesh.dev_key)); + reprovision_remote_address_client(&srv, node); + if (self_reprov) { + uint8_t dev_key[16]; + + ASSERT_EQUAL(current_dev_addr, bt_mesh_primary_addr()); + + /* Let Configuration Client activate the new Device Key + * and verify that it has been changed. + */ + ASSERT_OK(bt_mesh_key_export(dev_key, &bt_mesh.dev_key)); + ASSERT_TRUE(memcmp(prev_dev_key, dev_key, sizeof(dev_key))); + } + } + + PASS(); +} + +/** @brief Verify that the Remote Provisioning client and server is able to be reprovision + * by another device with a Remote Provisioning client and server. + */ +static void test_device_pb_remote_server_same_dev(void) +{ + k_sem_init(&prov_sem, 0, 1); + k_sem_init(&reprov_sem, 0, 1); + + bt_mesh_device_setup(&prov, &rpr_cli_srv_comp); + + ASSERT_OK(bt_mesh_prov_enable(BT_MESH_PROV_ADV)); + + LOG_INF("Waiting for being provisioned..."); + ASSERT_OK(k_sem_take(&prov_sem, K_SECONDS(20))); + + LOG_INF("Enabling PB-Remote server"); + ASSERT_OK(bt_mesh_prov_enable(BT_MESH_PROV_REMOTE)); + + /* Swap callback to catch when device reprovisioned. */ + prov.node_added = prov_node_added_rpr; + + const uint16_t initial_addr = bt_mesh_primary_addr(); + + LOG_INF("Devkey refresh, waiting for being reprov ...\n"); + reprovision_remote_devkey_server(initial_addr); + + LOG_INF("Composition data refresh, waiting for being reprov ...\n"); + reprovision_remote_comp_data_server(initial_addr); + + LOG_INF("Address refresh, waiting for being reprov ...\n"); + reprovision_remote_address_server(initial_addr); + + PASS(); +} + +static void comp_data_get(uint16_t server_addr, uint8_t page, struct net_buf_simple *comp) +{ + uint8_t page_rsp; + + net_buf_simple_reset(comp); + ASSERT_OK(bt_mesh_cfg_cli_comp_data_get(0, server_addr, page, &page_rsp, comp)); + ASSERT_EQUAL(page, page_rsp); +} + +static void comp_data_compare(struct net_buf_simple *comp1, struct net_buf_simple *comp2, + bool expect_equal) +{ + if (expect_equal) { + ASSERT_EQUAL(comp1->len, comp2->len); + if (memcmp(comp1->data, comp2->data, comp1->len)) { + FAIL("Composition data is not equal"); + } + } else { + if (comp1->len == comp2->len) { + if (!memcmp(comp1->data, comp2->data, comp1->len)) { + FAIL("Composition data is equal"); + } + } + } +} + /** @brief Test Node Composition Refresh procedure on Remote Provisioning client: * - initiate Node Composition Refresh procedure on a 3rd device. */ @@ -1543,6 +1783,10 @@ static const struct bst_test_instance test_connect[] = { "Device: pb-remote reprovisioning, NPPI robustness"), TEST_CASE(device, pb_remote_server_unproved_unresponsive, "Device: used for remote provisioning, starts unprovisioned, stops responding"), + TEST_CASE(device, pb_remote_client_server_same_dev, + "Device: used for remote provisioning, with both client and server"), + TEST_CASE(device, pb_remote_server_same_dev, + "Device: used for remote reprovisioning, with both client and server"), #endif TEST_CASE(provisioner, pb_adv_no_oob, diff --git a/tests/bsim/bluetooth/mesh/src/test_replay_cache.c b/tests/bsim/bluetooth/mesh/src/test_replay_cache.c index dbab05661cb..d7c140ab3ed 100644 --- a/tests/bsim/bluetooth/mesh/src/test_replay_cache.c +++ b/tests/bsim/bluetooth/mesh/src/test_replay_cache.c @@ -149,6 +149,8 @@ static void test_tx_immediate_replay_attack(void) } ASSERT_TRUE(is_tx_succeeded); + /* Let complete advertising of the previous transaction to prevent collisions. */ + k_sleep(K_SECONDS(1)); } bt_mesh.seq = seq; @@ -165,6 +167,8 @@ static void test_tx_immediate_replay_attack(void) } ASSERT_TRUE(!is_tx_succeeded); + /* Let complete advertising of the previous transaction to prevent collisions. */ + k_sleep(K_SECONDS(1)); } PASS(); @@ -208,6 +212,8 @@ static void test_tx_power_replay_attack(void) } ASSERT_TRUE(!is_tx_succeeded); + /* Let complete advertising of the previous transaction to prevent collisions. */ + k_sleep(K_SECONDS(1)); } for (int i = 0; i < 3; i++) { @@ -222,6 +228,8 @@ static void test_tx_power_replay_attack(void) } ASSERT_TRUE(is_tx_succeeded); + /* Let complete advertising of the previous transaction to prevent collisions. */ + k_sleep(K_SECONDS(1)); } PASS(); diff --git a/tests/bsim/bluetooth/mesh/tests_scripts/dfu/dfu_slot_idempotency.sh b/tests/bsim/bluetooth/mesh/tests_scripts/dfu/dfu_slot_idempotency.sh new file mode 100755 index 00000000000..3f18f1a2651 --- /dev/null +++ b/tests/bsim/bluetooth/mesh/tests_scripts/dfu/dfu_slot_idempotency.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +# Copyright 2023 Nordic Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +source $(dirname "${BASH_SOURCE[0]}")/../../_mesh_test.sh + +# Test DFU Slot API. This test tests that the APIs are idempotent. +conf=prj_mesh1d1_conf +overlay=overlay_pst_conf +RunTest dfu_slot_idempotency dfu_dist_dfu_slot_idempotency + +conf=prj_mesh1d1_conf +overlay="overlay_pst_conf_overlay_psa_conf" +RunTest dfu_slot_idempotency_psa dfu_dist_dfu_slot_idempotency diff --git a/tests/bsim/bluetooth/mesh/tests_scripts/dfu/dfu_slot_reservation.sh b/tests/bsim/bluetooth/mesh/tests_scripts/dfu/dfu_slot_reservation.sh new file mode 100755 index 00000000000..ddd7d0123f5 --- /dev/null +++ b/tests/bsim/bluetooth/mesh/tests_scripts/dfu/dfu_slot_reservation.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +# Copyright 2023 Nordic Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +source $(dirname "${BASH_SOURCE[0]}")/../../_mesh_test.sh + +# Test DFU Slot API. This test tests slot reservation APIs. +conf=prj_mesh1d1_conf +overlay=overlay_pst_conf +RunTest dfu_slot_reservation dfu_dist_dfu_slot_reservation + +conf=prj_mesh1d1_conf +overlay="overlay_pst_conf_overlay_psa_conf" +RunTest dfu_slot_reservation_psa dfu_dist_dfu_slot_reservation diff --git a/tests/bsim/bluetooth/mesh/tests_scripts/provision/pb_remote_client_server_same_dev.sh b/tests/bsim/bluetooth/mesh/tests_scripts/provision/pb_remote_client_server_same_dev.sh new file mode 100755 index 00000000000..33de74370e1 --- /dev/null +++ b/tests/bsim/bluetooth/mesh/tests_scripts/provision/pb_remote_client_server_same_dev.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +# Copyright 2023 Nordic Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +source $(dirname "${BASH_SOURCE[0]}")/../../_mesh_test.sh + +# Test a node re-provisioning through Remote Provisioning models. Procedure: +# 1. Device (prov_device_pb_remote_client_server_same_dev) provisions it self +# and start scanning for an upprovisioned device, and provisions the the +# second device (prov_device_pb_remote_server_same_dev) with local RPR server. +# 2. The first device (prov_device_pb_remote_client_server_same_dev) execute +# device key refresh procedure the second device (prov_device_pb_remote_server_same_dev). +# 3. The first device (prov_device_pb_remote_client_server_same_dev) execute +# composition refresh procedure the second device (prov_device_pb_remote_server_same_dev). +# 4. The first device (prov_device_pb_remote_client_server_same_dev) execute +# address refresh procedure the second device (prov_device_pb_remote_server_same_dev). +# 5. The first device (prov_device_pb_remote_client_server_same_dev) execute +# device key refresh procedure on it self with local RPR client and server. +# 6. The first device (prov_device_pb_remote_client_server_same_dev) execute +# composition refresh procedure on it self with local RPR client and server. +# 7. The first device (prov_device_pb_remote_client_server_same_dev) execute +# address refresh procedure on it self with local RPR client and server. +conf=prj_mesh1d1_conf +RunTest mesh_prov_pb_remote_client_server_same_dev \ + prov_device_pb_remote_client_server_same_dev \ + prov_device_pb_remote_server_same_dev + +conf=prj_mesh1d1_conf +overlay=overlay_psa_conf +RunTest mesh_prov_pb_remote_client_server_same_dev \ + prov_device_pb_remote_client_server_same_dev \ + prov_device_pb_remote_server_same_dev diff --git a/tests/drivers/build_all/modem/modem_cellular.conf b/tests/drivers/build_all/modem/modem_cellular.conf index e541af07e2d..3366fcf32d4 100644 --- a/tests/drivers/build_all/modem/modem_cellular.conf +++ b/tests/drivers/build_all/modem/modem_cellular.conf @@ -1,7 +1,10 @@ CONFIG_TEST=y CONFIG_TEST_RANDOM_GENERATOR=y CONFIG_SERIAL=y -CONFIG_UART_INTERRUPT_DRIVEN=y CONFIG_NETWORKING=y CONFIG_NET_L2_PPP=y CONFIG_MODEM=y +CONFIG_PM_DEVICE=y +CONFIG_MODEM_CELLULAR=y +CONFIG_UART_ASYNC_API=y +CONFIG_GPIO=y diff --git a/tests/drivers/build_all/modem/testcase.yaml b/tests/drivers/build_all/modem/testcase.yaml index 3614cb09655..8b42363b4ff 100644 --- a/tests/drivers/build_all/modem/testcase.yaml +++ b/tests/drivers/build_all/modem/testcase.yaml @@ -74,10 +74,9 @@ tests: - CONFIG_MODEM_IFACE_UART_ASYNC=y drivers.modem.modem_cellular.build: extra_args: CONF_FILE=modem_cellular.conf - platform_exclude: - - serpente - - particle_boron - - rak5010_nrf52840 - - litex_vexriscv - - ip_k66f + platform_allow: + - native_posix_64 + - native_posix + - qemu_x86 + - qemu_x86_64 min_ram: 36 diff --git a/tests/drivers/build_all/regulator/testcase.yaml b/tests/drivers/build_all/regulator/testcase.yaml index 9b9c25511c4..c5dbda1913f 100644 --- a/tests/drivers/build_all/regulator/testcase.yaml +++ b/tests/drivers/build_all/regulator/testcase.yaml @@ -3,8 +3,6 @@ tests: drivers.regulator.build: - tags: - - drivers - - regulator + tags: drivers regulator build_only: true platform_allow: native_posix diff --git a/tests/drivers/flash/common/boards/nrf5340dk_nrf5340_cpuapp_ns.conf b/tests/drivers/flash/common/boards/nrf5340dk_nrf5340_cpuapp_ns.conf new file mode 100644 index 00000000000..821a5e77e5b --- /dev/null +++ b/tests/drivers/flash/common/boards/nrf5340dk_nrf5340_cpuapp_ns.conf @@ -0,0 +1,4 @@ +CONFIG_FCB=y +CONFIG_FLASH_MAP=y +CONFIG_SETTINGS=y +CONFIG_SETTINGS_FCB=y diff --git a/tests/drivers/flash/common/boards/nrf9160dk_nrf9160_ns.conf b/tests/drivers/flash/common/boards/nrf9160dk_nrf9160_ns.conf new file mode 100644 index 00000000000..821a5e77e5b --- /dev/null +++ b/tests/drivers/flash/common/boards/nrf9160dk_nrf9160_ns.conf @@ -0,0 +1,4 @@ +CONFIG_FCB=y +CONFIG_FLASH_MAP=y +CONFIG_SETTINGS=y +CONFIG_SETTINGS_FCB=y diff --git a/tests/drivers/flash/common/boards/nrf9161dk_nrf9161_ns.conf b/tests/drivers/flash/common/boards/nrf9161dk_nrf9161_ns.conf new file mode 100644 index 00000000000..821a5e77e5b --- /dev/null +++ b/tests/drivers/flash/common/boards/nrf9161dk_nrf9161_ns.conf @@ -0,0 +1,4 @@ +CONFIG_FCB=y +CONFIG_FLASH_MAP=y +CONFIG_SETTINGS=y +CONFIG_SETTINGS_FCB=y diff --git a/tests/drivers/flash/common/src/main.c b/tests/drivers/flash/common/src/main.c index 4e73617ba41..79d75ca9b13 100644 --- a/tests/drivers/flash/common/src/main.c +++ b/tests/drivers/flash/common/src/main.c @@ -14,9 +14,6 @@ #define TEST_AREA_DEV_NODE DT_INST(0, nordic_qspi_nor) #elif defined(CONFIG_SPI_NOR) #define TEST_AREA_DEV_NODE DT_INST(0, jedec_spi_nor) -#elif defined(CONFIG_TRUSTED_EXECUTION_NONSECURE) -/* SoC embedded NVM */ -#define TEST_AREA slot1_ns_partition #else #define TEST_AREA storage_partition #endif diff --git a/tests/drivers/flash/common/testcase.yaml b/tests/drivers/flash/common/testcase.yaml index 8e3e3cfbc4c..d1a3174c901 100644 --- a/tests/drivers/flash/common/testcase.yaml +++ b/tests/drivers/flash/common/testcase.yaml @@ -44,7 +44,7 @@ tests: drivers.flash.common.tfm_ns: build_only: true filter: (CONFIG_FLASH_HAS_DRIVER_ENABLED and CONFIG_TRUSTED_EXECUTION_NONSECURE - and dt_label_with_parent_compat_enabled("slot1_ns_partition", "fixed-partitions")) + and dt_label_with_parent_compat_enabled("storage_partition", "fixed-partitions")) integration_platforms: - nrf9161dk_nrf9161_ns drivers.flash.common.stm32: diff --git a/tests/kernel/sched/schedule_api/prj.conf b/tests/kernel/sched/schedule_api/prj.conf index b24271c4232..4178dc560a9 100644 --- a/tests/kernel/sched/schedule_api/prj.conf +++ b/tests/kernel/sched/schedule_api/prj.conf @@ -8,3 +8,4 @@ CONFIG_MAX_THREAD_BYTES=5 CONFIG_TEST_USERSPACE=y CONFIG_MP_MAX_NUM_CPUS=1 CONFIG_ZTEST_FATAL_HOOK=y +CONFIG_MINIMAL_LIBC=y diff --git a/tests/kernel/sched/schedule_api/prj_dumb.conf b/tests/kernel/sched/schedule_api/prj_dumb.conf index 9d996d2c9fb..5331db2daed 100644 --- a/tests/kernel/sched/schedule_api/prj_dumb.conf +++ b/tests/kernel/sched/schedule_api/prj_dumb.conf @@ -6,3 +6,4 @@ CONFIG_SCHED_DUMB=y CONFIG_MAX_THREAD_BYTES=5 CONFIG_MP_MAX_NUM_CPUS=1 CONFIG_ZTEST_FATAL_HOOK=y +CONFIG_MINIMAL_LIBC=y diff --git a/tests/kernel/sched/schedule_api/prj_multiq.conf b/tests/kernel/sched/schedule_api/prj_multiq.conf index d1074c07585..0c37654fd60 100644 --- a/tests/kernel/sched/schedule_api/prj_multiq.conf +++ b/tests/kernel/sched/schedule_api/prj_multiq.conf @@ -6,3 +6,4 @@ CONFIG_SCHED_MULTIQ=y CONFIG_MAX_THREAD_BYTES=5 CONFIG_MP_MAX_NUM_CPUS=1 CONFIG_ZTEST_FATAL_HOOK=y +CONFIG_MINIMAL_LIBC=y diff --git a/tests/kernel/timer/timer_behavior/src/jitter_drift.c b/tests/kernel/timer/timer_behavior/src/jitter_drift.c index cd6afaea00d..6d3e78d514b 100644 --- a/tests/kernel/timer/timer_behavior/src/jitter_drift.c +++ b/tests/kernel/timer/timer_behavior/src/jitter_drift.c @@ -121,6 +121,17 @@ static void do_test_using(void (*sample_collection_fn)(void)) periodic_idx = 0; k_sem_init(&periodic_sem, 0, 1); + + /* Align to tick boundary. Otherwise the first handler execution + * might turn out to be significantly late and cause the test to + * fail. This can happen if k_timer_start() is called right before + * the upcoming tick boundary and in consequence the tick passes + * between the moment when the kernel decides what tick to use for + * the next timeout and the moment when the system timer actually + * sets up that timeout. + */ + k_sleep(K_TICKS(1)); + sample_collection_fn(); k_sem_take(&periodic_sem, K_FOREVER); diff --git a/tests/lib/devicetree/devices/testcase.yaml b/tests/lib/devicetree/devices/testcase.yaml index b57489b9a11..673e2319702 100644 --- a/tests/lib/devicetree/devices/testcase.yaml +++ b/tests/lib/devicetree/devices/testcase.yaml @@ -15,3 +15,5 @@ tests: - bl5340_dvk_cpuapp - bl5340_dvk_cpuapp_ns - mimxrt595_evk_cm33 + - nrf9131ek_nrf9131 + - nrf9131ek_nrf9131_ns diff --git a/tests/lib/newlib/heap_listener/prj.conf b/tests/lib/newlib/heap_listener/prj.conf index 31bc1ca7e76..8d8ebf99baf 100644 --- a/tests/lib/newlib/heap_listener/prj.conf +++ b/tests/lib/newlib/heap_listener/prj.conf @@ -1,4 +1,5 @@ CONFIG_ZTEST=y CONFIG_NEWLIB_LIBC=y +CONFIG_NEWLIB_LIBC_NANO=n CONFIG_NEWLIB_LIBC_HEAP_LISTENER=y CONFIG_ZTEST_NEW_API=y diff --git a/tests/net/lib/coap_client/src/main.c b/tests/net/lib/coap_client/src/main.c index a3862852c21..bb9d9fc38c5 100644 --- a/tests/net/lib/coap_client/src/main.c +++ b/tests/net/lib/coap_client/src/main.c @@ -392,7 +392,5 @@ ZTEST(coap_client, test_unmatching_tokens) k_sleep(K_MSEC(1)); k_sleep(K_MSEC(1)); clear_socket_events(); - zassert_equal(last_response_code, COAP_RESPONSE_CODE_NOT_FOUND, "Unexpected response %d", - last_response_code); - k_sleep(K_MSEC(1)); + k_sleep(K_MSEC(1000)); } diff --git a/tests/net/lib/lwm2m/content_json/prj.conf b/tests/net/lib/lwm2m/content_json/prj.conf index 3f2cd3e60f2..7ff07df9897 100644 --- a/tests/net/lib/lwm2m/content_json/prj.conf +++ b/tests/net/lib/lwm2m/content_json/prj.conf @@ -9,3 +9,5 @@ CONFIG_LWM2M=y CONFIG_LWM2M_COAP_MAX_MSG_SIZE=512 CONFIG_LWM2M_RW_JSON_SUPPORT=y CONFIG_JSON_LIBRARY=y + +CONFIG_PICOLIBC_IO_FLOAT=y diff --git a/tests/net/lib/lwm2m/content_plain_text/prj.conf b/tests/net/lib/lwm2m/content_plain_text/prj.conf index 6041addad83..877c969958f 100644 --- a/tests/net/lib/lwm2m/content_plain_text/prj.conf +++ b/tests/net/lib/lwm2m/content_plain_text/prj.conf @@ -6,3 +6,5 @@ CONFIG_ZTEST_NEW_API=y CONFIG_ENTROPY_GENERATOR=y CONFIG_TEST_RANDOM_GENERATOR=y CONFIG_LWM2M=y + +CONFIG_PICOLIBC_IO_FLOAT=y diff --git a/tests/net/lib/lwm2m/content_plain_text/src/main.c b/tests/net/lib/lwm2m/content_plain_text/src/main.c index c7cc857224c..7bc77a82327 100644 --- a/tests/net/lib/lwm2m/content_plain_text/src/main.c +++ b/tests/net/lib/lwm2m/content_plain_text/src/main.c @@ -470,37 +470,6 @@ ZTEST(net_content_plain_text_nodata, test_get_bool_nodata) zassert_equal(ret, -ENODATA, "Invalid error code returned"); } -ZTEST(net_content_plain_text, test_get_opaque) -{ - int ret; - const char *payload = "test_payload"; - uint8_t buf[16]; - bool last_block; - struct lwm2m_opaque_context ctx = { 0 }; - - test_payload_set(payload); - - ret = plain_text_reader.get_opaque(&test_in, buf, sizeof(buf), - &ctx, &last_block); - zassert_equal(ret, strlen(payload), "Invalid length returned"); - zassert_mem_equal(buf, payload, strlen(payload), - "Invalid value parsed"); - zassert_equal(test_in.offset, strlen(payload) + 1, - "Invalid packet offset"); -} - -ZTEST(net_content_plain_text_nodata, test_get_opaque_nodata) -{ - int ret; - uint8_t value[4]; - bool last_block; - struct lwm2m_opaque_context ctx = { 0 }; - - ret = plain_text_reader.get_opaque(&test_in, value, sizeof(value), - &ctx, &last_block); - zassert_equal(ret, 0, "Invalid error code returned"); -} - ZTEST(net_content_plain_text, test_get_objlnk) { int ret; diff --git a/tests/net/lib/lwm2m/interop/CMakeLists.txt b/tests/net/lib/lwm2m/interop/CMakeLists.txt new file mode 100644 index 00000000000..89c4d33e43e --- /dev/null +++ b/tests/net/lib/lwm2m/interop/CMakeLists.txt @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(lwm2m_interop_tests) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) + +include(${ZEPHYR_BASE}/samples/net/common/common.cmake) diff --git a/tests/net/lib/lwm2m/interop/README.md b/tests/net/lib/lwm2m/interop/README.md new file mode 100644 index 00000000000..858f5007f64 --- /dev/null +++ b/tests/net/lib/lwm2m/interop/README.md @@ -0,0 +1,103 @@ +# LwM2M Interoperability tests using Leshan demo server + +This directory contains list of testcases that use +the Twister's Pytest integration to run testcases against Leshan demo server. +These tests use emulated hardware (native_posix). + +These tests require setup that is not done in Twister run, so follow this documentation to set +up the test environment. + +## Network setup + +As with typical network samples, host machine uses IP address `192.0.2.2` and the emulated device +running Zephyr is using address `192.0.2.1`. + +Follow [Networking with the host system](https://docs.zephyrproject.org/latest/connectivity/networking/networking_with_host.html#networking-with-the-host-system) +from Zephyr's documentation how to set it up, or follow [Create NAT and routing for Zephyr native network on Linux](https://github.com/zephyrproject-rtos/net-tools/blob/master/README%20NAT.md). + +### Leshan server setup + +* Leshan server must be reachable from the device using IP address `192.0.2.2`. + Configure the port forwarding, if you use Docker to run Leshan. +* Leshan demo server REST API must be reachable from localhost. +* tcp/8080 Leshan web interface and REST API +* tcp/8081 Leshan bootstrap server REST API +* udp/5683 Leshan non-secure CoAP +* udp/5684 Leshan DTLS CoAP +* udp/5783 non-secure Bootstrap CoAP +* udp/5684 DTLS Bootstrap CoAP +* Download Leshan JAR file from https://ci.eclipse.org/leshan/job/leshan/lastSuccessfulBuild/artifact/leshan-server-demo.jar +* Download Leshan Bootstrap server JAR file from https://ci.eclipse.org/leshan/job/leshan/lastSuccessfulBuild/artifact/leshan-bsserver-demo.jar + +Both server can be started like this: +``` +java -jar ./leshan-server-demo.jar -wp 8080 -vv +java -jar ./leshan-bsserver-demo.jar -lp=5783 -slp=5784 -wp 8081 +``` + +Or create a helper script that does everything, including download: +``` +#!/bin/sh -eu + +# Download Leshan if needed +if [ ! -f leshan-server-demo.jar ]; then + wget https://ci.eclipse.org/leshan/job/leshan/lastSuccessfulBuild/artifact/leshan-server-demo.jar +fi + +if [ ! -f leshan-bsserver-demo.jar ]; then + wget 'https://ci.eclipse.org/leshan/job/leshan/lastSuccessfulBuild/artifact/leshan-bsserver-demo.jar' +fi + +mkdir -p log + +start-stop-daemon --make-pidfile --pidfile log/leshan.pid --chdir $(pwd) --background --start \ + --startas /bin/bash -- -c "exec java -jar ./leshan-server-demo.jar -wp 8080 -vv --models-folder objects >log/leshan.log 2>&1" + +start-stop-daemon --make-pidfile --pidfile log/leshan_bs.pid --chdir $(pwd) --background --start \ + --startas /bin/bash -- -c "exec java -jar ./leshan-bsserver-demo.jar -lp=5783 -slp=5784 -wp 8081 -vv >log/leshan_bs.log 2>&1" +``` + +Then stopping would require similar script: +``` +#!/bin/sh -eu + +start-stop-daemon --remove-pidfile --pidfile log/leshan.pid --stop +start-stop-daemon --remove-pidfile --pidfile log/leshan_bs.pid --stop +``` + +## Python package requirements + +These tests require extra package that is not installed when you follow the Zephyr's setup. +Install with `pip install CoAPthon3` + +## Running tests + +``` +twister -p native_posix -vv --enable-slow -T tests/net/lib/lwm2m/interop +`````` + +## Test specification + +Tests are written from test spec; +[OMA Enabler Test Specification (Interoperability) for Lightweight M2M](https://www.openmobilealliance.org/release/LightweightM2M/ETS/OMA-ETS-LightweightM2M-V1_1-20190912-D.pdf) + +Following tests are implemented: +* LightweightM2M-1.1-int-0 – Client Initiated Bootstrap +* LightweightM2M-1.1-int-1 – Client Initiated Bootstrap Full (PSK) +* LightweightM2M-1.1-int-101 – Initial Registration +* LightweightM2M-1.1-int-102 – Registration Update +* LightweightM2M-1.1-int-104 – Registration Update Trigge +* LightweightM2M-1.1-int-105 - Discarded Register Update +* LightweightM2M-1.1-int-107 – Extending the lifetime of a registration +* LightweightM2M-1.1-int-108 – Turn on Queue Mode +* LightweightM2M-1.1-int-109 – Behavior in Queue Mode +* LightweightM2M-1.1-int-201 – Querying basic information in Plain Text +* LightweightM2M-1.1-int-203 – Querying basic information in TLV format +* LightweightM2M-1.1-int-204 – Querying basic information in JSON format +* LightweightM2M-1.1-int-205 – Setting basic information in Plain Text +* LightweightM2M-1.1-int-211 – Querying basic information in CBOR format +* LightweightM2M-1.1-int-212 – Setting basic information in CBOR format +* LightweightM2M-1.1-int-215 – Setting basic information in TLV format +* LightweightM2M-1.1-int-220 – Setting basic information in JSON format +* LightweightM2M-1.1-int-221 – Attempt to perform operations on Security +* LightweightM2M-1.1-int-401 – UDP Channel Security – PSK Mode diff --git a/tests/net/lib/lwm2m/interop/boards/native_posix.conf b/tests/net/lib/lwm2m/interop/boards/native_posix.conf new file mode 100644 index 00000000000..44346db12ed --- /dev/null +++ b/tests/net/lib/lwm2m/interop/boards/native_posix.conf @@ -0,0 +1,7 @@ +CONFIG_DNS_RESOLVER=y +CONFIG_DNS_SERVER_IP_ADDRESSES=y +CONFIG_DNS_SERVER1="192.0.2.2" +CONFIG_LWM2M_DNS_SUPPORT=y +CONFIG_NET_CONFIG_MY_IPV4_GW="192.0.2.2" +CONFIG_NATIVE_POSIX_SLOWDOWN_TO_REAL_TIME=y +CONFIG_NATIVE_UART_0_ON_STDINOUT=y diff --git a/tests/net/lib/lwm2m/interop/boards/qemu_cortex_m3.conf b/tests/net/lib/lwm2m/interop/boards/qemu_cortex_m3.conf new file mode 100644 index 00000000000..7a3fd344e50 --- /dev/null +++ b/tests/net/lib/lwm2m/interop/boards/qemu_cortex_m3.conf @@ -0,0 +1,20 @@ +CONFIG_NET_L2_ETHERNET=y +CONFIG_ETH_DRIVER=y +CONFIG_ETH_STELLARIS=y +CONFIG_NET_QEMU_ETHERNET=y + +# RAM/ROM tuning +CONFIG_IDLE_STACK_SIZE=128 +CONFIG_MBEDTLS_HEAP_SIZE=7000 +CONFIG_ISR_STACK_SIZE=512 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1024 +CONFIG_LWM2M_ENGINE_STACK_SIZE=2000 +CONFIG_LWM2M_LOG_LEVEL_INF=y +CONFIG_LWM2M_ENGINE_MAX_MESSAGES=3 +CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE=0 +CONFIG_LWM2M_ENGINE_MAX_OBSERVER=5 +CONFIG_LWM2M_SECURITY_DTLS_TLS_CIPHERSUITE_MAX=3 +CONFIG_LWM2M_DEVICE_PWRSRC_MAX=2 +CONFIG_LWM2M_DEVICE_ERROR_CODE_MAX=5 +CONFIG_LWM2M_DEVICE_EXT_DEV_INFO_MAX=2 +CONFIG_LWM2M_NUM_ATTR=10 diff --git a/tests/net/lib/lwm2m/interop/prj.conf b/tests/net/lib/lwm2m/interop/prj.conf new file mode 100644 index 00000000000..66d6334264f --- /dev/null +++ b/tests/net/lib/lwm2m/interop/prj.conf @@ -0,0 +1,92 @@ +CONFIG_NETWORKING=y +CONFIG_LOG=y +CONFIG_LWM2M_LOG_LEVEL_DBG=y +CONFIG_TEST_RANDOM_GENERATOR=y +CONFIG_NET_IPV6=y +CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=3 +CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=2 +CONFIG_NET_IPV4=y +CONFIG_NET_DHCPV4=n +CONFIG_NET_IF_UNICAST_IPV4_ADDR_COUNT=3 +CONFIG_NET_IF_MCAST_IPV4_ADDR_COUNT=2 +CONFIG_PRINTK=y +CONFIG_NET_PKT_RX_COUNT=10 +CONFIG_NET_PKT_TX_COUNT=10 +CONFIG_NET_BUF_RX_COUNT=10 +CONFIG_NET_BUF_TX_COUNT=10 +CONFIG_NET_MAX_CONTEXTS=5 +CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::1" +CONFIG_NET_CONFIG_PEER_IPV6_ADDR="2001:db8::2" +CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.0.2.1" +CONFIG_NET_CONFIG_MY_IPV4_GW="192.0.2.2" + +CONFIG_NET_LOG=y + +CONFIG_NET_CONFIG_NEED_IPV6=y +CONFIG_NET_CONFIG_NEED_IPV4=y +CONFIG_NET_CONFIG_SETTINGS=y + +CONFIG_LWM2M=y +CONFIG_LWM2M_COAP_BLOCK_SIZE=512 +CONFIG_LWM2M_IPSO_SUPPORT=y +CONFIG_LWM2M_SHELL=y +CONFIG_LWM2M_ACCESS_CONTROL_ENABLE=n + +#Enable Portfolio object +CONFIG_LWM2M_PORTFOLIO_OBJ_SUPPORT=y + +#LwM2M v1.1 configure +CONFIG_LWM2M_VERSION_1_1=y +CONFIG_LWM2M_DTLS_SUPPORT=y +CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP=y + +#Enable SenML JSON content format +CONFIG_JSON_LIBRARY=y +CONFIG_BASE64=y +CONFIG_LWM2M_RW_SENML_JSON_SUPPORT=y + +#Enable SenML CBOR content format +CONFIG_LWM2M_RW_SENML_CBOR_SUPPORT=y +CONFIG_LWM2M_RW_SENML_CBOR_RECORDS=60 +CONFIG_ZCBOR_CANONICAL=y + +#Enable legacy content formats +CONFIG_LWM2M_RW_JSON_SUPPORT=y +CONFIG_LWM2M_RW_OMA_TLV_SUPPORT=y + +# Longer endpoint name might be returned in a registration reply +CONFIG_COAP_EXTENDED_OPTIONS_LEN=y +CONFIG_COAP_EXTENDED_OPTIONS_LEN_VALUE=40 + +# Use QUEUE mode by default +CONFIG_LWM2M_QUEUE_MODE_ENABLED=y +CONFIG_LWM2M_QUEUE_MODE_UPTIME=20 +CONFIG_LWM2M_UPDATE_PERIOD=30 +CONFIG_LWM2M_SECONDS_TO_UPDATE_EARLY=10 + +# LwM2M configuration as OMA-ETS-LightweightM2M_INT-V1_1-20190912-D Configuration 3 +CONFIG_LWM2M_ENGINE_DEFAULT_LIFETIME=30 +CONFIG_LWM2M_SERVER_DEFAULT_PMIN=1 +CONFIG_LWM2M_SERVER_DEFAULT_PMAX=10 + +CONFIG_MBEDTLS=y +CONFIG_MBEDTLS_TLS_VERSION_1_2=y + +# Special MbedTLS changes +CONFIG_MBEDTLS_ENABLE_HEAP=y +CONFIG_MBEDTLS_HEAP_SIZE=8192 +CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=1500 +CONFIG_MBEDTLS_CIPHER_CCM_ENABLED=y + +# Disable RSA, we don't parse certs: saves flash/memory +CONFIG_MBEDTLS_KEY_EXCHANGE_RSA_ENABLED=n +# Enable PSK instead +CONFIG_MBEDTLS_KEY_EXCHANGE_PSK_ENABLED=y + +CONFIG_NET_SOCKETS_SOCKOPT_TLS=y +CONFIG_NET_SOCKETS_TLS_MAX_CONTEXTS=4 +CONFIG_NET_SOCKETS_ENABLE_DTLS=y + +# MbedTLS needs a larger stack +CONFIG_MAIN_STACK_SIZE=2048 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 diff --git a/tests/net/lib/lwm2m/interop/pytest/leshan.py b/tests/net/lib/lwm2m/interop/pytest/leshan.py new file mode 100644 index 00000000000..4d69a3a977f --- /dev/null +++ b/tests/net/lib/lwm2m/interop/pytest/leshan.py @@ -0,0 +1,104 @@ +# Copyright (c) 2023 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import json +import requests +import binascii + +class Leshan: + def __init__(self, url: str): + self.api_url = url + self.timeout = 10 + self.format = 'TLV' + # self.format = "SENML_CBOR" + try: + resp = self.get('/security/clients') + if not isinstance(resp, list): + raise RuntimeError('Did not receive list of endpoints') + except requests.exceptions.ConnectionError: + raise RuntimeError('Leshan not responding') + + @staticmethod + def handle_response(resp: requests.models.Response): + """Generic response handler for all queries""" + if resp.status_code >= 300 or resp.status_code < 200: + raise RuntimeError(f'Error {resp.status_code}: {resp.text}') + if len(resp.text): + obj = json.loads(resp.text) + return obj + else: + return None + + def get(self, path: str): + """Send HTTP GET query""" + resp = requests.get(f'{self.api_url}{path}?timeout={self.timeout}&format={self.format}') + return Leshan.handle_response(resp) + + def put_raw(self, path: str, data: str | dict | None = None, headers: dict | None = None): + resp = requests.put(f'{self.api_url}{path}', data=data, headers=headers) + return Leshan.handle_response(resp) + + def put(self, path: str, data: str | dict, uri_options: str = ''): + if isinstance(data, dict): + data = json.dumps(data) + return self.put_raw(f'{path}?timeout={self.timeout}&format={self.format}' + uri_options, data=data, headers={'content-type': 'application/json'}) + + def post(self, path: str, data: str | dict | None = None): + resp = requests.post(f'{self.api_url}{path}', data=data, headers={'content-type': 'application/json'}) + return Leshan.handle_response(resp) + + def delete(self, path: str): + resp = requests.delete(f'{self.api_url}{path}') + return Leshan.handle_response(resp) + + def execute(self, endpoint: str, path: str): + return self.post(f'/clients/{endpoint}/{path}') + + def write(self, endpoint: str, path: str, value: bool | int | str): + if isinstance(value, bool): + type = 'boolean' + value = "true" if value else "false" + elif isinstance(value, int): + type = 'integer' + value = str(value) + elif isinstance(value, str): + type = 'string' + value = '"' + value + '"' + id = path.split('/')[2] + return self.put(f'/clients/{endpoint}/{path}', f'{{"id":{id},"kind":"singleResource","value":{value},"type":"{type}"}}') + + def read(self, endpoint: str, path: str): + resp = self.get(f'/clients/{endpoint}/{path}') + if not resp['success']: + return resp + content = resp['content'] + if content['kind'] == 'instance': + return content['resources'] + elif content['kind'] == 'singleResource': + return content['value'] + elif content['kind'] == 'multiResource': + return content['values'] + raise RuntimeError(f'Unhandled type {content["kind"]}') + + def create_psk_device(self, endpoint: str, passwd: str): + psk = binascii.b2a_hex(passwd.encode()).decode() + self.put('/security/clients/', f'{{"endpoint":"{endpoint}","tls":{{"mode":"psk","details":{{"identity":"{endpoint}","key":"{psk}"}} }} }}') + + def delete_device(self, endpoint: str): + self.delete(f'/security/clients/{endpoint}') + + def create_bs_device(self, endpoint: str, server_uri: str, passwd: str): + psk = binascii.b2a_hex(passwd.encode()).decode() + data = f'{{"tls":{{"mode":"psk","details":{{"identity":"{endpoint}","key":"{psk}"}}}},"endpoint":"{endpoint}"}}' + self.put('/security/clients/', data) + id = str([ord(n) for n in endpoint]) + key = str([ord(n) for n in passwd]) + content = '{"servers":{"0":{"binding":"U","defaultMinPeriod":1,"lifetime":86400,"notifIfDisabled":false,"shortId":1}},"security":{"1":{"bootstrapServer":false,"clientOldOffTime":1,"publicKeyOrId":' + id + ',"secretKey":' + key + ',"securityMode":"PSK","serverId":1,"serverSmsNumber":"","smsBindingKeyParam":[],"smsBindingKeySecret":[],"smsSecurityMode":"NO_SEC","uri":"'+server_uri+'"}},"oscore":{},"toDelete":["/0","/1"]}' + self.post(f'/bootstrap/{endpoint}', content) + + def delete_bs_device(self, endpoint: str): + self.delete(f'/security/clients/{endpoint}') + self.delete(f'/bootstrap/{endpoint}') diff --git a/tests/net/lib/lwm2m/interop/pytest/test_lwm2m.py b/tests/net/lib/lwm2m/interop/pytest/test_lwm2m.py new file mode 100644 index 00000000000..21ed51ef1e6 --- /dev/null +++ b/tests/net/lib/lwm2m/interop/pytest/test_lwm2m.py @@ -0,0 +1,446 @@ +# Copyright (c) 2023 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 + +import time +import logging +import pytest +from leshan import Leshan +import os +import binascii +import random +import string + +from twister_harness import Shell + +LESHAN_IP: str = '192.0.2.2' +COAP_PORT: int = 5683 +COAPS_PORT: int = 5684 +BOOTSTRAP_COAPS_PORT: int = 5784 + +logger = logging.getLogger(__name__) + +@pytest.fixture(scope='module') +def helperclient() -> object: + try: + from coapthon.client.helperclient import HelperClient + except ModuleNotFoundError: + pytest.skip('CoAPthon3 package not installed') + return HelperClient(server=('127.0.0.1', COAP_PORT)) + +@pytest.fixture(scope='session') +def leshan() -> Leshan: + try: + return Leshan("http://localhost:8080/api") + except RuntimeError: + pytest.skip('Leshan server not available') + +@pytest.fixture(scope='session') +def leshan_bootstrap() -> Leshan: + try: + return Leshan("http://localhost:8081/api") + except RuntimeError: + pytest.skip('Leshan Bootstrap server not available') + +# +# Test specification: +# https://www.openmobilealliance.org/release/LightweightM2M/ETS/OMA-ETS-LightweightM2M-V1_1-20190912-D.pdf +# + +def verify_LightweightM2M_1_1_int_0(shell: Shell): + logger.info("LightweightM2M-1.1-int-0 - Client Initiated Bootstrap") + shell._device.readlines_until(regex='.*Bootstrap started with endpoint', timeout=5.0) + shell._device.readlines_until(regex='.*Bootstrap registration done', timeout=5.0) + shell._device.readlines_until(regex='.*Bootstrap data transfer done', timeout=5.0) + +def verify_LightweightM2M_1_1_int_1(shell: Shell, leshan: Leshan, endpoint: str): + logger.info("LightweightM2M-1.1-int-1 - Client Initiated Bootstrap Full (PSK)") + verify_LightweightM2M_1_1_int_0(shell) + verify_LightweightM2M_1_1_int_101(shell, leshan, endpoint) + verify_LightweightM2M_1_1_int_401(shell, leshan, endpoint) + +def verify_LightweightM2M_1_1_int_101(shell: Shell, leshan: Leshan, endpoint: str): + logger.info("LightweightM2M-1.1-int-101 - Initial Registration") + shell._device.readlines_until(regex='.*Registration Done', timeout=5.0) + assert leshan.get(f'/clients/{endpoint}') + +def verify_LightweightM2M_1_1_int_102(shell: Shell, leshan: Leshan, endpoint: str): + logger.info("LightweightM2M-1.1-int-102 - Registration Update") + lines = shell.get_filtered_output(shell.exec_command('lwm2m read 1/0/1 -u32')) + litetime = int(lines[0]) + lifetime = litetime + 10 + start_time = time.time() * 1000 + leshan.write(endpoint, '1/0/1', lifetime) + shell._device.readlines_until(regex='.*net_lwm2m_rd_client: Update Done', timeout=5.0) + latest = leshan.get(f'/clients/{endpoint}') + assert latest["lastUpdate"] > start_time + assert latest["lastUpdate"] <= time.time()*1000 + assert latest["lifetime"] == lifetime + shell.exec_command('lwm2m write 1/0/1 -u32 86400') + +def verify_LightweightM2M_1_1_int_103(): + """LightweightM2M-1.1-int-103 - Deregistration""" + # Unsupported. We don't have "disabled" functionality in server object + +def verify_LightweightM2M_1_1_int_104(shell: Shell, leshan: Leshan, endpoint: str): + logger.info("LightweightM2M-1.1-int-104 - Registration Update Trigger") + shell.exec_command('lwm2m update') + shell._device.readlines_until(regex='.*net_lwm2m_rd_client: Update Done', timeout=5.0) + leshan.execute(endpoint, '1/0/8') + shell._device.readlines_until(regex='.*net_lwm2m_rd_client: Update Done', timeout=5.0) + +def verify_LightweightM2M_1_1_int_105(shell: Shell, leshan: Leshan, endpoint: str, helperclient: object): + logger.info("LightweightM2M-1.1-int-105 - Discarded Register Update") + status = leshan.get(f'/clients/{endpoint}') + if status["secure"]: + logger.debug("Skip, requires non-secure connection") + return + id = status["registrationId"] + assert id + # Fake unregister message + helperclient.delete(f'rd/{id}', timeout=0.1) + helperclient.stop() + time.sleep(1) + shell.exec_command('lwm2m update') + shell._device.readlines_until(regex=r'.*Failed with code 4\.4', timeout=5.0) + shell._device.readlines_until(regex='.*Registration Done', timeout=10.0) + +def verify_LightweightM2M_1_1_int_107(shell: Shell, leshan: Leshan, endpoint: str): + logger.info("LightweightM2M-1.1-int-107 - Extending the lifetime of a registration") + leshan.write(endpoint, '1/0/1', 120) + shell._device.readlines_until(regex='.*net_lwm2m_rd_client: Update Done', timeout=5.0) + lines = shell.get_filtered_output(shell.exec_command('lwm2m read 1/0/1 -u32')) + lifetime = int(lines[0]) + assert lifetime == 120 + logger.debug(f'sleeping for {lifetime} s') + shell._device.readlines_until(regex='.*net_lwm2m_rd_client: Update Done', timeout=lifetime) + assert leshan.get(f'/clients/{endpoint}') + +def verify_LightweightM2M_1_1_int_108(leshan, endpoint): + logger.info("LightweightM2M-1.1-int-108 - Turn on Queue Mode") + assert leshan.get(f'/clients/{endpoint}')["queuemode"] + +def verify_LightweightM2M_1_1_int_109(shell: Shell, leshan: Leshan, endpoint: str): + logger.info("LightweightM2M-1.1-int-109 - Behavior in Queue Mode") + verify_LightweightM2M_1_1_int_107(shell, leshan, endpoint) + shell._device.readlines_until(regex='.*Queue mode RX window closed', timeout=120) + # Restore previous value + shell.exec_command('lwm2m write 1/0/1 -u32 86400') + shell._device.readlines_until(regex='.*Registration update complete', timeout=10) + +def verify_LightweightM2M_1_1_int_201(shell: Shell, leshan: Leshan, endpoint: str): + + logger.info("LightweightM2M-1.1-int-201 - Querying basic information in Plain Text format") + fmt = leshan.format + leshan.format = 'TEXT' + assert leshan.get(f'/clients/{endpoint}/3/0/0')['content']['value'] == 'Zephyr' + assert leshan.get(f'/clients/{endpoint}/3/0/1')['content']['value'] == 'client-1' + assert leshan.get(f'/clients/{endpoint}/3/0/2')['content']['value'] == 'serial-1' + leshan.format = fmt + +def verify_device_object(resp): + ''' Verify that Device object match Configuration 3 ''' + assert resp['valid'] is True + found = 0 + for res in resp['content']['resources']: + if res['id'] == 0: + assert res['value'] == 'Zephyr' + found += 1 + elif res['id'] == 1: + assert res['value'] == 'client-1' + found += 1 + elif res['id'] == 2: + assert res['value'] == 'serial-1' + found += 1 + elif res['id'] == 3: + assert res['value'] == '1.2.3' + found += 1 + elif res['id'] == 11: + assert res['kind'] == 'multiResource' + assert res['values']['0'] == '0' + found += 1 + elif res['id'] == 16: + assert res['value'] == 'U' + found += 1 + assert found == 6 + +def verify_server_object(obj): + ''' Verify that server object match Configuration 3 ''' + found = 0 + for res in obj['resources']: + if res['id'] == 0: + assert res['value'] == '1' + found += 1 + elif res['id'] == 1: + assert res['value'] == '86400' + found += 1 + elif res['id'] == 2: + assert res['value'] == '1' + found += 1 + elif res['id'] == 3: + assert res['value'] == '10' + found += 1 + elif res['id'] == 5: + assert res['value'] == '86400' + found += 1 + elif res['id'] == 6: + assert res['value'] is False + found += 1 + elif res['id'] == 7: + assert res['value'] == 'U' + found += 1 + assert found == 7 + +def verify_LightweightM2M_1_1_int_203(shell: Shell, leshan: Leshan, endpoint: str): + shell.exec_command('lwm2m update') + logger.info('LightweightM2M-1.1-int-203 - Querying basic information in TLV format') + fmt = leshan.format + leshan.format = 'TLV' + resp = leshan.get(f'/clients/{endpoint}/3/0') + verify_device_object(resp) + leshan.format = fmt + +def verify_LightweightM2M_1_1_int_204(shell: Shell, leshan: Leshan, endpoint: str): + shell.exec_command('lwm2m update') + logger.info('LightweightM2M-1.1-int-204 - Querying basic information in JSON format') + fmt = leshan.format + leshan.format = 'JSON' + resp = leshan.get(f'/clients/{endpoint}/3/0') + verify_device_object(resp) + leshan.format = fmt + +def verify_LightweightM2M_1_1_int_205(shell: Shell, leshan: Leshan, endpoint: str): + logger.info('LightweightM2M-1.1-int-205 - Setting basic information in Plain Text format') + fmt = leshan.format + leshan.format = 'TEXT' + leshan.write(endpoint, '1/0/2', 101) + leshan.write(endpoint, '1/0/3', 1010) + leshan.write(endpoint, '1/0/5', 2000) + assert leshan.read(endpoint, '1/0/2') == '101' + assert leshan.read(endpoint, '1/0/3') == '1010' + assert leshan.read(endpoint, '1/0/5') == '2000' + leshan.write(endpoint, '1/0/2', 1) + leshan.write(endpoint, '1/0/3', 10) + leshan.write(endpoint, '1/0/5', 86400) + assert leshan.read(endpoint, '1/0/2') == '1' + assert leshan.read(endpoint, '1/0/3') == '10' + assert leshan.read(endpoint, '1/0/5') == '86400' + leshan.format = fmt + +def verify_LightweightM2M_1_1_int_211(shell: Shell, leshan: Leshan, endpoint: str): + logger.info('LightweightM2M-1.1-int-211 - Querying basic information in CBOR format') + fmt = leshan.format + leshan.format = 'CBOR' + lines = shell.get_filtered_output(shell.exec_command('lwm2m read 1/0/0 -u16')) + id = lines[0] + assert leshan.read(endpoint, '1/0/0') == id + assert leshan.read(endpoint, '1/0/6') is False + assert leshan.read(endpoint, '1/0/7') == 'U' + leshan.format = fmt + +def verify_LightweightM2M_1_1_int_212(shell: Shell, leshan: Leshan, endpoint: str): + logger.info('LightweightM2M-1.1-int-212 - Setting basic information in CBOR format') + fmt = leshan.format + leshan.format = 'CBOR' + leshan.write(endpoint, '1/0/2', 101) + leshan.write(endpoint, '1/0/3', 1010) + leshan.write(endpoint, '1/0/6', True) + assert leshan.read(endpoint, '1/0/2') == '101' + assert leshan.read(endpoint, '1/0/3') == '1010' + assert leshan.read(endpoint, '1/0/6') is True + leshan.write(endpoint, '1/0/2', 1) + leshan.write(endpoint, '1/0/3', 10) + leshan.write(endpoint, '1/0/6', False) + leshan.format = fmt + +def verify_setting_basic_in_format(shell, leshan, endpoint, format): + fmt = leshan.format + leshan.format = format + server_obj = leshan.get(f'/clients/{endpoint}/1/0')['content'] + verify_server_object(server_obj) + # Remove Read-Only resources, so we don't end up writing those + for res in server_obj['resources']: + if res['id'] in (0, 11, 12): + server_obj['resources'].remove(res) + data = '''{ + "kind": "instance", + "id": 0, + "resources": [ + { + "id": 2, + "kind": "singleResource", + "value": "101", + "type": "integer" + }, + { + "id": 3, + "kind": "singleResource", + "value": "1010", + "type": "integer" + }, + { + "id": 5, + "kind": "singleResource", + "value": "2000", + "type": "integer" + }, + { + "id": 6, + "kind": "singleResource", + "value": true, + "type": "boolean" + }, + { + "id": 7, + "kind": "singleResource", + "value": "U", + "type": "string" + } + ] + }''' + assert leshan.put(f'/clients/{endpoint}/1/0', data, uri_options = '&replace=false')['status'] == 'CHANGED(204)' + resp = leshan.get(f'/clients/{endpoint}/1/0') + assert resp['valid'] is True + found = 0 + for res in resp['content']['resources']: + if res['id'] == 2: + assert res['value'] == '101' + found += 1 + elif res['id'] == 3: + assert res['value'] == '1010' + found += 1 + elif res['id'] == 5: + assert res['value'] == '2000' + found += 1 + elif res['id'] == 6: + assert res['value'] is True + found += 1 + elif res['id'] == 7: + assert res['value'] == 'U' + found += 1 + assert found == 5 + assert leshan.put(f'/clients/{endpoint}/1/0', data = server_obj, uri_options = '&replace=true')['status'] == 'CHANGED(204)' + server_obj = leshan.get(f'/clients/{endpoint}/1/0')['content'] + verify_server_object(server_obj) + leshan.format = fmt + +def verify_LightweightM2M_1_1_int_215(shell: Shell, leshan: Leshan, endpoint: str): + logger.info('LightweightM2M-1.1-int-215 - Setting basic information in TLV format') + verify_setting_basic_in_format(shell, leshan, endpoint, 'TLV') + +def verify_LightweightM2M_1_1_int_220(shell: Shell, leshan: Leshan, endpoint: str): + logger.info('LightweightM2M-1.1-int-220 - Setting basic information in JSON format') + verify_setting_basic_in_format(shell, leshan, endpoint, 'JSON') + +def verify_LightweightM2M_1_1_int_221(shell: Shell, leshan: Leshan, endpoint: str): + logger.info('LightweightM2M-1.1-int-221 - Attempt to perform operations on Security') + assert leshan.read(endpoint, '0/0')['status'] == 'UNAUTHORIZED(401)' + assert leshan.write(endpoint, '0/0/0', 'coap://localhost')['status'] == 'UNAUTHORIZED(401)' + assert leshan.put_raw(f'/clients/{endpoint}/0/attributes?pmin=10')['status'] == 'UNAUTHORIZED(401)' + +def verify_LightweightM2M_1_1_int_401(shell: Shell, leshan: Leshan, endpoint: str): + logger.info("LightweightM2M-1.1-int-401 - UDP Channel Security - Pre-shared Key Mode") + lines = shell.get_filtered_output(shell.exec_command('lwm2m read 0/0/0 -s')) + host = lines[0] + assert 'coaps://' in host + lines = shell.get_filtered_output(shell.exec_command('lwm2m read 0/0/2 -u8')) + mode = int(lines[0]) + assert mode == 0 + resp = leshan.get(f'/clients/{endpoint}') + assert resp["secure"] + +def test_lwm2m_bootstrap_psk(shell: Shell, leshan, leshan_bootstrap): + try: + # Generate randon device id and password (PSK key) + endpoint = 'client_' + binascii.b2a_hex(os.urandom(1)).decode() + passwd = ''.join(random.choice(string.ascii_lowercase) for i in range(16)) + + + # Create device entries in Leshan and Bootstrap server + leshan_bootstrap.create_bs_device(endpoint, f'coaps://{LESHAN_IP}:{COAPS_PORT}', passwd) + leshan.create_psk_device(endpoint, passwd) + + # Allow engine to start & stop once. + time.sleep(2) + + # + # Verify PSK security using Bootstrap + # + + # Write bootsrap server information and PSK keys + shell.exec_command(f'lwm2m write 0/0/0 -s coaps://{LESHAN_IP}:{BOOTSTRAP_COAPS_PORT}') + shell.exec_command('lwm2m write 0/0/1 -b 1') + shell.exec_command('lwm2m write 0/0/2 -u8 0') + shell.exec_command(f'lwm2m write 0/0/3 -s {endpoint}') + shell.exec_command(f'lwm2m write 0/0/5 -s {passwd}') + shell.exec_command(f'lwm2m start {endpoint} -b 1') + + + # + # Bootstrap Interface test cases + # LightweightM2M-1.1-int-0 (included) + # LightweightM2M-1.1-int-401 (included) + verify_LightweightM2M_1_1_int_1(shell, leshan, endpoint) + + # + # Registration Interface test cases (using PSK security) + # + verify_LightweightM2M_1_1_int_102(shell, leshan, endpoint) + # skip, not implemented verify_LightweightM2M_1_1_int_103() + verify_LightweightM2M_1_1_int_104(shell, leshan, endpoint) + # skip, included in 109: verify_LightweightM2M_1_1_int_107(shell, leshan, endpoint) + verify_LightweightM2M_1_1_int_108(leshan, endpoint) + verify_LightweightM2M_1_1_int_109(shell, leshan, endpoint) + + # + # Device management & Service Enablement Interface test cases + # + verify_LightweightM2M_1_1_int_201(shell, leshan, endpoint) + verify_LightweightM2M_1_1_int_203(shell, leshan, endpoint) + verify_LightweightM2M_1_1_int_204(shell, leshan, endpoint) + verify_LightweightM2M_1_1_int_205(shell, leshan, endpoint) + verify_LightweightM2M_1_1_int_211(shell, leshan, endpoint) + verify_LightweightM2M_1_1_int_212(shell, leshan, endpoint) + verify_LightweightM2M_1_1_int_215(shell, leshan, endpoint) + + shell.exec_command('lwm2m stop') + shell._device.readlines_until(regex=r'.*Deregistration success', timeout=10.0) + + finally: + # Remove device and bootstrap information + # Leshan does not accept non-secure connection if device information is provided with PSK + leshan.delete_device(endpoint) + leshan_bootstrap.delete_bs_device(endpoint) + + +def test_lwm2m_nosecure(shell: Shell, leshan, helperclient): + + # Allow engine to start & stop once. + time.sleep(2) + + # Generate randon device id and password (PSK key) + endpoint = 'client_' + binascii.b2a_hex(os.urandom(1)).decode() + + # + # Registration Interface test cases (using Non-secure mode) + # + shell.exec_command(f'lwm2m write 0/0/0 -s coap://{LESHAN_IP}:{COAP_PORT}') + shell.exec_command('lwm2m write 0/0/1 -b 0') + shell.exec_command('lwm2m write 0/0/2 -u8 3') + shell.exec_command(f'lwm2m write 0/0/3 -s {endpoint}') + shell.exec_command('lwm2m create 1/0') + shell.exec_command('lwm2m write 0/0/10 -u16 1') + shell.exec_command('lwm2m write 1/0/0 -u16 1') + shell.exec_command('lwm2m write 1/0/1 -u32 86400') + shell.exec_command(f'lwm2m start {endpoint} -b 0') + shell._device.readlines_until(regex=f"RD Client started with endpoint '{endpoint}'", timeout=10.0) + + verify_LightweightM2M_1_1_int_101(shell, leshan, endpoint) + verify_LightweightM2M_1_1_int_105(shell, leshan, endpoint, helperclient) # needs no-security + verify_LightweightM2M_1_1_int_215(shell, leshan, endpoint) + verify_LightweightM2M_1_1_int_220(shell, leshan, endpoint) + verify_LightweightM2M_1_1_int_221(shell, leshan, endpoint) + + # All done + shell.exec_command('lwm2m stop') + shell._device.readlines_until(regex=r'.*Deregistration success', timeout=10.0) diff --git a/tests/net/lib/lwm2m/interop/requirements.txt b/tests/net/lib/lwm2m/interop/requirements.txt new file mode 100644 index 00000000000..38c501218ee --- /dev/null +++ b/tests/net/lib/lwm2m/interop/requirements.txt @@ -0,0 +1 @@ +CoAPthon3 diff --git a/tests/net/lib/lwm2m/interop/src/lwm2m-client.c b/tests/net/lib/lwm2m/interop/src/lwm2m-client.c new file mode 100644 index 00000000000..983fea79f3e --- /dev/null +++ b/tests/net/lib/lwm2m/interop/src/lwm2m-client.c @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2017 Linaro Limited + * Copyright (c) 2017-2019 Foundries.io + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define LOG_MODULE_NAME net_lwm2m_client_app +#define LOG_LEVEL LOG_LEVEL_DBG + +#include +LOG_MODULE_REGISTER(LOG_MODULE_NAME); +#include +#include +#include +#include +#include +#include + +#define APP_BANNER "Run LWM2M client" + +#define WAIT_TIME K_SECONDS(10) +#define CONNECT_TIME K_SECONDS(10) + +#define NAME "Zephyr" +#define MODEL "client-1" +#define SERIAL "serial-1" +#define VERSION "1.2.3" + +static struct lwm2m_ctx client; + +static int device_reboot_cb(uint16_t obj_inst_id, + uint8_t *args, uint16_t args_len) +{ + LOG_INF("DEVICE: REBOOT"); + return 0; +} + + +static int lwm2m_setup(void) +{ + /* setup DEVICE object */ + + lwm2m_set_res_buf(&LWM2M_OBJ(3, 0, 0), NAME, sizeof(NAME), + sizeof(NAME), LWM2M_RES_DATA_FLAG_RO); + lwm2m_set_res_buf(&LWM2M_OBJ(3, 0, 1), MODEL, sizeof(MODEL), + sizeof(MODEL), LWM2M_RES_DATA_FLAG_RO); + lwm2m_set_res_buf(&LWM2M_OBJ(3, 0, 2), SERIAL, sizeof(SERIAL), + sizeof(SERIAL), LWM2M_RES_DATA_FLAG_RO); + lwm2m_set_res_buf(&LWM2M_OBJ(3, 0, 3), VERSION, sizeof(VERSION), + sizeof(VERSION), LWM2M_RES_DATA_FLAG_RO); + lwm2m_register_exec_callback(&LWM2M_OBJ(3, 0, 4), device_reboot_cb); + lwm2m_set_res_buf(&LWM2M_OBJ(3, 0, 17), CONFIG_BOARD, sizeof(CONFIG_BOARD), + sizeof(CONFIG_BOARD), LWM2M_RES_DATA_FLAG_RO); + + return 0; +} + +static void rd_client_event(struct lwm2m_ctx *client, + enum lwm2m_rd_client_event client_event) +{ + switch (client_event) { + + case LWM2M_RD_CLIENT_EVENT_NONE: + /* do nothing */ + break; + + case LWM2M_RD_CLIENT_EVENT_BOOTSTRAP_REG_FAILURE: + LOG_DBG("Bootstrap registration failure!"); + break; + + case LWM2M_RD_CLIENT_EVENT_BOOTSTRAP_REG_COMPLETE: + LOG_DBG("Bootstrap registration complete"); + break; + + case LWM2M_RD_CLIENT_EVENT_BOOTSTRAP_TRANSFER_COMPLETE: + LOG_DBG("Bootstrap transfer complete"); + break; + + case LWM2M_RD_CLIENT_EVENT_REGISTRATION_FAILURE: + LOG_DBG("Registration failure!"); + break; + + case LWM2M_RD_CLIENT_EVENT_REGISTRATION_COMPLETE: + LOG_DBG("Registration complete"); + break; + + case LWM2M_RD_CLIENT_EVENT_REG_TIMEOUT: + LOG_DBG("Registration timeout!"); + break; + + case LWM2M_RD_CLIENT_EVENT_REG_UPDATE_COMPLETE: + LOG_DBG("Registration update complete"); + break; + + case LWM2M_RD_CLIENT_EVENT_DEREGISTER_FAILURE: + LOG_DBG("Deregister failure!"); + break; + + case LWM2M_RD_CLIENT_EVENT_DISCONNECT: + LOG_DBG("Disconnected"); + break; + + case LWM2M_RD_CLIENT_EVENT_QUEUE_MODE_RX_OFF: + LOG_DBG("Queue mode RX window closed"); + break; + + case LWM2M_RD_CLIENT_EVENT_ENGINE_SUSPENDED: + LOG_DBG("LwM2M engine suspended"); + break; + + case LWM2M_RD_CLIENT_EVENT_NETWORK_ERROR: + LOG_ERR("LwM2M engine reported a network error."); + lwm2m_rd_client_stop(client, rd_client_event, true); + break; + + case LWM2M_RD_CLIENT_EVENT_REG_UPDATE: + LOG_DBG("Registration update"); + break; + case LWM2M_RD_CLIENT_EVENT_DEREGISTER: + LOG_DBG("Deregistration client"); + break; + } +} + +static void observe_cb(enum lwm2m_observe_event event, + struct lwm2m_obj_path *path, void *user_data) +{ + char buf[LWM2M_MAX_PATH_STR_SIZE]; + + switch (event) { + + case LWM2M_OBSERVE_EVENT_OBSERVER_ADDED: + LOG_INF("Observer added for %s", lwm2m_path_log_buf(buf, path)); + break; + + case LWM2M_OBSERVE_EVENT_OBSERVER_REMOVED: + LOG_INF("Observer removed for %s", lwm2m_path_log_buf(buf, path)); + break; + + case LWM2M_OBSERVE_EVENT_NOTIFY_ACK: + LOG_INF("Notify acknowledged for %s", lwm2m_path_log_buf(buf, path)); + break; + + case LWM2M_OBSERVE_EVENT_NOTIFY_TIMEOUT: + LOG_INF("Notify timeout for %s, trying registration update", + lwm2m_path_log_buf(buf, path)); + + lwm2m_rd_client_update(); + break; + } +} + +int main(void) +{ + int ret; + +#if defined(CONFIG_BOARD_NATIVE_POSIX) + srandom(time(NULL)); +#endif + + ret = lwm2m_setup(); + if (ret < 0) { + LOG_ERR("Cannot setup LWM2M fields (%d)", ret); + return 0; + } + + client.tls_tag = 1; + + lwm2m_rd_client_start(&client, CONFIG_BOARD, 0, rd_client_event, observe_cb); + lwm2m_rd_client_stop(&client, rd_client_event, false); + + return 0; +} diff --git a/tests/net/lib/lwm2m/interop/testcase.yaml b/tests/net/lib/lwm2m/interop/testcase.yaml new file mode 100644 index 00000000000..aeba64748df --- /dev/null +++ b/tests/net/lib/lwm2m/interop/testcase.yaml @@ -0,0 +1,14 @@ +tests: + net.lwm2m.interop: + harness: pytest + timeout: 300 + slow: true + integration_platforms: + - native_posix + platform_allow: + - native_posix + - qemu_cortex_m3 + tags: + - testing + - pytest + - shell diff --git a/tests/net/lib/lwm2m/lwm2m_engine/src/stubs.c b/tests/net/lib/lwm2m/lwm2m_engine/src/stubs.c index 93f5cce1012..5db155f1e29 100644 --- a/tests/net/lib/lwm2m/lwm2m_engine/src/stubs.c +++ b/tests/net/lib/lwm2m/lwm2m_engine/src/stubs.c @@ -24,8 +24,8 @@ DEFINE_FAKE_VALUE_FUNC(int, generate_notify_message, struct lwm2m_ctx *, struct DEFINE_FAKE_VALUE_FUNC(int64_t, engine_observe_shedule_next_event, struct observe_node *, uint16_t, const int64_t); DEFINE_FAKE_VALUE_FUNC(int, handle_request, struct coap_packet *, struct lwm2m_message *); -DEFINE_FAKE_VOID_FUNC(lwm2m_udp_receive, struct lwm2m_ctx *, uint8_t *, uint16_t, struct sockaddr *, - udp_request_handler_cb_t); +DEFINE_FAKE_VOID_FUNC(lwm2m_udp_receive, struct lwm2m_ctx *, uint8_t *, uint16_t, + struct sockaddr *); DEFINE_FAKE_VALUE_FUNC(bool, lwm2m_rd_client_is_registred, struct lwm2m_ctx *); DEFINE_FAKE_VOID_FUNC(lwm2m_engine_context_close, struct lwm2m_ctx *); DEFINE_FAKE_VALUE_FUNC(int, lwm2m_get_res_buf, const struct lwm2m_obj_path *, void **, uint16_t *, diff --git a/tests/net/lib/lwm2m/lwm2m_engine/src/stubs.h b/tests/net/lib/lwm2m/lwm2m_engine/src/stubs.h index 088d72d4623..7b8ca481f85 100644 --- a/tests/net/lib/lwm2m/lwm2m_engine/src/stubs.h +++ b/tests/net/lib/lwm2m/lwm2m_engine/src/stubs.h @@ -39,7 +39,7 @@ DECLARE_FAKE_VALUE_FUNC(int64_t, engine_observe_shedule_next_event, struct obser const int64_t); DECLARE_FAKE_VALUE_FUNC(int, handle_request, struct coap_packet *, struct lwm2m_message *); DECLARE_FAKE_VOID_FUNC(lwm2m_udp_receive, struct lwm2m_ctx *, uint8_t *, uint16_t, - struct sockaddr *, udp_request_handler_cb_t); + struct sockaddr *); DECLARE_FAKE_VALUE_FUNC(bool, lwm2m_rd_client_is_registred, struct lwm2m_ctx *); DECLARE_FAKE_VOID_FUNC(lwm2m_engine_context_close, struct lwm2m_ctx *); DECLARE_FAKE_VALUE_FUNC(int, lwm2m_get_res_buf, const struct lwm2m_obj_path *, void **, uint16_t *, diff --git a/tests/net/lib/lwm2m/lwm2m_rd_client/CMakeLists.txt b/tests/net/lib/lwm2m/lwm2m_rd_client/CMakeLists.txt index 5f29bdad0dd..2a8e8959c25 100644 --- a/tests/net/lib/lwm2m/lwm2m_rd_client/CMakeLists.txt +++ b/tests/net/lib/lwm2m/lwm2m_rd_client/CMakeLists.txt @@ -23,7 +23,7 @@ add_compile_definitions(CONFIG_LWM2M_RD_CLIENT_ENDPOINT_NAME_MAX_LENGTH=32) add_compile_definitions(CONFIG_LWM2M_RD_CLIENT_MAX_RETRIES=2) add_compile_definitions(CONFIG_LWM2M_COAP_BLOCK_SIZE=256) add_compile_definitions(CONFIG_LWM2M_COAP_MAX_MSG_SIZE=512) -add_compile_definitions(CONFIG_LWM2M_ENGINE_DEFAULT_LIFETIME=60) +add_compile_definitions(CONFIG_LWM2M_ENGINE_DEFAULT_LIFETIME=20) add_compile_definitions(CONFIG_LWM2M_SECURITY_INSTANCE_COUNT=1) add_compile_definitions(CONFIG_LWM2M_SECONDS_TO_UPDATE_EARLY=10) add_compile_definitions(CONFIG_LWM2M_QUEUE_MODE_UPTIME=10) @@ -32,3 +32,4 @@ add_compile_definitions(CONFIG_LWM2M_QUEUE_MODE_ENABLED=1) add_compile_definitions(CONFIG_LWM2M_TLS_SESSION_CACHING=1) add_compile_definitions(CONFIG_LWM2M_RD_CLIENT_LISTEN_AT_IDLE=1) add_compile_definitions(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP=1) +add_compile_definitions(CONFIG_LWM2M_UPDATE_PERIOD=0) diff --git a/tests/net/lib/lwm2m/lwm2m_rd_client/src/main.c b/tests/net/lib/lwm2m/lwm2m_rd_client/src/main.c index 08c0820d5e9..03d0dce7f49 100644 --- a/tests/net/lib/lwm2m/lwm2m_rd_client/src/main.c +++ b/tests/net/lib/lwm2m/lwm2m_rd_client/src/main.c @@ -106,6 +106,9 @@ static void lwm2m_event_cb(struct lwm2m_ctx *client, enum lwm2m_rd_client_event case LWM2M_RD_CLIENT_EVENT_REG_UPDATE: LOG_INF("*** LWM2M_RD_CLIENT_EVENT_REG_UPDATE"); break; + case LWM2M_RD_CLIENT_EVENT_DEREGISTER: + LOG_INF("*** LWM2M_RD_CLIENT_EVENT_DEREGISTER"); + break; } show_lwm2m_event(client_event); @@ -211,6 +214,7 @@ ZTEST(lwm2m_rd_client, test_start_registration_ok) coap_header_get_code_fake.custom_fake = coap_header_get_code_fake_deleted; zassert_true(lwm2m_rd_client_stop(&ctx, lwm2m_event_cb, true) == 0, NULL); + zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_DEREGISTER), NULL); zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_DISCONNECT), NULL); zassert_true(!lwm2m_rd_client_is_registred(&ctx), NULL); } @@ -425,6 +429,7 @@ ZTEST(lwm2m_rd_client, test_deregistration_timeout) test_prepare_pending_message_cb(&message_reply_timeout_cb_default); zassert_true(lwm2m_rd_client_stop(&ctx, lwm2m_event_cb, true) == 0, NULL); + zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_DEREGISTER), NULL); zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_DEREGISTER_FAILURE)); } @@ -529,6 +534,7 @@ ZTEST(lwm2m_rd_client, test_suspend_stop_resume) zassert_equal(lwm2m_rd_client_stop(&ctx, lwm2m_event_cb, false), 0); zassert_true(lwm2m_rd_client_resume() == 0, NULL); + zassert_false(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_DEREGISTER), NULL); zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_DISCONNECT), NULL); } diff --git a/tests/net/lib/lwm2m/lwm2m_rd_client/src/stubs.c b/tests/net/lib/lwm2m/lwm2m_rd_client/src/stubs.c index 58f64f4984d..41789e20340 100644 --- a/tests/net/lib/lwm2m/lwm2m_rd_client/src/stubs.c +++ b/tests/net/lib/lwm2m/lwm2m_rd_client/src/stubs.c @@ -117,10 +117,12 @@ uint16_t counter = RD_CLIENT_MAX_SERVICE_ITERATIONS; struct lwm2m_message *pending_message; void *(*pending_message_cb)(); static bool running; +K_SEM_DEFINE(srv_sem, 0, 1); static void service_work_fn(struct k_work *work) { while (running) { + k_sleep(K_MSEC(10)); if (pending_message != NULL && pending_message_cb != NULL) { pending_message_cb(pending_message); pending_message = NULL; @@ -129,8 +131,9 @@ static void service_work_fn(struct k_work *work) if (next && next < k_uptime_get()) { next = 0; service(NULL); + k_sem_give(&srv_sem); } - k_sleep(K_MSEC(10)); + counter--; /* avoid endless loop if rd client is stuck somewhere */ @@ -143,10 +146,8 @@ static void service_work_fn(struct k_work *work) void wait_for_service(uint16_t cycles) { - uint16_t end = counter - cycles; - - while (counter > end) { - k_sleep(K_MSEC(10)); + while (cycles--) { + k_sem_take(&srv_sem, K_MSEC(100)); } } @@ -157,6 +158,7 @@ void test_lwm2m_engine_start_service(void) running = true; counter = RD_CLIENT_MAX_SERVICE_ITERATIONS; k_work_submit(&service_work); + k_sem_reset(&srv_sem); } void test_lwm2m_engine_stop_service(void) diff --git a/tests/net/socket/socketpair/testcase.yaml b/tests/net/socket/socketpair/testcase.yaml index e771e7c61d4..d2b4462ce2d 100644 --- a/tests/net/socket/socketpair/testcase.yaml +++ b/tests/net/socket/socketpair/testcase.yaml @@ -18,3 +18,11 @@ tests: extra_configs: - CONFIG_PICOLIBC=y platform_exclude: vmu_rt1170 mimxrt1160_evk_cm7 # See #61246 + net.socket.socketpair.high_mem: + min_ram: 64 + extra_configs: + # Low buffer sizes (e.g., 8192) will verify the crash fix, but tests will still + # fail due to insufficient memory. So, use high buffer sizes. + - CONFIG_NET_SOCKETPAIR_BUFFER_SIZE=4096 + - CONFIG_HEAP_MEM_POOL_SIZE=32768 + platform_exclude: vmu_rt1170 mimxrt1160_evk_cm7 # See #61246 diff --git a/tests/subsys/mgmt/mcumgr/mcumgr_client/CMakeLists.txt b/tests/subsys/mgmt/mcumgr/mcumgr_client/CMakeLists.txt index a0f76554629..20a96480514 100644 --- a/tests/subsys/mgmt/mcumgr/mcumgr_client/CMakeLists.txt +++ b/tests/subsys/mgmt/mcumgr/mcumgr_client/CMakeLists.txt @@ -14,4 +14,4 @@ FILE(GLOB app_sources target_sources(app PRIVATE ${app_sources}) -add_compile_definitions(CONFIG_MCUMGR_GRP_IMG_UPDATABLE_IMAGE_NUMBER=1) +zephyr_library_link_libraries(MCUBOOT_BOOTUTIL) diff --git a/tests/subsys/mgmt/mcumgr/mcumgr_client/prj.conf b/tests/subsys/mgmt/mcumgr/mcumgr_client/prj.conf index 46835a49dc7..461a0ec9477 100644 --- a/tests/subsys/mgmt/mcumgr/mcumgr_client/prj.conf +++ b/tests/subsys/mgmt/mcumgr/mcumgr_client/prj.conf @@ -12,14 +12,17 @@ CONFIG_NET_BUF=y CONFIG_BASE64=y CONFIG_ZCBOR=y CONFIG_CRC=y -# Enable mcumgr +# Enable MCUboot util library +CONFIG_MCUBOOT_BOOTUTIL_LIB=y +# Enable mcumgr client CONFIG_MCUMGR=y CONFIG_SMP_CLIENT=y -CONFIG_MCUMGR_GRP_IMG_UPDATABLE_IMAGE_NUMBER=1 CONFIG_MCUMGR_GRP_OS_CLIENT=y CONFIG_MCUMGR_GRP_IMG_CLIENT=y CONFIG_MCUMGR_GRP_OS_CLIENT_ECHO=y CONFIG_MCUMGR_GRP_OS_CLIENT_RESET=y +# disable default image group build +CONFIG_IMG_MANAGER=n # Extend System Workqueue stack size CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2304 diff --git a/tests/subsys/mgmt/mcumgr/mcumgr_client/src/img_gr_stub.c b/tests/subsys/mgmt/mcumgr/mcumgr_client/src/img_gr_stub.c index 5bd784a3954..aea03935fe6 100644 --- a/tests/subsys/mgmt/mcumgr/mcumgr_client/src/img_gr_stub.c +++ b/tests/subsys/mgmt/mcumgr/mcumgr_client/src/img_gr_stub.c @@ -24,6 +24,12 @@ static struct mcumgr_image_data image_dummy_info[2]; static size_t test_offset; static uint8_t *image_hash_ptr; +#ifdef CONFIG_MCUMGR_GRP_IMG_UPDATABLE_IMAGE_NUMBER +#define IMG_UPDATABLE_IMAGE_COUNT CONFIG_MCUMGR_GRP_IMG_UPDATABLE_IMAGE_NUMBER +#else +#define IMG_UPDATABLE_IMAGE_COUNT 1 +#endif + #define ZCBOR_ENCODE_FLAG(zse, label, value) \ (zcbor_tstr_put_lit(zse, label) && zcbor_bool_put(zse, value)) @@ -117,7 +123,7 @@ void img_read_response(int count) zcbor_tstr_put_term(zse, image_dummy_info[i].version) && zcbor_tstr_put_term(zse, "hash") && - zcbor_bstr_encode_ptr(zse, image_dummy_info[i].hash, IMG_MGMT_HASH_LEN) && + zcbor_bstr_encode_ptr(zse, image_dummy_info[i].hash, IMG_MGMT_DATA_SHA_LEN) && ZCBOR_ENCODE_FLAG(zse, "bootable", image_dummy_info[i].flags.bootable) && ZCBOR_ENCODE_FLAG(zse, "pending", image_dummy_info[i].flags.pending) && ZCBOR_ENCODE_FLAG(zse, "confirmed", image_dummy_info[i].flags.confirmed) && @@ -126,7 +132,7 @@ void img_read_response(int count) zcbor_map_end_encode(zse, 15); } - ok = ok && zcbor_list_end_encode(zse, 2 * CONFIG_MCUMGR_GRP_IMG_UPDATABLE_IMAGE_NUMBER); + ok = ok && zcbor_list_end_encode(zse, 2 * IMG_UPDATABLE_IMAGE_COUNT); ok = ok && zcbor_map_end_encode(zse, 15); diff --git a/tests/subsys/mgmt/mcumgr/mcumgr_client/src/main.c b/tests/subsys/mgmt/mcumgr/mcumgr_client/src/main.c index a15e82f17a6..bf70f178fe7 100644 --- a/tests/subsys/mgmt/mcumgr/mcumgr_client/src/main.c +++ b/tests/subsys/mgmt/mcumgr/mcumgr_client/src/main.c @@ -27,7 +27,7 @@ static struct smp_client_object smp_client; static struct img_mgmt_client img_client; static struct os_mgmt_client os_client; -ZTEST(mcumgr_client, img_upload) +ZTEST(mcumgr_client, test_img_upload) { int rc; struct mcumgr_image_upload response; @@ -108,7 +108,7 @@ ZTEST(mcumgr_client, img_upload) response.image_upload_offset); } -ZTEST(mcumgr_client, img_erase) +ZTEST(mcumgr_client, test_img_erase) { int rc; @@ -130,7 +130,7 @@ ZTEST(mcumgr_client, img_erase) zassert_equal(MGMT_ERR_EOK, rc, "Expected to receive %d response %d", MGMT_ERR_EOK, rc); } -ZTEST(mcumgr_client, image_state_read) +ZTEST(mcumgr_client, test_image_state_read) { int rc; struct mcumgr_image_state res_buf; @@ -153,7 +153,7 @@ ZTEST(mcumgr_client, image_state_read) res_buf.image_list_length); } -ZTEST(mcumgr_client, image_state_set) +ZTEST(mcumgr_client, test_image_state_set) { int rc; char hash[32]; @@ -199,7 +199,7 @@ ZTEST(mcumgr_client, image_state_set) true, image_info[0].flags.confirmed); } -ZTEST(mcumgr_client, os_reset) +ZTEST(mcumgr_client, test_os_reset) { int rc; @@ -217,7 +217,7 @@ ZTEST(mcumgr_client, os_reset) zassert_equal(MGMT_ERR_EOK, rc, "Expected to receive %d response %d", MGMT_ERR_EOK, rc); } -ZTEST(mcumgr_client, os_echo) +ZTEST(mcumgr_client, test_os_echo) { int rc; diff --git a/tests/subsys/mgmt/mcumgr/mcumgr_client/testcase.yaml b/tests/subsys/mgmt/mcumgr/mcumgr_client/testcase.yaml new file mode 100644 index 00000000000..99b9fac34ad --- /dev/null +++ b/tests/subsys/mgmt/mcumgr/mcumgr_client/testcase.yaml @@ -0,0 +1,11 @@ +# +# Copyright (c) 2023 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# +tests: + mgmt.mcumgr.mcumgr.client: + platform_allow: native_posix + tags: + - mcumgr + - mcumgr_client diff --git a/tests/subsys/mgmt/mcumgr/smp_client/src/main.c b/tests/subsys/mgmt/mcumgr/smp_client/src/main.c index be1a3968274..7b6ad8eafa8 100644 --- a/tests/subsys/mgmt/mcumgr/smp_client/src/main.c +++ b/tests/subsys/mgmt/mcumgr/smp_client/src/main.c @@ -32,7 +32,7 @@ int smp_client_res_cb(struct net_buf *nb, void *user_data) return 0; } -ZTEST(smp_client, buf_alloc) +ZTEST(smp_client, test_buf_alloc) { struct smp_client_object smp_client; @@ -56,7 +56,7 @@ ZTEST(smp_client, buf_alloc) } } -ZTEST(smp_client, msg_send_timeout) +ZTEST(smp_client, test_msg_send_timeout) { struct net_buf *nb; @@ -72,7 +72,7 @@ ZTEST(smp_client, msg_send_timeout) zassert_equal_ptr(response_ptr, &testing_user_data, "User data not returned correctly"); } -ZTEST(smp_client, msg_response_handler) +ZTEST(smp_client, test_msg_response_handler) { struct smp_hdr dst_hdr; int rc; diff --git a/tests/subsys/mgmt/mcumgr/smp_client/testcase.yaml b/tests/subsys/mgmt/mcumgr/smp_client/testcase.yaml new file mode 100644 index 00000000000..49971116251 --- /dev/null +++ b/tests/subsys/mgmt/mcumgr/smp_client/testcase.yaml @@ -0,0 +1,11 @@ +# +# Copyright (c) 2023 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# +tests: + mgmt.mcumgr.smp.client: + platform_allow: native_posix + tags: + - mcumgr + - smp_client diff --git a/tests/subsys/openthread/radio_test.c b/tests/subsys/openthread/radio_test.c index fb6c8755039..87a7013a0d5 100644 --- a/tests/subsys/openthread/radio_test.c +++ b/tests/subsys/openthread/radio_test.c @@ -30,8 +30,6 @@ DEFINE_FFF_GLOBALS; #define FRAME_TYPE_MASK 0x07 #define FRAME_TYPE_ACK 0x02 -#define PHY_SHR_DURATION 160 - K_SEM_DEFINE(ot_sem, 0, 1); /** @@ -295,10 +293,10 @@ ZTEST(openthread_radio, test_tx_test) get_time_mock_fake.return_val = (int64_t)UINT32_MAX * NSEC_PER_USEC + 1000; frm->mInfo.mTxInfo.mTxDelayBaseTime = 3U; frm->mInfo.mTxInfo.mTxDelay = 5U; - expected_target_time = get_time_mock_fake.return_val + - (frm->mInfo.mTxInfo.mTxDelayBaseTime + - frm->mInfo.mTxInfo.mTxDelay + PHY_SHR_DURATION) * - NSEC_PER_USEC; + expected_target_time = + get_time_mock_fake.return_val + + (frm->mInfo.mTxInfo.mTxDelayBaseTime + frm->mInfo.mTxInfo.mTxDelay) * + NSEC_PER_USEC; } /* ACKed frame */ diff --git a/west.yml b/west.yml index 727d6d807ab..988343a9d10 100644 --- a/west.yml +++ b/west.yml @@ -183,7 +183,7 @@ manifest: groups: - hal - name: hal_nordic - revision: 9ae7c765985ebdea3d9b98c0d3b154794f0b47cf + revision: b9633ecea67bf52925d4c61455046223b46402b1 path: modules/hal/nordic groups: - hal @@ -303,7 +303,7 @@ manifest: revision: 42b7c577714b8f22ce82a901e19c1814af4609a8 path: modules/lib/open-amp - name: openthread - revision: f7690fe7e9d638341921808cba6a3e695ec0131e + revision: 4ed44bc7d58d9a98c6cca13a50d38129045ab3df path: modules/lib/openthread - name: picolibc path: modules/lib/picolibc @@ -331,7 +331,7 @@ manifest: groups: - debug - name: trusted-firmware-m - revision: 8b6146261fe2c0ad61154e20c7e338601eae2208 + revision: 33c0f47bcb19721a5c33e6fe1eee9225d00bb5bc path: modules/tee/tf-m/trusted-firmware-m groups: - tee