commit 59248c1b98ed553ab903a9a9782d540a62591175 Author: pstruebi Date: Thu Feb 13 16:23:03 2025 +0100 Initial commit for hci_uart diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..635a99b --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# editors +*.swp +*~ + +# build +/build*/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..51a2518 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(hci_uart) + +target_sources(app PRIVATE src/main.c) diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..d07ddd4 --- /dev/null +++ b/README.rst @@ -0,0 +1,202 @@ +.. zephyr:code-sample:: bluetooth_hci_uart + :name: HCI UART + :relevant-api: hci_raw bluetooth uart_interface + + Expose a Bluetooth controller to another device or CPU over UART. + +Overview +********* + +Expose Bluetooth controller support over UART to another device/CPU +using the H:4 HCI transport protocol (requires HW flow control from the UART). + +Requirements +************ + +* A board with Bluetooth LE 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` in the +Zephyr tree, and it 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. This can be very useful for testing +the Zephyr Link Layer with the BlueZ Host. The Zephyr Bluetooth LE controller can also +provide a modern Bluetooth LE 5.0 controller to a Linux-based machine for native +BLE support or QEMU-based development. + +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 + :board: nrf52dk/nrf52832 + :goals: build flash + +.. _bluetooth-hci-uart-qemu-posix: + +Using the controller with QEMU or native_sim +============================================ + +In order to use the HCI UART controller with QEMU or :ref:`native_sim ` 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:: + If using the BBC micro:bit you will need to modify the baudrate argument + from ``1000000`` to ``115200``. + +.. 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_native` section to use QEMU with it. + +.. _bluetooth-hci-uart-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. + +.. note:: + If using the BBC micro:bit you will need to modify the baudrate argument + from ``1000000`` to ``115200``. + +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 otherwise 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 -- -DEXTRA_CONF_FILE='debug.conf' + +Then attach RTT as described here: :ref:`Using Segger J-Link ` + +Support for the Direction Finding +================================= + +The sample can be built with the support for the Bluetooth LE Direction Finding. +To enable this feature build this sample for specific board variants that provide +required hardware configuration for the Radio. + +.. code-block:: console + + west build samples/bluetooth/hci_uart -b nrf52833dk/nrf52833@df -- -DCONFIG_BT_CTLR_DF=y + +You can use following targets: + +* ``nrf5340dk/nrf5340/cpunet@df`` +* ``nrf52833dk/nrf52833@df`` + +Check the :zephyr:code-sample:`ble_direction_finding_connectionless_rx` and the +:zephyr:code-sample:`ble_direction_finding_connectionless_tx` for more details. + +Using a USB CDC ACM UART +======================== + +The sample can be configured to use a USB UART instead. See :zephyr_file:`samples/bluetooth/hci_uart/boards/nrf52840dongle_nrf52840.conf` and :zephyr_file:`samples/bluetooth/hci_uart/boards/nrf52840dongle_nrf52840.overlay`. + +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:: cfg + + CONFIG_BT_HCI=y + CONFIG_BT_CTLR=n + +Similarly, the ``zephyr,bt-hci`` DTS property selects which HCI instance to use. +The UART needs to have as its child node a HCI UART node: + +.. code-block:: dts + + / { + chosen { + zephyr,console = &uart0; + zephyr,shell-uart = &uart0; + zephyr,bt-hci = &bt_hci_uart; + }; + }; + + &uart1 { + status = "okay"; + bt_hci_uart: bt_hci_uart { + compatible = "zephyr,bt-hci-uart"; + status = "okay"; + }; + }; diff --git a/boards/96b_nitrogen.overlay b/boards/96b_nitrogen.overlay new file mode 100644 index 0000000..26603b9 --- /dev/null +++ b/boards/96b_nitrogen.overlay @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +&uart0 { + compatible = "nordic,nrf-uart"; + current-speed = <1000000>; + hw-flow-control; + status = "okay"; +}; diff --git a/boards/bbc_microbit.conf b/boards/bbc_microbit.conf new file mode 100644 index 0000000..4ec62e1 --- /dev/null +++ b/boards/bbc_microbit.conf @@ -0,0 +1,8 @@ +CONFIG_MAIN_STACK_SIZE=512 +CONFIG_IDLE_STACK_SIZE=256 +CONFIG_ISR_STACK_SIZE=512 +CONFIG_BT_MAX_CONN=10 +# Revert values set in prj.conf, set them to their Kconfig default value +CONFIG_BT_BUF_CMD_TX_SIZE=65 +CONFIG_BT_BUF_ACL_RX_SIZE=69 +CONFIG_BT_BUF_EVT_DISCARDABLE_SIZE=43 diff --git a/boards/esp32_devkitc_wroom_procpu.conf b/boards/esp32_devkitc_wroom_procpu.conf new file mode 100644 index 0000000..460f012 --- /dev/null +++ b/boards/esp32_devkitc_wroom_procpu.conf @@ -0,0 +1,4 @@ +CONFIG_CONSOLE=y +CONFIG_STDOUT_CONSOLE=y +CONFIG_UART_CONSOLE=y +CONFIG_BT_MAX_CONN=9 diff --git a/boards/esp32_devkitc_wroom_procpu.overlay b/boards/esp32_devkitc_wroom_procpu.overlay new file mode 100644 index 0000000..c962a4a --- /dev/null +++ b/boards/esp32_devkitc_wroom_procpu.overlay @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + chosen { + zephyr,bt-c2h-uart = &uart1; + }; +}; + +&pinctrl { + + uart1_default: uart1_default { + group1 { + pinmux = , + , + ; + }; + group2 { + pinmux = ; + bias-pull-up; + }; + }; + +}; + +&uart1 { + status = "okay"; + current-speed = <921600>; + pinctrl-0 = <&uart1_default>; + pinctrl-names = "default"; +}; diff --git a/boards/nrf51_blenano.conf b/boards/nrf51_blenano.conf new file mode 100644 index 0000000..ca09bb8 --- /dev/null +++ b/boards/nrf51_blenano.conf @@ -0,0 +1,8 @@ +CONFIG_MAIN_STACK_SIZE=512 +CONFIG_BT_MAX_CONN=10 +CONFIG_IDLE_STACK_SIZE=256 +CONFIG_ISR_STACK_SIZE=512 +# Revert values set in prj.conf, set them to their Kconfig default value +CONFIG_BT_BUF_CMD_TX_SIZE=65 +CONFIG_BT_BUF_ACL_RX_SIZE=69 +CONFIG_BT_BUF_EVT_DISCARDABLE_SIZE=43 diff --git a/boards/nrf51_blenano.overlay b/boards/nrf51_blenano.overlay new file mode 100644 index 0000000..b3c8444 --- /dev/null +++ b/boards/nrf51_blenano.overlay @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +&uart0 { + compatible = "nordic,nrf-uart"; + current-speed = <1000000>; + status = "okay"; + hw-flow-control; +}; diff --git a/boards/nrf51dk_nrf51822.conf b/boards/nrf51dk_nrf51822.conf new file mode 100644 index 0000000..33a42e7 --- /dev/null +++ b/boards/nrf51dk_nrf51822.conf @@ -0,0 +1 @@ +CONFIG_MAIN_STACK_SIZE=512 diff --git a/boards/nrf51dk_nrf51822.overlay b/boards/nrf51dk_nrf51822.overlay new file mode 100644 index 0000000..b3c8444 --- /dev/null +++ b/boards/nrf51dk_nrf51822.overlay @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +&uart0 { + compatible = "nordic,nrf-uart"; + current-speed = <1000000>; + status = "okay"; + hw-flow-control; +}; diff --git a/boards/nrf51dongle_nrf51822.conf b/boards/nrf51dongle_nrf51822.conf new file mode 100644 index 0000000..33a42e7 --- /dev/null +++ b/boards/nrf51dongle_nrf51822.conf @@ -0,0 +1 @@ +CONFIG_MAIN_STACK_SIZE=512 diff --git a/boards/nrf51dongle_nrf51822.overlay b/boards/nrf51dongle_nrf51822.overlay new file mode 100644 index 0000000..b3c8444 --- /dev/null +++ b/boards/nrf51dongle_nrf51822.overlay @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +&uart0 { + compatible = "nordic,nrf-uart"; + current-speed = <1000000>; + status = "okay"; + hw-flow-control; +}; diff --git a/boards/nrf52833dk_nrf52833.overlay b/boards/nrf52833dk_nrf52833.overlay new file mode 100644 index 0000000..1dd3aea --- /dev/null +++ b/boards/nrf52833dk_nrf52833.overlay @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + + &uart0 { + compatible = "nordic,nrf-uarte"; + current-speed = <1000000>; + status = "okay"; + hw-flow-control; +}; diff --git a/boards/nrf52833dk_nrf52833_df.overlay b/boards/nrf52833dk_nrf52833_df.overlay new file mode 100644 index 0000000..50d31f0 --- /dev/null +++ b/boards/nrf52833dk_nrf52833_df.overlay @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + + &uart0 { + compatible = "nordic,nrf-uarte"; + current-speed = <1000000>; + status = "okay"; + hw-flow-control; +}; + +&radio { + status = "okay"; + /* This is an example number of antennas that may be available + * on antenna matrix board. + */ + dfe-antenna-num = <10>; + /* This is an example switch pattern that will be used to set an + * antenna for Tx PDU (period before start of Tx CTE). + */ + dfe-pdu-antenna = <0x0>; + + /* These are example GPIO pin numbers that are provided to + * Radio peripheral. The pins will be acquired by Radio to + * drive antenna switching when AoD is enabled. + */ + dfegpio0-gpios = <&gpio0 3 0>; + dfegpio1-gpios = <&gpio0 4 0>; + dfegpio2-gpios = <&gpio0 28 0>; + dfegpio3-gpios = <&gpio0 29 0>; +}; diff --git a/boards/nrf52840dk_nrf52840.overlay b/boards/nrf52840dk_nrf52840.overlay new file mode 100644 index 0000000..b3c8444 --- /dev/null +++ b/boards/nrf52840dk_nrf52840.overlay @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +&uart0 { + compatible = "nordic,nrf-uart"; + current-speed = <1000000>; + status = "okay"; + hw-flow-control; +}; diff --git a/boards/nrf52840dongle_nrf52840.conf b/boards/nrf52840dongle_nrf52840.conf new file mode 100644 index 0000000..72c806e --- /dev/null +++ b/boards/nrf52840dongle_nrf52840.conf @@ -0,0 +1,4 @@ +CONFIG_USB_DEVICE_STACK=y +CONFIG_USB_DEVICE_PRODUCT="Zephyr HCI UART sample" +CONFIG_USB_CDC_ACM=y +CONFIG_USB_DEVICE_INITIALIZE_AT_BOOT=n diff --git a/boards/nrf52_blenano2.overlay b/boards/nrf52_blenano2.overlay new file mode 100644 index 0000000..b3c8444 --- /dev/null +++ b/boards/nrf52_blenano2.overlay @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +&uart0 { + compatible = "nordic,nrf-uart"; + current-speed = <1000000>; + status = "okay"; + hw-flow-control; +}; diff --git a/boards/nrf52dk_nrf52832.overlay b/boards/nrf52dk_nrf52832.overlay new file mode 100644 index 0000000..b3c8444 --- /dev/null +++ b/boards/nrf52dk_nrf52832.overlay @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +&uart0 { + compatible = "nordic,nrf-uart"; + current-speed = <1000000>; + status = "okay"; + hw-flow-control; +}; diff --git a/boards/nrf5340dk_nrf5340_cpuapp.overlay b/boards/nrf5340dk_nrf5340_cpuapp.overlay new file mode 100644 index 0000000..6a4b799 --- /dev/null +++ b/boards/nrf5340dk_nrf5340_cpuapp.overlay @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +&uart0 { + compatible = "nordic,nrf-uarte"; + current-speed = <1000000>; + status = "okay"; + hw-flow-control; +}; diff --git a/boards/nrf5340dk_nrf5340_cpunet.overlay b/boards/nrf5340dk_nrf5340_cpunet.overlay new file mode 100644 index 0000000..6a4b799 --- /dev/null +++ b/boards/nrf5340dk_nrf5340_cpunet.overlay @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +&uart0 { + compatible = "nordic,nrf-uarte"; + current-speed = <1000000>; + status = "okay"; + hw-flow-control; +}; diff --git a/boards/nrf5340dk_nrf5340_cpunet_df.overlay b/boards/nrf5340dk_nrf5340_cpunet_df.overlay new file mode 100644 index 0000000..fdef632 --- /dev/null +++ b/boards/nrf5340dk_nrf5340_cpunet_df.overlay @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + + &uart0 { + compatible = "nordic,nrf-uarte"; + current-speed = <1000000>; + status = "okay"; + hw-flow-control; +}; + +&radio { + status = "okay"; + /* This is an example number of antennas that may be available + * on antenna matrix board. + */ + dfe-antenna-num = <10>; + /* This is an example switch pattern that will be used to set an + * antenna for Tx PDU (period before start of Tx CTE). + */ + dfe-pdu-antenna = <0x0>; + + /* These are example GPIO pin numbers that are provided to + * Radio peripheral. The pins will be acquired by Radio to + * drive antenna switching when AoD is enabled. + */ + dfegpio0-gpios = <&gpio0 4 0>; + dfegpio1-gpios = <&gpio0 5 0>; + dfegpio2-gpios = <&gpio0 6 0>; + dfegpio3-gpios = <&gpio0 7 0>; +}; diff --git a/boards/nrf5340pdk_nrf5340_cpuapp.overlay b/boards/nrf5340pdk_nrf5340_cpuapp.overlay new file mode 100644 index 0000000..6a4b799 --- /dev/null +++ b/boards/nrf5340pdk_nrf5340_cpuapp.overlay @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +&uart0 { + compatible = "nordic,nrf-uarte"; + current-speed = <1000000>; + status = "okay"; + hw-flow-control; +}; diff --git a/boards/nrf54l15dk_nrf54l15_cpuapp.overlay b/boards/nrf54l15dk_nrf54l15_cpuapp.overlay new file mode 100644 index 0000000..7613ab8 --- /dev/null +++ b/boards/nrf54l15dk_nrf54l15_cpuapp.overlay @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&uart20 { + compatible = "nordic,nrf-uarte"; + current-speed = <1000000>; + status = "okay"; + hw-flow-control; +}; diff --git a/boards/nrf54l15dk_nrf54l15_cpuapp_df.overlay b/boards/nrf54l15dk_nrf54l15_cpuapp_df.overlay new file mode 100644 index 0000000..470bfda --- /dev/null +++ b/boards/nrf54l15dk_nrf54l15_cpuapp_df.overlay @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&uart20 { + compatible = "nordic,nrf-uarte"; + current-speed = <1000000>; + status = "okay"; + hw-flow-control; +}; + +&radio { + status = "okay"; + /* This is an example number of antennas that may be available + * on antenna matrix board. + */ + dfe-antenna-num = <10>; + /* This is an example switch pattern that will be used to set an + * antenna for Tx PDU (period before start of Tx CTE). + */ + dfe-pdu-antenna = <0x0>; + + /* These are example GPIO pin numbers that are provided to + * Radio peripheral. The pins will be acquired by Radio to + * drive antenna switching when AoD is enabled. + */ + dfegpio0-gpios = <&gpio1 4 0>; + dfegpio1-gpios = <&gpio1 5 0>; + dfegpio2-gpios = <&gpio1 6 0>; + dfegpio3-gpios = <&gpio1 7 0>; +}; diff --git a/boards/nrf9160dk_nrf52840.conf b/boards/nrf9160dk_nrf52840.conf new file mode 100644 index 0000000..764798d --- /dev/null +++ b/boards/nrf9160dk_nrf52840.conf @@ -0,0 +1,4 @@ +# Override prj.conf defaults +CONFIG_CONSOLE=y +CONFIG_STDOUT_CONSOLE=y +CONFIG_UART_CONSOLE=y diff --git a/boards/nrf9160dk_nrf52840.overlay b/boards/nrf9160dk_nrf52840.overlay new file mode 100644 index 0000000..8ec7417 --- /dev/null +++ b/boards/nrf9160dk_nrf52840.overlay @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2021 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include + +&uart1 { + current-speed = <1000000>; + hw-flow-control; +}; + +/ { + chosen { + zephyr,bt-c2h-uart=&uart1; + }; +}; diff --git a/boards/nrf9160dk_nrf52840_0_14_0.overlay b/boards/nrf9160dk_nrf52840_0_14_0.overlay new file mode 100644 index 0000000..692d2a5 --- /dev/null +++ b/boards/nrf9160dk_nrf52840_0_14_0.overlay @@ -0,0 +1,8 @@ +/* + * Copyright (c) 2021 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* Use the reset line that is available starting from v0.14.0 of the DK. */ +#include diff --git a/boards/rv32m1_vega_openisa_rv32m1_ri5cy.conf b/boards/rv32m1_vega_openisa_rv32m1_ri5cy.conf new file mode 100644 index 0000000..c54e373 --- /dev/null +++ b/boards/rv32m1_vega_openisa_rv32m1_ri5cy.conf @@ -0,0 +1,7 @@ +# +# Copyright 2020, NXP +# +# SPDX-License-Identifier: Apache-2.0 +# + +CONFIG_UART_INTERRUPT_DRIVEN=y diff --git a/boards/rv32m1_vega_openisa_rv32m1_ri5cy.overlay b/boards/rv32m1_vega_openisa_rv32m1_ri5cy.overlay new file mode 100644 index 0000000..b749352 --- /dev/null +++ b/boards/rv32m1_vega_openisa_rv32m1_ri5cy.overlay @@ -0,0 +1,28 @@ +/* + * Copyright 2019 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + chosen { + zephyr,bt-c2h-uart = &lpuart0; + }; +}; + +&lptmr1 { + interrupt-parent = <&intmux0_ch2>; +}; + +&intmux0_ch2 { + status = "okay"; +}; + +&intmux0_ch3 { + status = "okay"; +}; + +&generic_fsk { + interrupt-parent = <&intmux0_ch3>; + status = "okay"; +}; diff --git a/boards/yd_esp32_procpu.conf b/boards/yd_esp32_procpu.conf new file mode 100644 index 0000000..460f012 --- /dev/null +++ b/boards/yd_esp32_procpu.conf @@ -0,0 +1,4 @@ +CONFIG_CONSOLE=y +CONFIG_STDOUT_CONSOLE=y +CONFIG_UART_CONSOLE=y +CONFIG_BT_MAX_CONN=9 diff --git a/boards/yd_esp32_procpu.overlay b/boards/yd_esp32_procpu.overlay new file mode 100644 index 0000000..c962a4a --- /dev/null +++ b/boards/yd_esp32_procpu.overlay @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + chosen { + zephyr,bt-c2h-uart = &uart1; + }; +}; + +&pinctrl { + + uart1_default: uart1_default { + group1 { + pinmux = , + , + ; + }; + group2 { + pinmux = ; + bias-pull-up; + }; + }; + +}; + +&uart1 { + status = "okay"; + current-speed = <921600>; + pinctrl-0 = <&uart1_default>; + pinctrl-names = "default"; +}; diff --git a/debug.conf b/debug.conf new file mode 100644 index 0000000..7d0c43d --- /dev/null +++ b/debug.conf @@ -0,0 +1,21 @@ +CONFIG_ASSERT=y + +CONFIG_THREAD_NAME=y +CONFIG_THREAD_ANALYZER=y +CONFIG_THREAD_ANALYZER_AUTO=y +CONFIG_THREAD_ANALYZER_RUN_UNLOCKED=y + +CONFIG_HW_STACK_PROTECTION=y + +CONFIG_CONSOLE=y +CONFIG_LOG=y +CONFIG_LOG_BUFFER_SIZE=4096 +CONFIG_RTT_CONSOLE=y +CONFIG_LOG_BACKEND_RTT=y +CONFIG_LOG_BACKEND_RTT_MODE_DROP=n +CONFIG_USE_SEGGER_RTT=y +CONFIG_SEGGER_RTT_BUFFER_SIZE_UP=4096 +CONFIG_LOG_BACKEND_SHOW_COLOR=n +CONFIG_LOG_PROCESS_THREAD_STACK_SIZE=1024 + +CONFIG_LOG_DEFAULT_LEVEL=3 diff --git a/dts/arm/nordic/override.dtsi b/dts/arm/nordic/override.dtsi new file mode 100644 index 0000000..882f707 --- /dev/null +++ b/dts/arm/nordic/override.dtsi @@ -0,0 +1,7 @@ +/* Keep default IRQ priority low for peripherals to reduce Radio ISR latency. + * ARM Cortex-M4 lowest priority value of 5, i.e. considering Zephyr reserved 2 + * levels for Exceptions and ZLI (if enabled). + * ARM Cortex-M0 lowest priority value of 3, i.e. we use it as Zephyr has no + * support for ZLI on Cortex-M0. + */ +#define NRF_DEFAULT_IRQ_PRIORITY 3 diff --git a/overlay-all-bt_ll_sw_split.conf b/overlay-all-bt_ll_sw_split.conf new file mode 100644 index 0000000..8d6dbdd --- /dev/null +++ b/overlay-all-bt_ll_sw_split.conf @@ -0,0 +1,109 @@ +CONFIG_BT_BUF_EVT_RX_COUNT=16 + +CONFIG_BT_BUF_EVT_RX_SIZE=255 +CONFIG_BT_BUF_ACL_RX_SIZE=255 +CONFIG_BT_BUF_ACL_TX_SIZE=251 +CONFIG_BT_BUF_CMD_TX_SIZE=255 + +# Host and Controller common dependencies +CONFIG_BT_EXT_ADV=y +CONFIG_BT_PER_ADV=y +CONFIG_BT_PER_ADV_SYNC=y +CONFIG_BT_PER_ADV_SYNC_MAX=2 + +# Broadcast and Connected ISO +CONFIG_BT_ISO_BROADCASTER=y +CONFIG_BT_ISO_SYNC_RECEIVER=y +CONFIG_BT_ISO_CENTRAL=y +CONFIG_BT_ISO_PERIPHERAL=y + +# ISO Streams +CONFIG_BT_ISO_MAX_CHAN=2 + +# Controller +CONFIG_BT_LL_SW_SPLIT=y +CONFIG_BT_CTLR_ASSERT_HANDLER=y +CONFIG_BT_CTLR_DTM_HCI=y + +# Rx ACL and Adv Reports +CONFIG_BT_CTLR_RX_BUFFERS=9 +CONFIG_BT_CTLR_DATA_LENGTH_MAX=251 + +# Coded PHY support +CONFIG_BT_CTLR_PHY_CODED=y + +# Advertising Sets and Extended Scanning +CONFIG_BT_CTLR_ADV_EXT=y +CONFIG_BT_CTLR_ADV_SET=3 +CONFIG_BT_CTLR_ADV_DATA_LEN_MAX=191 +CONFIG_BT_CTLR_SCAN_DATA_LEN_MAX=1650 + +CONFIG_BT_CTLR_ADVANCED_FEATURES=y +CONFIG_BT_CTLR_ADV_AUX_SET=3 +CONFIG_BT_CTLR_ADV_AUX_PDU_BACK2BACK=y +CONFIG_BT_CTLR_ADV_SYNC_SET=3 +CONFIG_BT_CTLR_ADV_SYNC_PDU_BACK2BACK=y +CONFIG_BT_CTLR_ADV_DATA_BUF_MAX=6 + +# Increase the below to receive interleaved advertising chains +CONFIG_BT_CTLR_SCAN_AUX_SET=1 + +CONFIG_BT_CTLR_ADV_RESERVE_MAX=n +CONFIG_BT_CTLR_CENTRAL_RESERVE_MAX=n +CONFIG_BT_CTLR_SLOT_RESERVATION_UPDATE=n +CONFIG_BT_CTLR_SCAN_UNRESERVED=y +CONFIG_BT_TICKER_NEXT_SLOT_GET_MATCH=y +CONFIG_BT_TICKER_EXT=y +CONFIG_BT_TICKER_EXT_SLOT_WINDOW_YIELD=y + +# Control Procedure +CONFIG_BT_CTLR_LLCP_LOCAL_PROC_CTX_BUF_NUM=6 + +# Direction Finding +CONFIG_BT_CTLR_DF=y +CONFIG_BT_CTLR_DF_PER_ADV_CTE_NUM_MAX=3 +CONFIG_BT_CTLR_DF_PER_SCAN_CTE_NUM_MAX=3 + +# Direction Finding Tx +CONFIG_BT_CTLR_DF_CTE_TX=y +CONFIG_BT_CTLR_DF_CONN_CTE_TX=y +CONFIG_BT_CTLR_DF_ANT_SWITCH_TX=y +CONFIG_BT_CTLR_DF_CONN_CTE_RSP=y + +# Direction Finding Rx +CONFIG_BT_CTLR_DF_CTE_RX=y +CONFIG_BT_CTLR_DF_CONN_CTE_RX=y +CONFIG_BT_CTLR_DF_ANT_SWITCH_RX=y +CONFIG_BT_CTLR_DF_CONN_CTE_REQ=y + +# ISO Broadcaster Controller +CONFIG_BT_CTLR_ADV_EXT=y +CONFIG_BT_CTLR_ADV_PERIODIC=y +CONFIG_BT_CTLR_ADV_ISO=y +CONFIG_BT_CTLR_ADV_ISO_PDU_LEN_MAX=247 +CONFIG_BT_CTLR_ADV_ISO_STREAM_MAX=2 + +# ISO Receive Controller +CONFIG_BT_CTLR_ADV_EXT=y +CONFIG_BT_CTLR_SYNC_PERIODIC=y +CONFIG_BT_CTLR_SYNC_ISO=y +CONFIG_BT_CTLR_SYNC_ISO_PDU_LEN_MAX=251 +CONFIG_BT_CTLR_SYNC_ISO_STREAM_MAX=2 + +# ISO Connection Oriented +CONFIG_BT_CTLR_CENTRAL_ISO=y +CONFIG_BT_CTLR_PERIPHERAL_ISO=y +CONFIG_BT_CTLR_CONN_ISO_SDU_LEN_MAX=247 +CONFIG_BT_CTLR_CONN_ISO_PDU_LEN_MAX=251 + +# ISO Transmissions +CONFIG_BT_CTLR_ISO_TX_BUFFERS=8 +CONFIG_BT_CTLR_ISO_TX_BUFFER_SIZE=255 +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/prj.conf b/prj.conf new file mode 100644 index 0000000..bdc73dd --- /dev/null +++ b/prj.conf @@ -0,0 +1,23 @@ +CONFIG_CONSOLE=n +CONFIG_STDOUT_CONSOLE=n +CONFIG_UART_CONSOLE=n +CONFIG_GPIO=y +CONFIG_SERIAL=y +CONFIG_UART_INTERRUPT_DRIVEN=y +CONFIG_BT=y +CONFIG_BT_HCI_RAW=y +CONFIG_BT_HCI_RAW_H4=y +CONFIG_BT_HCI_RAW_H4_ENABLE=y +CONFIG_BT_BUF_ACL_RX_SIZE=255 +CONFIG_BT_BUF_CMD_TX_SIZE=255 +CONFIG_BT_BUF_EVT_DISCARDABLE_SIZE=255 +CONFIG_BT_CTLR_ASSERT_HANDLER=y +CONFIG_BT_MAX_CONN=16 +CONFIG_BT_TINYCRYPT_ECC=n +CONFIG_BT_CTLR_DTM_HCI=y + +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=512 + +# Workaround: Unable to allocate command buffer when using K_NO_WAIT since +# Host number of completed commands does not follow normal flow control. +CONFIG_BT_BUF_CMD_TX_COUNT=10 diff --git a/sample.yaml b/sample.yaml new file mode 100644 index 0000000..c626906 --- /dev/null +++ b/sample.yaml @@ -0,0 +1,81 @@ +sample: + name: Bluetooth HCI UART + description: Allows Zephyr to provide Bluetooth connectivity via UART +tests: + sample.bluetooth.hci_uart.nrf5: + harness: bluetooth + platform_allow: + - nrf52dk/nrf52832 + tags: + - uart + - bluetooth + sample.bluetooth.hci_uart.nrf52833.df: + harness: bluetooth + platform_allow: nrf52833dk/nrf52833 + extra_args: + - DTC_OVERLAY_FILE=./boards/nrf52833dk_nrf52833_df.overlay + - SNIPPET="bt-ll-sw-split" + extra_configs: + - CONFIG_BT_CTLR_DF=y + tags: + - uart + - bluetooth + sample.bluetooth.hci_uart.nrf5340_netcore.df: + harness: bluetooth + platform_allow: nrf5340dk/nrf5340/cpunet + extra_args: + - DTC_OVERLAY_FILE=./boards/nrf5340dk_nrf5340_cpunet_df.overlay + - SNIPPET="bt-ll-sw-split" + extra_configs: + - CONFIG_BT_CTLR_DF=y + tags: + - uart + - bluetooth + sample.bluetooth.hci_uart.nrf52833.df.iq_report: + harness: bluetooth + platform_allow: nrf52833dk/nrf52833 + extra_args: + - DTC_OVERLAY_FILE=./boards/nrf52833dk_nrf52833_df.overlay + - SNIPPET="bt-ll-sw-split" + extra_configs: + - CONFIG_BT_CTLR_DF=y + - CONFIG_BT_CTLR_DTM_HCI_DF_IQ_REPORT=y + tags: + - uart + - bluetooth + sample.bluetooth.hci_uart.nrf5340_netcore.df.iq_report: + harness: bluetooth + platform_allow: nrf5340dk/nrf5340/cpunet + extra_args: + - DTC_OVERLAY_FILE=./boards/nrf5340dk_nrf5340_cpunet_df.overlay + - SNIPPET="bt-ll-sw-split" + extra_configs: + - CONFIG_BT_CTLR_DF=y + - CONFIG_BT_CTLR_DTM_HCI_DF_IQ_REPORT=y + tags: + - uart + - bluetooth + sample.bluetooth.hci_uart.nrf52833.all: + harness: bluetooth + platform_allow: nrf52833dk/nrf52833 + integration_platforms: + - nrf52833dk/nrf52833 + extra_args: + - OVERLAY_CONFIG=overlay-all-bt_ll_sw_split.conf + - DTC_OVERLAY_FILE=./boards/nrf52833dk_nrf52833_df.overlay + - SNIPPET="bt-ll-sw-split" + tags: + - uart + - bluetooth + sample.bluetooth.hci_uart.nrf54l15.all: + harness: bluetooth + platform_allow: nrf54l15dk/nrf54l15/cpuapp + integration_platforms: + - nrf54l15dk/nrf54l15/cpuapp + extra_args: + - OVERLAY_CONFIG=overlay-all-bt_ll_sw_split.conf + - DTC_OVERLAY_FILE=./boards/nrf54l15dk_nrf54l15_cpuapp_df.overlay + - SNIPPET="bt-ll-sw-split" + tags: + - uart + - bluetooth diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..a2baa89 --- /dev/null +++ b/src/main.c @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2016 Nordic Semiconductor ASA + * Copyright (c) 2015-2016 Intel Corporation + * + * 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 + +#define LOG_MODULE_NAME hci_uart +LOG_MODULE_REGISTER(LOG_MODULE_NAME); + +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. */ + +/* Length of a discard/flush buffer. + * This is sized to align with a BLE HCI packet: + * 1 byte H:4 header + 32 bytes ACL/event data + * Bigger values might overflow the stack since this is declared as a local + * variable, smaller ones will force the caller to call into discard more + * often. + */ +#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_H4, K_NO_WAIT, + &type, sizeof(type)); + 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, bt_buf_get_type(buf), + 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 (IS_ENABLED(CONFIG_USB_CDC_ACM)) { + if (usb_enable(NULL)) { + LOG_ERR("Failed to enable USB"); + return -EINVAL; + } + } + + 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; +} + +SYS_INIT(hci_uart_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEVICE); + +int main(void) +{ + /* incoming events and data from the controller */ + static K_FIFO_DEFINE(rx_queue); + int err; + + LOG_DBG("Start"); + __ASSERT(hci_uart_dev, "UART device is NULL"); + + /* 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"); + } + } + return 0; +}