added xiao nrf54l15 support for otbr
This commit is contained in:
11
.gitignore
vendored
Normal file
11
.gitignore
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Zephyr/west build output
|
||||||
|
build/
|
||||||
|
build-*/
|
||||||
|
|
||||||
|
# west workspace metadata, if this repo is ever used as a manifest repo
|
||||||
|
.west/
|
||||||
|
|
||||||
|
# Editor / OS droppings
|
||||||
|
._*
|
||||||
|
.DS_Store
|
||||||
|
*.swp
|
||||||
18
CMakeLists.txt
Normal file
18
CMakeLists.txt
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2020 Nordic Semiconductor ASA
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
|
||||||
|
#
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.20.0)
|
||||||
|
|
||||||
|
set(OT_NCP_VENDOR_HOOK_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
||||||
|
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||||
|
|
||||||
|
project(openthread_coprocessor)
|
||||||
|
|
||||||
|
# NORDIC SDK APP START
|
||||||
|
target_sources(app PRIVATE src/main.c src/nrf_802154_radio_wrapper.c)
|
||||||
|
target_sources_ifdef(CONFIG_RCP_SAMPLE_HCI app PRIVATE src/rcp_hci.c)
|
||||||
|
# NORDIC SDK APP END
|
||||||
22
Kconfig
Normal file
22
Kconfig
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2023 Nordic Semiconductor ASA
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
|
||||||
|
#
|
||||||
|
|
||||||
|
# Set product string for USB device
|
||||||
|
config CDC_ACM_SERIAL_PRODUCT_STRING
|
||||||
|
default "Thread Co-Processor"
|
||||||
|
depends on USB_DEVICE_STACK_NEXT
|
||||||
|
|
||||||
|
menu "Zephyr Kernel"
|
||||||
|
source "Kconfig.zephyr"
|
||||||
|
endmenu
|
||||||
|
|
||||||
|
module = OT_COPROCESSOR
|
||||||
|
module-str = ot_coprocessor
|
||||||
|
source "$(ZEPHYR_BASE)/subsys/logging/Kconfig.template.log_config"
|
||||||
|
|
||||||
|
config RCP_SAMPLE_HCI
|
||||||
|
bool "Bluetooth HCI USB support for the coprocessor sample"
|
||||||
|
select EXPERIMENTAL
|
||||||
10
Kconfig.sysbuild
Normal file
10
Kconfig.sysbuild
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2026 Nordic Semiconductor ASA
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
|
||||||
|
#
|
||||||
|
|
||||||
|
config PARTITION_MANAGER
|
||||||
|
default n
|
||||||
|
|
||||||
|
source "share/sysbuild/Kconfig"
|
||||||
202
README.rst
Normal file
202
README.rst
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
.. _ot_coprocessor_sample:
|
||||||
|
|
||||||
|
Thread: Co-processor
|
||||||
|
####################
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
:local:
|
||||||
|
:depth: 2
|
||||||
|
|
||||||
|
The :ref:`Thread <ug_thread>` Co-processor sample demonstrates how to implement OpenThread's :ref:`thread_architectures_designs_cp` inside the Zephyr environment.
|
||||||
|
The sample uses the :ref:`thread_architectures_designs_cp_rcp` architecture.
|
||||||
|
|
||||||
|
The sample is based on Zephyr's :zephyr:code-sample:`openthread-coprocessor` sample.
|
||||||
|
However, it customizes Zephyr's sample to fulfill the |NCS| requirements (for example, by increasing the stack size dedicated for the user application), and also extends it with features such as:
|
||||||
|
|
||||||
|
* Increased Mbed TLS heap size.
|
||||||
|
* Lowered main stack size to increase user application space.
|
||||||
|
* No obsolete configuration options.
|
||||||
|
* Vendor hooks for co-processor architecture allowing users to extend handled properties by their own, customized functionalities.
|
||||||
|
* Thread 1.4 features, including support for Thread 1.3 and Thread 1.2.
|
||||||
|
|
||||||
|
This sample supports optional :ref:`logging extension <ot_coprocessor_sample_logging>`, which can be turned on or off independently.
|
||||||
|
To enable logging extension, use the ``ot-debug`` snippet.
|
||||||
|
|
||||||
|
.. _ot_coprocessor_sample_requirements:
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
************
|
||||||
|
|
||||||
|
The sample supports the following development kits for testing the network status:
|
||||||
|
|
||||||
|
.. table-from-sample-yaml::
|
||||||
|
|
||||||
|
To test the sample, you need at least one development kit.
|
||||||
|
You can use additional development kits programmed with the Co-processor sample for testing network joining.
|
||||||
|
|
||||||
|
Moreover, the sample requires a Userspace higher layer process running on your device to communicate with the MCU co-processor part.
|
||||||
|
This sample uses ``ot-cli`` as reference.
|
||||||
|
|
||||||
|
|
||||||
|
Overview
|
||||||
|
********
|
||||||
|
|
||||||
|
The sample demonstrates using a co-processor target on the MCU to communicate with `ot-cli` on Unix-like operating system.
|
||||||
|
According to the co-processor architecture, the MCU part must cooperate with user higher layer process to establish the complete full stack application.
|
||||||
|
The sample shows how to set up the connection between the co-processor and the host.
|
||||||
|
|
||||||
|
By default, this sample comes with the :ref:`RCP set of OpenThread functionalities <thread_ug_feature_sets>` enabled (:kconfig:option:`CONFIG_OPENTHREAD_NORDIC_LIBRARY_RCP`).
|
||||||
|
|
||||||
|
Application architecture options
|
||||||
|
================================
|
||||||
|
|
||||||
|
.. include:: ../cli/README.rst
|
||||||
|
:start-after: ot_cli_sample_architecture_options_start
|
||||||
|
:end-before: ot_cli_sample_architecture_options_end
|
||||||
|
|
||||||
|
.. _ot_coprocessor_sample_logging:
|
||||||
|
|
||||||
|
Logging extension
|
||||||
|
=================
|
||||||
|
|
||||||
|
By default, this sample uses :ref:`Spinel logging backend <ug_logging_backends_spinel>` for sending log messages to the host device using the Spinel protocol.
|
||||||
|
This is a useful feature, because it does not require separate interfaces to communicate with the co-processor through the Spinel protocol and collect log messages.
|
||||||
|
Moreover, using the Spinel logging backend (by setting :kconfig:option:`CONFIG_LOG_BACKEND_SPINEL`) does not exclude using another backend like UART or RTT at the same time.
|
||||||
|
|
||||||
|
By default, the log levels for all modules are set to critical to not engage the microprocessor in unnecessary activities.
|
||||||
|
To make the solution flexible, you can change independently the log levels for your modules, for the whole Zephyr system, and for OpenThread.
|
||||||
|
Use the ``ot-debug`` snippet as reference for this purpose.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
.. code-block:: none
|
||||||
|
|
||||||
|
west build -b nrf54l15dk_nrf54l15_cpuapp -p -- -Dcoprocessor_SNIPPET=ot-debug
|
||||||
|
|
||||||
|
User interface
|
||||||
|
**************
|
||||||
|
|
||||||
|
All the interactions with the application are handled using serial communication.
|
||||||
|
|
||||||
|
You can interact with the sample through ``ot-daemon`` or ``ot-cli`` with commands listed in `OpenThread CLI Reference`_.
|
||||||
|
See :ref:`ug_thread_tools_ot_apps` for more information.
|
||||||
|
|
||||||
|
You can also use your own application, provided that it supports the Spinel communication protocol.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|thread_hwfc_enabled|
|
||||||
|
In addition, the Co-processor sample reconfigures the baud rate to 1000000 bit/s by default.
|
||||||
|
|
||||||
|
Diagnostic module
|
||||||
|
=================
|
||||||
|
|
||||||
|
The Co-processor sample enables a diagnostic module in a similar way as described in the :ref:`ot_cli_sample_diag_module` section of the :ref:`ot_cli_sample` sample documentation.
|
||||||
|
However, the Co-processor and CLI samples use different commands for the module, as described in the :ref:`ot_coprocessor_testing` section.
|
||||||
|
|
||||||
|
Rebooting to bootloader
|
||||||
|
=======================
|
||||||
|
|
||||||
|
The Co-processor sample enables rebooting to bootloader for the ``nrf52840dongle/nrf52840`` board target, similar to what is described in the :ref:`ot_cli_sample_bootloader` section of the :ref:`ot_cli_sample` sample documentation.
|
||||||
|
However, the Co-processor and CLI samples use different commands, as described in the :ref:`ot_coprocessor_testing` section.
|
||||||
|
Additionally, the :ref:`ug_thread_tools_ot_apps` should be built with ``-DOT_PLATFORM_BOOTLOADER_MODE=ON`` option.
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
*************
|
||||||
|
|
||||||
|
|config|
|
||||||
|
|
||||||
|
Check and configure the following library option that is used by the sample:
|
||||||
|
|
||||||
|
* :kconfig:option:`CONFIG_OPENTHREAD_COPROCESSOR_RCP` - Selects the RCP architecture for the sample.
|
||||||
|
|
||||||
|
|
||||||
|
.. include:: /includes/sample_fem_support.txt
|
||||||
|
|
||||||
|
Building and running
|
||||||
|
********************
|
||||||
|
|
||||||
|
.. |sample path| replace:: :file:`samples/openthread/coprocessor`
|
||||||
|
|
||||||
|
|enable_thread_before_testing|
|
||||||
|
|
||||||
|
.. include:: /includes/build_and_run.txt
|
||||||
|
|
||||||
|
.. _ot_coprocessor_testing:
|
||||||
|
|
||||||
|
HCI support
|
||||||
|
===========
|
||||||
|
|
||||||
|
The sample supports Bluetooth® Low Energy (LE) Controller functionality using HCI over UART.
|
||||||
|
To enable HCI support, complete the following steps:
|
||||||
|
|
||||||
|
.. tabs::
|
||||||
|
|
||||||
|
.. tab:: nRF54L15, nRF54L10, and nRF54L05 DKs
|
||||||
|
|
||||||
|
HCI communication is handled over a dedicated UART peripheral on the nRF54L Series System-on-Chip (SoC).
|
||||||
|
After connecting the board using the SEGGER J-Link USB port, two serial ports will be created.
|
||||||
|
The first port is used for HCI and the second for the Thread co-processor (Spinel).
|
||||||
|
|
||||||
|
To enable HCI support, run the following command:
|
||||||
|
|
||||||
|
.. parsed-literal::
|
||||||
|
:class: highlight
|
||||||
|
|
||||||
|
west build -b *board_target* -p -- -DEXTRA_CONF_FILE=extra_conf/rcp_hci_nrf54l_05_10_15.conf -DEXTRA_DTC_OVERLAY_FILE=extra_conf/rcp_hci_nrf54l_05_10_15.overlay
|
||||||
|
|
||||||
|
.. tab:: Other supported boards
|
||||||
|
|
||||||
|
For a list of supported boards, see the :ref:`ot_coprocessor_sample_requirements` section.
|
||||||
|
|
||||||
|
HCI communication is handled over a USB CDC ACM interface.
|
||||||
|
After connecting the board using the nRF USB port, two serial ports will be created.
|
||||||
|
Usually, the first port is used for HCI and the second for the Thread co-processor.
|
||||||
|
|
||||||
|
To enable HCI support, run the following command:
|
||||||
|
|
||||||
|
.. parsed-literal::
|
||||||
|
:class: highlight
|
||||||
|
|
||||||
|
west build -b *board_target* -p -- -DEXTRA_CONF_FILE=extra_conf/rcp_hci.conf -DEXTRA_DTC_OVERLAY_FILE=extra_conf/rcp_hci.overlay
|
||||||
|
|
||||||
|
Vendor hooks
|
||||||
|
============
|
||||||
|
|
||||||
|
The sample supports vendor hooks for co-processor architecture allowing you to extend handled properties by your own, customized functionalities.
|
||||||
|
|
||||||
|
To enable vendor hooks, set the :kconfig:option:`CONFIG_OPENTHREAD_COPROCESSOR_VENDOR_HOOK_SOURCE` Kconfig option to the path of the vendor hook source file and run the following command with *board_target* replaced with the board target name:
|
||||||
|
|
||||||
|
.. parsed-literal::
|
||||||
|
:class: highlight
|
||||||
|
|
||||||
|
west build -b *board_target* -p -- -DCONFIG_OPENTHREAD_COPROCESSOR_VENDOR_HOOK_SOURCE="/src/user_vendor_hook.cpp"
|
||||||
|
|
||||||
|
Testing
|
||||||
|
=======
|
||||||
|
|
||||||
|
After building the sample and programming it to your development kit, complete the following steps to test it:
|
||||||
|
|
||||||
|
1. Connect the development kit's SEGGER J-Link USB port to the PC USB port with a USB cable.
|
||||||
|
If you are using HCI for boards other than nRF54L15, nRF54L10, and nRF54L05 DKs, connect the kit's nRF USB port to the PC USB port instead.
|
||||||
|
#. Get the kit's serial port name (for example, :file:`/dev/ttyACM0`).
|
||||||
|
#. Run and configure ot-cli as described in :ref:`ug_thread_tools_ot_apps`.
|
||||||
|
#. From this point, you can follow the :ref:`ot_cli_sample_testing` instructions in the CLI sample by removing the `ot` prefix for each command.
|
||||||
|
If you are using HCI, follow the instructions for the :zephyr:code-sample:`bluetooth_hci_uart` sample in the Zephyr documentation.
|
||||||
|
You can follow these instead of or in addition to the CLI sample instructions.
|
||||||
|
|
||||||
|
Dependencies
|
||||||
|
************
|
||||||
|
|
||||||
|
This sample uses the following Zephyr libraries:
|
||||||
|
|
||||||
|
* :ref:`zephyr:kernel_api`:
|
||||||
|
|
||||||
|
* ``include/kernel.h``
|
||||||
|
|
||||||
|
* :ref:`zephyr:thread_protocol_interface`
|
||||||
|
|
||||||
|
* :ref:`zephyr:logging_api`:
|
||||||
|
|
||||||
|
* ``include/logging/log.h``
|
||||||
|
|
||||||
|
* :ref:`zephyr:bluetooth-hci`
|
||||||
18
boards/xiao_nrf54l15_nrf54l15_cpuapp.conf
Normal file
18
boards/xiao_nrf54l15_nrf54l15_cpuapp.conf
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# OpenThread RCP on the Seeed XIAO nRF54L15.
|
||||||
|
|
||||||
|
# The XIAO has no external SPI flash populated.
|
||||||
|
CONFIG_SPI_NOR=n
|
||||||
|
|
||||||
|
# Nordic uses this on the nRF54L15 DK to keep the main stack clear of CRACEN
|
||||||
|
# (the hardware crypto engine) stack usage.
|
||||||
|
CONFIG_MAIN_STACK_SIZE=2048
|
||||||
|
|
||||||
|
# Nothing may write to uart20 except spinel. The boot banner and the UART log
|
||||||
|
# backend would both land mid-stream and desynchronise otbr-agent's HDLC
|
||||||
|
# decoder. Logs ride in-band over spinel instead, where otbr-agent decodes them.
|
||||||
|
CONFIG_BOOT_BANNER=n
|
||||||
|
CONFIG_LOG=y
|
||||||
|
CONFIG_LOG_MAX_LEVEL=1
|
||||||
|
CONFIG_LOG_BACKEND_UART=n
|
||||||
|
CONFIG_LOG_BACKEND_SPINEL=y
|
||||||
|
CONFIG_LOG_PROCESS_THREAD_STACK_SIZE=2048
|
||||||
36
boards/xiao_nrf54l15_nrf54l15_cpuapp.overlay
Normal file
36
boards/xiao_nrf54l15_nrf54l15_cpuapp.overlay
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* OpenThread RCP on the Seeed XIAO nRF54L15.
|
||||||
|
*
|
||||||
|
* uart20 (P1.09 TX / P1.08 RX) is wired to the onboard CMSIS-DAP USB-serial
|
||||||
|
* bridge, so it is both the board's default console and the only UART that
|
||||||
|
* reaches the host over USB. The RCP needs that link for spinel, so:
|
||||||
|
*
|
||||||
|
* - zephyr,ot-uart -> uart20 (spinel to otbr-agent on the host)
|
||||||
|
* - zephyr,console -> uart21 (moved off, or console output would corrupt
|
||||||
|
* the spinel byte stream)
|
||||||
|
*
|
||||||
|
* uart21 (P2.08 TX / P2.07 RX) is exposed on the XIAO edge pads. Nothing needs
|
||||||
|
* to be attached; hook up a USB-UART adapter there if you want boot logs.
|
||||||
|
*
|
||||||
|
* Unlike Nordic's nrf54l15dk overlay, hw-flow-control is NOT set: the XIAO's
|
||||||
|
* pinctrl only defines TX and RX for uart20, and the CMSIS-DAP bridge does not
|
||||||
|
* carry RTS/CTS. See docs/ for the baud rate this was validated at.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/ {
|
||||||
|
chosen {
|
||||||
|
zephyr,ot-uart = &uart20;
|
||||||
|
zephyr,console = &uart21;
|
||||||
|
zephyr,shell-uart = &uart21;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
&uart20 {
|
||||||
|
status = "okay";
|
||||||
|
current-speed = <1000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
&uart21 {
|
||||||
|
status = "okay";
|
||||||
|
current-speed = <115200>;
|
||||||
|
};
|
||||||
24
prj.conf
Normal file
24
prj.conf
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2020 Nordic Semiconductor
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
|
||||||
|
#
|
||||||
|
|
||||||
|
# Enable OpenThread
|
||||||
|
CONFIG_OPENTHREAD=y
|
||||||
|
|
||||||
|
# Set OpenThread NCP architecture
|
||||||
|
CONFIG_OPENTHREAD_COPROCESSOR=y
|
||||||
|
CONFIG_OPENTHREAD_COPROCESSOR_RCP=y
|
||||||
|
|
||||||
|
# Nordic feature set
|
||||||
|
CONFIG_OPENTHREAD_NORDIC_LIBRARY_RCP=y
|
||||||
|
|
||||||
|
# Increase Settings storage size
|
||||||
|
CONFIG_PM_PARTITION_SIZE_SETTINGS_STORAGE=0x8000
|
||||||
|
|
||||||
|
# Disable RTT
|
||||||
|
CONFIG_USE_SEGGER_RTT=n
|
||||||
|
|
||||||
|
# Disable GPIO
|
||||||
|
CONFIG_GPIO=n
|
||||||
127
scripts/ncs-toolchain.sh
Executable file
127
scripts/ncs-toolchain.sh
Executable file
@@ -0,0 +1,127 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# nRF Connect SDK toolchain environment.
|
||||||
|
#
|
||||||
|
# Normally you do not source this directly. It is wired into Zephyr's own
|
||||||
|
# entry point via ~/.zephyrrc (see scripts/install-zephyrrc.sh), so that
|
||||||
|
#
|
||||||
|
# source /opt/ncs/v3.4.0/zephyr/zephyr-env.sh
|
||||||
|
#
|
||||||
|
# sets up ZEPHYR_BASE *and* the toolchain in one step. Sourcing zephyr-env.sh
|
||||||
|
# without this leaves you on the system cmake and python, and the build fails:
|
||||||
|
#
|
||||||
|
# Could NOT find Python3: Found unsuitable version "3.10.6",
|
||||||
|
# but required is at least "3.12"
|
||||||
|
#
|
||||||
|
# because /usr/local/bin/python3.10 (and the host's 3.14) are found before the
|
||||||
|
# Python 3.12 that the SDK toolchain bundle ships.
|
||||||
|
#
|
||||||
|
# Nothing is hardcoded: the bundle directory is discovered, and the variables
|
||||||
|
# are read from the bundle's own environment.json, so toolchain or SDK updates
|
||||||
|
# are picked up instead of silently rotting.
|
||||||
|
#
|
||||||
|
# Overrides, if the defaults guess wrong:
|
||||||
|
# NCS_ROOT SDK install root (default /opt/ncs)
|
||||||
|
# NCS_VERSION version dir under NCS_ROOT (default: newest vX.Y.Z)
|
||||||
|
# NCS_TOOLCHAIN toolchain bundle directory (default: most recent)
|
||||||
|
|
||||||
|
_ncs_warn() { echo "ncs-toolchain: $*" >&2; }
|
||||||
|
|
||||||
|
: "${NCS_ROOT:=/opt/ncs}"
|
||||||
|
|
||||||
|
if [ ! -d "$NCS_ROOT" ]; then
|
||||||
|
_ncs_warn "SDK root not found: $NCS_ROOT (set NCS_ROOT)"
|
||||||
|
else
|
||||||
|
|
||||||
|
# --- locate the SDK version directory ---------------------------------------
|
||||||
|
# Prefer the tree that zephyr-env.sh is actually being sourced from, so the
|
||||||
|
# toolchain always matches the SDK in use rather than guessing.
|
||||||
|
if [ -z "${NCS_VERSION:-}" ] && [ -n "${ZEPHYR_BASE:-}" ]; then
|
||||||
|
case "$ZEPHYR_BASE" in
|
||||||
|
"$NCS_ROOT"/*) NCS_VERSION="${ZEPHYR_BASE#"$NCS_ROOT"/}"; NCS_VERSION="${NCS_VERSION%%/*}" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
if [ -z "${NCS_VERSION:-}" ]; then
|
||||||
|
NCS_VERSION="$(
|
||||||
|
for d in "$NCS_ROOT"/v*/; do
|
||||||
|
[ -d "$d/zephyr" ] && basename "$d"
|
||||||
|
done | sort -V | tail -1
|
||||||
|
)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- locate the toolchain bundle --------------------------------------------
|
||||||
|
if [ -z "${NCS_TOOLCHAIN:-}" ]; then
|
||||||
|
# Bundles are keyed by an opaque id and manifest.json carries no plain SDK
|
||||||
|
# version to match on, so take the most recently installed.
|
||||||
|
NCS_TOOLCHAIN="$(
|
||||||
|
for d in "$NCS_ROOT"/toolchains/*/; do
|
||||||
|
[ -f "$d/environment.json" ] && printf '%s %s\n' "$(stat -c %Y "$d")" "${d%/}"
|
||||||
|
done | sort -n | tail -1 | cut -d' ' -f2-
|
||||||
|
)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${NCS_TOOLCHAIN:-}" ] || [ ! -f "$NCS_TOOLCHAIN/environment.json" ]; then
|
||||||
|
_ncs_warn "no toolchain bundle with environment.json under $NCS_ROOT/toolchains (set NCS_TOOLCHAIN)"
|
||||||
|
elif [ "${_NCS_TOOLCHAIN_APPLIED:-}" = "$NCS_TOOLCHAIN" ]; then
|
||||||
|
: # already applied in this shell; re-prepending PATH would just pile up
|
||||||
|
else
|
||||||
|
|
||||||
|
# --- apply the bundle's declared environment --------------------------------
|
||||||
|
# environment.json declares each variable as either a "string" or a list of
|
||||||
|
# paths relative to the bundle root, plus how to combine with any existing
|
||||||
|
# value ("prepend_to" or "overwrite"). Honour what it declares rather than
|
||||||
|
# copying today's values, so added or changed variables are picked up too.
|
||||||
|
#
|
||||||
|
# Parsed with the host python: the bundle's python is only usable once
|
||||||
|
# PYTHONHOME points at it, and a PYTHONHOME/PYTHONPATH left over from an
|
||||||
|
# earlier sourcing would break it -- hence `env -u`.
|
||||||
|
_ncs_env="$(env -u PYTHONHOME -u PYTHONPATH python3 - "$NCS_TOOLCHAIN" <<'PY'
|
||||||
|
import json, os, shlex, sys
|
||||||
|
|
||||||
|
root = sys.argv[1]
|
||||||
|
with open(os.path.join(root, "environment.json")) as fh:
|
||||||
|
spec = json.load(fh)
|
||||||
|
|
||||||
|
for var in spec.get("env_vars", []):
|
||||||
|
key = var["key"]
|
||||||
|
if var["type"] == "string":
|
||||||
|
value = var["value"]
|
||||||
|
elif var["type"] == "relative_paths":
|
||||||
|
value = os.pathsep.join(os.path.join(root, v) for v in var["values"])
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if var.get("existing_value_treatment") == "prepend_to":
|
||||||
|
existing = os.environ.get(key, "")
|
||||||
|
if existing:
|
||||||
|
value = value + os.pathsep + existing
|
||||||
|
print(f"export {key}={shlex.quote(value)}")
|
||||||
|
PY
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [ -z "$_ncs_env" ]; then
|
||||||
|
_ncs_warn "failed to read $NCS_TOOLCHAIN/environment.json"
|
||||||
|
else
|
||||||
|
eval "$_ncs_env"
|
||||||
|
|
||||||
|
# Some bundles declare this as "zephyr/gnu", which the Zephyr build does
|
||||||
|
# not accept; the SDK variant is plain "zephyr".
|
||||||
|
case "${ZEPHYR_TOOLCHAIN_VARIANT:-}" in
|
||||||
|
zephyr/*) export ZEPHYR_TOOLCHAIN_VARIANT=zephyr ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
export NCS_ROOT NCS_TOOLCHAIN
|
||||||
|
[ -n "${NCS_VERSION:-}" ] && export NCS_VERSION NCS_WORKSPACE="$NCS_ROOT/$NCS_VERSION"
|
||||||
|
|
||||||
|
# west resolves projects from the nearest .west found walking up from the
|
||||||
|
# current directory. A repo outside the SDK tree has none, so `west build`
|
||||||
|
# must run from inside the workspace with absolute -d and source paths.
|
||||||
|
[ -n "${NCS_WORKSPACE:-}" ] && export WEST_TOPDIR="$NCS_WORKSPACE"
|
||||||
|
|
||||||
|
export _NCS_TOOLCHAIN_APPLIED="$NCS_TOOLCHAIN"
|
||||||
|
fi
|
||||||
|
unset _ncs_env
|
||||||
|
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
unset -f _ncs_warn
|
||||||
31
src/main.c
Normal file
31
src/main.c
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
|
||||||
|
*/
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
|
||||||
|
#if defined(CONFIG_RCP_SAMPLE_HCI)
|
||||||
|
#include "rcp_hci.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
LOG_MODULE_REGISTER(coprocessor_sample, CONFIG_OT_COPROCESSOR_LOG_LEVEL);
|
||||||
|
|
||||||
|
#define WELCOME_TEXT \
|
||||||
|
"\n\r" \
|
||||||
|
"\n\r" \
|
||||||
|
"=========================================================\n\r" \
|
||||||
|
"OpenThread Coprocessor application is now running on NCS.\n\r" \
|
||||||
|
"=========================================================\n\r"
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
LOG_INF(WELCOME_TEXT);
|
||||||
|
|
||||||
|
#if defined(CONFIG_RCP_SAMPLE_HCI)
|
||||||
|
run_hci();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
74
src/nrf_802154_radio_wrapper.c
Normal file
74
src/nrf_802154_radio_wrapper.c
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Nordic Semiconductor ASA
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
|
||||||
|
*/
|
||||||
|
#include "nrf_802154_radio_wrapper.h"
|
||||||
|
#include <nrf_802154.h>
|
||||||
|
|
||||||
|
#ifdef CONFIG_NETWORKING
|
||||||
|
#include <zephyr/device.h>
|
||||||
|
#include <zephyr/net/ieee802154_radio.h>
|
||||||
|
|
||||||
|
static const struct device *const radio_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_ieee802154));
|
||||||
|
static struct ieee802154_radio_api *radio_api;
|
||||||
|
|
||||||
|
#else
|
||||||
|
static uint16_t get_capabilities(void)
|
||||||
|
{
|
||||||
|
nrf_802154_capabilities_t caps = nrf_802154_capabilities_get();
|
||||||
|
|
||||||
|
/* Convert nrf_802154_capabilities_t to ieee802154_hw_caps to keep backwards compatibility
|
||||||
|
* See @ref ieee802154_hw_caps in zephyr/net/ieee802154_radio.h for more details.
|
||||||
|
*/
|
||||||
|
return /* IEEE802154_HW_FCS */ BIT(1) |
|
||||||
|
/* IEEE802154_HW_PROMISC */ BIT(3) |
|
||||||
|
/* IEEE802154_HW_FILTER */ BIT(2) |
|
||||||
|
/* IEEE802154_HW_CSMA */ ((caps & NRF_802154_CAPABILITY_CSMA) ? BIT(4) : 0UL) |
|
||||||
|
/* IEEE802154_HW_TX_RX_ACK */ BIT(5) |
|
||||||
|
/* IEEE802154_HW_RX_TX_ACK */ BIT(7) |
|
||||||
|
/* IEEE802154_HW_ENERGY_SCAN */ BIT(0) |
|
||||||
|
/* IEEE802154_HW_TXTIME */
|
||||||
|
((caps & NRF_802154_CAPABILITY_DELAYED_TX) ? BIT(8) : 0UL) |
|
||||||
|
/* IEEE802154_HW_RXTIME */
|
||||||
|
((caps & NRF_802154_CAPABILITY_DELAYED_RX) ? BIT(10) : 0UL) |
|
||||||
|
/* IEEE802154_HW_SLEEP_TO_TX */ BIT(9) |
|
||||||
|
/* IEEE802154_HW_TX_SEC */ BIT(12) |
|
||||||
|
((caps & NRF_802154_CAPABILITY_SECURITY) ? BIT(11) : 0UL)
|
||||||
|
#if defined(CONFIG_IEEE802154_NRF5_MULTIPLE_CCA)
|
||||||
|
| /* IEEE802154_OPENTHREAD_HW_CAPS_BITS_START */ BIT(14)
|
||||||
|
#endif
|
||||||
|
#if defined(CONFIG_IEEE802154_SELECTIVE_TXCHANNEL)
|
||||||
|
| /* IEEE802154_HW_SELECTIVE_TXCHANNEL */ BIT(13)
|
||||||
|
#endif
|
||||||
|
#if defined(CONFIG_IEEE802154_NRF5_CST_ENDPOINT)
|
||||||
|
| /* IEEE802154_OPENTHREAD_HW_CAPS_BITS_START + 1*/ BIT(15)
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool nrf_802154_radio_wrapper_auto_ack_get(void)
|
||||||
|
{
|
||||||
|
return nrf_802154_auto_ack_get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void nrf_802154_radio_wrapper_auto_ack_set(bool enabled)
|
||||||
|
{
|
||||||
|
nrf_802154_auto_ack_set(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t nrf_802154_radio_wrapper_hw_capabilities_get(void)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_NETWORKING
|
||||||
|
__ASSERT_NO_MSG(device_is_ready(radio_dev));
|
||||||
|
|
||||||
|
radio_api = (struct ieee802154_radio_api *)radio_dev->api;
|
||||||
|
__ASSERT_NO_MSG(radio_api != NULL);
|
||||||
|
|
||||||
|
return radio_api->get_capabilities(radio_dev);
|
||||||
|
#else
|
||||||
|
return get_capabilities();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
44
src/nrf_802154_radio_wrapper.h
Normal file
44
src/nrf_802154_radio_wrapper.h
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Nordic Semiconductor ASA
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __NRF_802154_RADIO_WRAPPER_H__
|
||||||
|
#define __NRF_802154_RADIO_WRAPPER_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets current automatic acknowledgments mode state.
|
||||||
|
*
|
||||||
|
* @return true when auto ACK is set, false otherwise
|
||||||
|
*/
|
||||||
|
bool nrf_802154_radio_wrapper_auto_ack_get(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables or disables the automatic acknowledgments mode (auto ACK).
|
||||||
|
*
|
||||||
|
* @param[in] enabled Value setting (true) or clearing (false)
|
||||||
|
* auto ACK radio mode.
|
||||||
|
*/
|
||||||
|
void nrf_802154_radio_wrapper_auto_ack_set(bool enabled);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the device hardware capabilities
|
||||||
|
*
|
||||||
|
* @return 0 on failure; nonzero hw capabilities value
|
||||||
|
* on success (@ref ieee802154_hw_caps).
|
||||||
|
*/
|
||||||
|
uint16_t nrf_802154_radio_wrapper_hw_capabilities_get(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __NRF_802154_RADIO_WRAPPER_H__ */
|
||||||
381
src/rcp_hci.c
Normal file
381
src/rcp_hci.c
Normal file
@@ -0,0 +1,381 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <zephyr/arch/cpu.h>
|
||||||
|
#include <zephyr/sys/byteorder.h>
|
||||||
|
#include <zephyr/sys/util.h>
|
||||||
|
|
||||||
|
#include <zephyr/device.h>
|
||||||
|
#include <zephyr/init.h>
|
||||||
|
#include <zephyr/drivers/uart.h>
|
||||||
|
|
||||||
|
#include <zephyr/net_buf.h>
|
||||||
|
#include <zephyr/bluetooth/bluetooth.h>
|
||||||
|
#include <zephyr/bluetooth/l2cap.h>
|
||||||
|
#include <zephyr/bluetooth/hci.h>
|
||||||
|
#include <zephyr/bluetooth/buf.h>
|
||||||
|
#include <zephyr/bluetooth/hci_raw.h>
|
||||||
|
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
|
||||||
|
LOG_MODULE_REGISTER(rcp_hci_module, CONFIG_OT_COPROCESSOR_LOG_LEVEL);
|
||||||
|
|
||||||
|
static const struct device *const hci_uart_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_bt_c2h_uart));
|
||||||
|
static K_THREAD_STACK_DEFINE(tx_thread_stack, CONFIG_BT_HCI_TX_STACK_SIZE);
|
||||||
|
static struct k_thread tx_thread_data;
|
||||||
|
static K_FIFO_DEFINE(tx_queue);
|
||||||
|
|
||||||
|
/* RX in terms of bluetooth communication */
|
||||||
|
static K_FIFO_DEFINE(uart_tx_queue);
|
||||||
|
|
||||||
|
#define H4_CMD 0x01
|
||||||
|
#define H4_ACL 0x02
|
||||||
|
#define H4_SCO 0x03
|
||||||
|
#define H4_EVT 0x04
|
||||||
|
#define H4_ISO 0x05
|
||||||
|
|
||||||
|
/* Receiver states. */
|
||||||
|
#define ST_IDLE 0 /* Waiting for packet type. */
|
||||||
|
#define ST_HDR 1 /* Receiving packet header. */
|
||||||
|
#define ST_PAYLOAD 2 /* Receiving packet payload. */
|
||||||
|
#define ST_DISCARD 3 /* Dropping packet. */
|
||||||
|
|
||||||
|
#define H4_DISCARD_LEN 33
|
||||||
|
|
||||||
|
static int h4_read(const struct device *uart, uint8_t *buf, size_t len)
|
||||||
|
{
|
||||||
|
int rx = uart_fifo_read(uart, buf, len);
|
||||||
|
|
||||||
|
LOG_DBG("read %d req %d", rx, len);
|
||||||
|
|
||||||
|
return rx;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool valid_type(uint8_t type)
|
||||||
|
{
|
||||||
|
return (type == H4_CMD) | (type == H4_ACL) | (type == H4_ISO);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Function expects that type is validated and only CMD, ISO or ACL will be used. */
|
||||||
|
static uint32_t get_len(const uint8_t *hdr_buf, uint8_t type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case H4_CMD:
|
||||||
|
return ((const struct bt_hci_cmd_hdr *)hdr_buf)->param_len;
|
||||||
|
case H4_ISO:
|
||||||
|
return bt_iso_hdr_len(
|
||||||
|
sys_le16_to_cpu(((const struct bt_hci_iso_hdr *)hdr_buf)->len));
|
||||||
|
case H4_ACL:
|
||||||
|
return sys_le16_to_cpu(((const struct bt_hci_acl_hdr *)hdr_buf)->len);
|
||||||
|
default:
|
||||||
|
LOG_ERR("Invalid type: %u", type);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Function expects that type is validated and only CMD, ISO or ACL will be used. */
|
||||||
|
static int hdr_len(uint8_t type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case H4_CMD:
|
||||||
|
return sizeof(struct bt_hci_cmd_hdr);
|
||||||
|
case H4_ISO:
|
||||||
|
return sizeof(struct bt_hci_iso_hdr);
|
||||||
|
case H4_ACL:
|
||||||
|
return sizeof(struct bt_hci_acl_hdr);
|
||||||
|
default:
|
||||||
|
LOG_ERR("Invalid type: %u", type);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rx_isr(void)
|
||||||
|
{
|
||||||
|
static struct net_buf *buf;
|
||||||
|
static int remaining;
|
||||||
|
static uint8_t state;
|
||||||
|
static uint8_t type;
|
||||||
|
static uint8_t hdr_buf[MAX(sizeof(struct bt_hci_cmd_hdr), sizeof(struct bt_hci_acl_hdr))];
|
||||||
|
int read;
|
||||||
|
|
||||||
|
do {
|
||||||
|
switch (state) {
|
||||||
|
case ST_IDLE:
|
||||||
|
/* Get packet type */
|
||||||
|
read = h4_read(hci_uart_dev, &type, sizeof(type));
|
||||||
|
/* since we read in loop until no data is in the fifo,
|
||||||
|
* it is possible that read = 0.
|
||||||
|
*/
|
||||||
|
if (read) {
|
||||||
|
if (valid_type(type)) {
|
||||||
|
/* Get expected header size and switch
|
||||||
|
* to receiving header.
|
||||||
|
*/
|
||||||
|
remaining = hdr_len(type);
|
||||||
|
state = ST_HDR;
|
||||||
|
} else {
|
||||||
|
LOG_WRN("Unknown header %d", type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ST_HDR:
|
||||||
|
read = h4_read(hci_uart_dev, &hdr_buf[hdr_len(type) - remaining],
|
||||||
|
remaining);
|
||||||
|
remaining -= read;
|
||||||
|
if (remaining == 0) {
|
||||||
|
/* Header received. Allocate buffer and get
|
||||||
|
* payload length. If allocation fails leave
|
||||||
|
* interrupt. On failed allocation state machine
|
||||||
|
* is reset.
|
||||||
|
*/
|
||||||
|
buf = bt_buf_get_tx(bt_buf_type_from_h4(type, BT_BUF_OUT),
|
||||||
|
K_NO_WAIT, NULL, 0);
|
||||||
|
if (!buf) {
|
||||||
|
LOG_ERR("No available command buffers!");
|
||||||
|
state = ST_IDLE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
remaining = get_len(hdr_buf, type);
|
||||||
|
|
||||||
|
net_buf_add_mem(buf, hdr_buf, hdr_len(type));
|
||||||
|
if (remaining > net_buf_tailroom(buf)) {
|
||||||
|
LOG_ERR("Not enough space in buffer");
|
||||||
|
net_buf_unref(buf);
|
||||||
|
state = ST_DISCARD;
|
||||||
|
} else {
|
||||||
|
state = ST_PAYLOAD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ST_PAYLOAD:
|
||||||
|
read = h4_read(hci_uart_dev, net_buf_tail(buf), remaining);
|
||||||
|
buf->len += read;
|
||||||
|
remaining -= read;
|
||||||
|
if (remaining == 0) {
|
||||||
|
/* Packet received */
|
||||||
|
LOG_DBG("putting RX packet in queue.");
|
||||||
|
k_fifo_put(&tx_queue, buf);
|
||||||
|
state = ST_IDLE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ST_DISCARD: {
|
||||||
|
uint8_t discard[H4_DISCARD_LEN];
|
||||||
|
size_t to_read = MIN(remaining, sizeof(discard));
|
||||||
|
|
||||||
|
read = h4_read(hci_uart_dev, discard, to_read);
|
||||||
|
remaining -= read;
|
||||||
|
if (remaining == 0) {
|
||||||
|
state = ST_IDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
read = 0;
|
||||||
|
__ASSERT_NO_MSG(0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (read);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tx_isr(void)
|
||||||
|
{
|
||||||
|
static struct net_buf *buf;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
if (!buf) {
|
||||||
|
buf = k_fifo_get(&uart_tx_queue, K_NO_WAIT);
|
||||||
|
if (!buf) {
|
||||||
|
uart_irq_tx_disable(hci_uart_dev);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
len = uart_fifo_fill(hci_uart_dev, buf->data, buf->len);
|
||||||
|
net_buf_pull(buf, len);
|
||||||
|
if (!buf->len) {
|
||||||
|
net_buf_unref(buf);
|
||||||
|
buf = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bt_uart_isr(const struct device *unused, void *user_data)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(unused);
|
||||||
|
ARG_UNUSED(user_data);
|
||||||
|
|
||||||
|
if (!(uart_irq_rx_ready(hci_uart_dev) || uart_irq_tx_ready(hci_uart_dev))) {
|
||||||
|
LOG_DBG("spurious interrupt");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uart_irq_tx_ready(hci_uart_dev)) {
|
||||||
|
tx_isr();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uart_irq_rx_ready(hci_uart_dev)) {
|
||||||
|
rx_isr();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tx_thread(void *p1, void *p2, void *p3)
|
||||||
|
{
|
||||||
|
while (1) {
|
||||||
|
struct net_buf *buf;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/* Wait until a buffer is available */
|
||||||
|
buf = k_fifo_get(&tx_queue, K_FOREVER);
|
||||||
|
/* Pass buffer to the stack */
|
||||||
|
err = bt_send(buf);
|
||||||
|
if (err) {
|
||||||
|
LOG_ERR("Unable to send (err %d)", err);
|
||||||
|
net_buf_unref(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Give other threads a chance to run if tx_queue keeps getting
|
||||||
|
* new data all the time.
|
||||||
|
*/
|
||||||
|
k_yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int h4_send(struct net_buf *buf)
|
||||||
|
{
|
||||||
|
LOG_DBG("buf %p type %u len %u", buf, buf->data[0], buf->len);
|
||||||
|
|
||||||
|
k_fifo_put(&uart_tx_queue, buf);
|
||||||
|
uart_irq_tx_enable(hci_uart_dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(CONFIG_BT_CTLR_ASSERT_HANDLER)
|
||||||
|
void bt_ctlr_assert_handle(char *file, uint32_t line)
|
||||||
|
{
|
||||||
|
uint32_t len = 0U, pos = 0U;
|
||||||
|
|
||||||
|
/* Disable interrupts, this is unrecoverable */
|
||||||
|
(void)irq_lock();
|
||||||
|
|
||||||
|
uart_irq_rx_disable(hci_uart_dev);
|
||||||
|
uart_irq_tx_disable(hci_uart_dev);
|
||||||
|
|
||||||
|
if (file) {
|
||||||
|
while (file[len] != '\0') {
|
||||||
|
if (file[len] == '/') {
|
||||||
|
pos = len + 1;
|
||||||
|
}
|
||||||
|
len++;
|
||||||
|
}
|
||||||
|
file += pos;
|
||||||
|
len -= pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
uart_poll_out(hci_uart_dev, H4_EVT);
|
||||||
|
/* Vendor-Specific debug event */
|
||||||
|
uart_poll_out(hci_uart_dev, 0xff);
|
||||||
|
/* 0xAA + strlen + \0 + 32-bit line number */
|
||||||
|
uart_poll_out(hci_uart_dev, 1 + len + 1 + 4);
|
||||||
|
uart_poll_out(hci_uart_dev, 0xAA);
|
||||||
|
|
||||||
|
if (len) {
|
||||||
|
while (*file != '\0') {
|
||||||
|
uart_poll_out(hci_uart_dev, *file);
|
||||||
|
file++;
|
||||||
|
}
|
||||||
|
uart_poll_out(hci_uart_dev, 0x00);
|
||||||
|
}
|
||||||
|
|
||||||
|
uart_poll_out(hci_uart_dev, line >> 0 & 0xff);
|
||||||
|
uart_poll_out(hci_uart_dev, line >> 8 & 0xff);
|
||||||
|
uart_poll_out(hci_uart_dev, line >> 16 & 0xff);
|
||||||
|
uart_poll_out(hci_uart_dev, line >> 24 & 0xff);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_BT_CTLR_ASSERT_HANDLER */
|
||||||
|
|
||||||
|
static int hci_uart_init(void)
|
||||||
|
{
|
||||||
|
LOG_DBG("");
|
||||||
|
|
||||||
|
if (!device_is_ready(hci_uart_dev)) {
|
||||||
|
LOG_ERR("HCI UART %s is not ready", hci_uart_dev->name);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
uart_irq_rx_disable(hci_uart_dev);
|
||||||
|
uart_irq_tx_disable(hci_uart_dev);
|
||||||
|
|
||||||
|
uart_irq_callback_set(hci_uart_dev, bt_uart_isr);
|
||||||
|
|
||||||
|
uart_irq_rx_enable(hci_uart_dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void run_hci(void)
|
||||||
|
{
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
__ASSERT(hci_uart_dev, "UART device is NULL");
|
||||||
|
/* incoming events and data from the controller */
|
||||||
|
static K_FIFO_DEFINE(rx_queue);
|
||||||
|
|
||||||
|
/* Enable the raw interface, this will in turn open the HCI driver */
|
||||||
|
bt_enable_raw(&rx_queue);
|
||||||
|
|
||||||
|
if (IS_ENABLED(CONFIG_BT_WAIT_NOP)) {
|
||||||
|
/* Issue a Command Complete with NOP */
|
||||||
|
int i;
|
||||||
|
|
||||||
|
const struct {
|
||||||
|
const uint8_t h4;
|
||||||
|
const struct bt_hci_evt_hdr hdr;
|
||||||
|
const 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),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (i = 0; i < sizeof(cc_evt); i++) {
|
||||||
|
uart_poll_out(hci_uart_dev, *(((const uint8_t *)&cc_evt) + i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Spawn the TX thread and start feeding commands and data to the
|
||||||
|
* controller
|
||||||
|
*/
|
||||||
|
k_thread_create(&tx_thread_data, tx_thread_stack, K_THREAD_STACK_SIZEOF(tx_thread_stack),
|
||||||
|
tx_thread, NULL, NULL, NULL, K_PRIO_COOP(7), 0, K_NO_WAIT);
|
||||||
|
k_thread_name_set(&tx_thread_data, "HCI uart TX");
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
struct net_buf *buf;
|
||||||
|
|
||||||
|
buf = k_fifo_get(&rx_queue, K_FOREVER);
|
||||||
|
err = h4_send(buf);
|
||||||
|
if (err) {
|
||||||
|
LOG_ERR("Failed to send");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SYS_INIT(hci_uart_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);
|
||||||
12
src/rcp_hci.h
Normal file
12
src/rcp_hci.h
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __RCP_HCI_H__
|
||||||
|
#define __RCP_HCI_H__
|
||||||
|
|
||||||
|
void run_hci(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
157
src/user_vendor_hook.cpp
Normal file
157
src/user_vendor_hook.cpp
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Nordic Semiconductor ASA
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* This file shows how to implement the NCP vendor hook.
|
||||||
|
*/
|
||||||
|
#if OPENTHREAD_ENABLE_NCP_VENDOR_HOOK
|
||||||
|
#include "nrf_802154_radio_wrapper.h"
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
#include <ncp_base.hpp>
|
||||||
|
#include <ncp_hdlc.hpp>
|
||||||
|
#include <common/new.hpp>
|
||||||
|
|
||||||
|
#define VENDOR_SPINEL_PROP_VENDOR_NAME SPINEL_PROP_VENDOR__BEGIN
|
||||||
|
#define VENDOR_SPINEL_PROP_AUTO_ACK_ENABLED SPINEL_PROP_VENDOR__BEGIN + 1
|
||||||
|
#define VENDOR_SPINEL_PROP_HW_CAPABILITIES SPINEL_PROP_VENDOR__BEGIN + 2
|
||||||
|
|
||||||
|
LOG_MODULE_REGISTER(ncp_sample_vendor_hook, CONFIG_OT_COPROCESSOR_LOG_LEVEL);
|
||||||
|
|
||||||
|
namespace ot {
|
||||||
|
namespace Ncp {
|
||||||
|
|
||||||
|
otError NcpBase::VendorCommandHandler(uint8_t aHeader, unsigned int aCommand)
|
||||||
|
{
|
||||||
|
otError error = OT_ERROR_NONE;
|
||||||
|
|
||||||
|
LOG_DBG("VendorCommandHandler");
|
||||||
|
|
||||||
|
switch (aCommand)
|
||||||
|
{
|
||||||
|
// TODO: Implement your command handlers here.
|
||||||
|
|
||||||
|
default:
|
||||||
|
error = PrepareLastStatusResponse(aHeader, SPINEL_STATUS_INVALID_COMMAND);
|
||||||
|
}
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NcpBase::VendorHandleFrameRemovedFromNcpBuffer(Spinel::Buffer::FrameTag aFrameTag)
|
||||||
|
{
|
||||||
|
// This method is a callback which mirrors `NcpBase::HandleFrameRemovedFromNcpBuffer()`.
|
||||||
|
// It is called when a spinel frame is sent and removed from NCP buffer.
|
||||||
|
//
|
||||||
|
// (a) This can be used to track and verify that a vendor spinel frame response is
|
||||||
|
// delivered to the host (tracking the frame using its tag).
|
||||||
|
//
|
||||||
|
// (b) It indicates that NCP buffer space is now available (since a spinel frame is
|
||||||
|
// removed). This can be used to implement reliability mechanisms to re-send
|
||||||
|
// a failed spinel command response (or an async spinel frame) transmission
|
||||||
|
// (failed earlier due to NCP buffer being full).
|
||||||
|
|
||||||
|
OT_UNUSED_VARIABLE(aFrameTag);
|
||||||
|
}
|
||||||
|
|
||||||
|
otError NcpBase::VendorGetPropertyHandler(spinel_prop_key_t aPropKey)
|
||||||
|
{
|
||||||
|
otError error = OT_ERROR_NONE;
|
||||||
|
|
||||||
|
switch (aPropKey)
|
||||||
|
{
|
||||||
|
case VENDOR_SPINEL_PROP_VENDOR_NAME:
|
||||||
|
LOG_DBG("Got NAME get property request");
|
||||||
|
error = mEncoder.WriteUtf8("Nordic Seminconductor");
|
||||||
|
break;
|
||||||
|
case VENDOR_SPINEL_PROP_AUTO_ACK_ENABLED:
|
||||||
|
LOG_DBG("Got VENDOR_SPINEL_PROP_AUTO_ACK_ENABLED get property request");
|
||||||
|
error = mEncoder.WriteUint8(nrf_802154_radio_wrapper_auto_ack_get());
|
||||||
|
break;
|
||||||
|
case VENDOR_SPINEL_PROP_HW_CAPABILITIES:
|
||||||
|
LOG_DBG("Got VENDOR_SPINEL_PROP_HW_CAPABILITIES get property request");
|
||||||
|
error = mEncoder.WriteUint16(nrf_802154_radio_wrapper_hw_capabilities_get());
|
||||||
|
break;
|
||||||
|
|
||||||
|
// TODO: Implement your get properties handlers here.
|
||||||
|
default:
|
||||||
|
error = OT_ERROR_NOT_FOUND;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
otError NcpBase::VendorSetPropertyHandler(spinel_prop_key_t aPropKey)
|
||||||
|
{
|
||||||
|
otError error = OT_ERROR_NONE;
|
||||||
|
|
||||||
|
switch (aPropKey)
|
||||||
|
{
|
||||||
|
case VENDOR_SPINEL_PROP_AUTO_ACK_ENABLED: {
|
||||||
|
LOG_DBG("Got VENDOR_SPINEL_PROP_AUTO_ACK_ENABLED set property request");
|
||||||
|
|
||||||
|
uint8_t mode = 0;
|
||||||
|
SuccessOrExit(error = mDecoder.ReadUint8(mode));
|
||||||
|
nrf_802154_radio_wrapper_auto_ack_set(mode);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// TODO: Implement your set properties handlers here.
|
||||||
|
default:
|
||||||
|
error = OT_ERROR_NOT_FOUND;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Ncp
|
||||||
|
} // namespace ot
|
||||||
|
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
// When OPENTHREAD_ENABLE_NCP_VENDOR_HOOK is enabled, vendor code is
|
||||||
|
// expected to provide the `otNcpInit()` function. The reason behind
|
||||||
|
// this is to enable vendor code to define its own sub-class of
|
||||||
|
// `NcpBase` or `NcpHdlc`/`NcpSpi`.
|
||||||
|
//
|
||||||
|
// Example below show how to add a vendor sub-class over `NcpHdlc`.
|
||||||
|
|
||||||
|
class NcpVendorUart : public ot::Ncp::NcpHdlc
|
||||||
|
{
|
||||||
|
|
||||||
|
static int SendHdlc(const uint8_t *aBuf, uint16_t aBufLength)
|
||||||
|
{
|
||||||
|
return mSendCallback(aBuf, aBufLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
NcpVendorUart(ot::Instance *aInstance, otNcpHdlcSendCallback aSendCallback)
|
||||||
|
: ot::Ncp::NcpHdlc(aInstance, &NcpVendorUart::SendHdlc)
|
||||||
|
{
|
||||||
|
OT_ASSERT(aSendCallback);
|
||||||
|
mSendCallback = aSendCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add public/private methods or member variables
|
||||||
|
private:
|
||||||
|
static otNcpHdlcSendCallback mSendCallback;
|
||||||
|
};
|
||||||
|
|
||||||
|
otNcpHdlcSendCallback NcpVendorUart::mSendCallback;
|
||||||
|
|
||||||
|
static OT_DEFINE_ALIGNED_VAR(sNcpVendorRaw, sizeof(NcpVendorUart), uint64_t);
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" void otNcpHdlcInit(otInstance *aInstance, otNcpHdlcSendCallback aSendCallback)
|
||||||
|
{
|
||||||
|
NcpVendorUart *ncpVendor = NULL;
|
||||||
|
ot::Instance *instance = static_cast<ot::Instance *>(aInstance);
|
||||||
|
|
||||||
|
ncpVendor = new (&sNcpVendorRaw) NcpVendorUart(instance, aSendCallback);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user