From 74cf624f4f9e4be4dfd155826988a94ef7732d43 Mon Sep 17 00:00:00 2001 From: Lingao Meng Date: Thu, 11 Apr 2024 12:39:37 +0800 Subject: [PATCH] samples: bluetooth: Add support for hci 3wire Add support 3wire hci uart. (cherry picked from commit b50e1d5ec8e7342454c0b6527921d3ca7432d2aa) Original-Signed-off-by: Lingao Meng GitOrigin-RevId: b50e1d5ec8e7342454c0b6527921d3ca7432d2aa Change-Id: I920aba32cae497f5cab188e362277c9be448b838 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/zephyr/+/5588623 Tested-by: ChromeOS Prod (Robot) Reviewed-by: Tristan Honscheid Commit-Queue: Tristan Honscheid Tested-by: Tristan Honscheid --- .../bluetooth/hci_uart_3wire/CMakeLists.txt | 8 + samples/bluetooth/hci_uart_3wire/README.rst | 192 ++++ .../boards/96b_nitrogen.overlay | 7 + .../hci_uart_3wire/boards/bbc_microbit.conf | 8 + .../boards/bbc_microbit_v2.overlay | 7 + .../boards/nrf52833dk_nrf52833.overlay | 11 + .../boards/nrf52833dk_nrf52833_df.overlay | 32 + .../boards/nrf52840dk_nrf52840.overlay | 7 + .../boards/nrf52840dongle_nrf52840.conf | 4 + .../boards/nrf52_blenano2.overlay | 7 + .../boards/nrf52dk_nrf52832.overlay | 7 + .../boards/nrf5340dk_nrf5340_cpuapp.overlay | 7 + .../boards/nrf5340dk_nrf5340_cpunet.overlay | 7 + .../nrf5340dk_nrf5340_cpunet_df.overlay | 32 + .../boards/nrf5340pdk_nrf5340_cpuapp.overlay | 7 + .../boards/nrf9160dk_nrf52840.conf | 4 + .../boards/nrf9160dk_nrf52840.overlay | 19 + .../boards/nrf9160dk_nrf52840_0_14_0.overlay | 8 + samples/bluetooth/hci_uart_3wire/debug.conf | 21 + .../overlay-all-bt_ll_sw_split.conf | 108 +++ samples/bluetooth/hci_uart_3wire/prj.conf | 22 + samples/bluetooth/hci_uart_3wire/sample.yaml | 60 ++ samples/bluetooth/hci_uart_3wire/src/main.c | 821 ++++++++++++++++++ 23 files changed, 1406 insertions(+) create mode 100644 samples/bluetooth/hci_uart_3wire/CMakeLists.txt create mode 100644 samples/bluetooth/hci_uart_3wire/README.rst create mode 100644 samples/bluetooth/hci_uart_3wire/boards/96b_nitrogen.overlay create mode 100644 samples/bluetooth/hci_uart_3wire/boards/bbc_microbit.conf create mode 100644 samples/bluetooth/hci_uart_3wire/boards/bbc_microbit_v2.overlay create mode 100644 samples/bluetooth/hci_uart_3wire/boards/nrf52833dk_nrf52833.overlay create mode 100644 samples/bluetooth/hci_uart_3wire/boards/nrf52833dk_nrf52833_df.overlay create mode 100644 samples/bluetooth/hci_uart_3wire/boards/nrf52840dk_nrf52840.overlay create mode 100644 samples/bluetooth/hci_uart_3wire/boards/nrf52840dongle_nrf52840.conf create mode 100644 samples/bluetooth/hci_uart_3wire/boards/nrf52_blenano2.overlay create mode 100644 samples/bluetooth/hci_uart_3wire/boards/nrf52dk_nrf52832.overlay create mode 100644 samples/bluetooth/hci_uart_3wire/boards/nrf5340dk_nrf5340_cpuapp.overlay create mode 100644 samples/bluetooth/hci_uart_3wire/boards/nrf5340dk_nrf5340_cpunet.overlay create mode 100644 samples/bluetooth/hci_uart_3wire/boards/nrf5340dk_nrf5340_cpunet_df.overlay create mode 100644 samples/bluetooth/hci_uart_3wire/boards/nrf5340pdk_nrf5340_cpuapp.overlay create mode 100644 samples/bluetooth/hci_uart_3wire/boards/nrf9160dk_nrf52840.conf create mode 100644 samples/bluetooth/hci_uart_3wire/boards/nrf9160dk_nrf52840.overlay create mode 100644 samples/bluetooth/hci_uart_3wire/boards/nrf9160dk_nrf52840_0_14_0.overlay create mode 100644 samples/bluetooth/hci_uart_3wire/debug.conf create mode 100644 samples/bluetooth/hci_uart_3wire/overlay-all-bt_ll_sw_split.conf create mode 100644 samples/bluetooth/hci_uart_3wire/prj.conf create mode 100644 samples/bluetooth/hci_uart_3wire/sample.yaml create mode 100644 samples/bluetooth/hci_uart_3wire/src/main.c diff --git a/samples/bluetooth/hci_uart_3wire/CMakeLists.txt b/samples/bluetooth/hci_uart_3wire/CMakeLists.txt new file mode 100644 index 00000000000..c4f3f3fbbe8 --- /dev/null +++ b/samples/bluetooth/hci_uart_3wire/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_3wire) + +target_sources(app PRIVATE src/main.c) diff --git a/samples/bluetooth/hci_uart_3wire/README.rst b/samples/bluetooth/hci_uart_3wire/README.rst new file mode 100644 index 00000000000..2b664d499d6 --- /dev/null +++ b/samples/bluetooth/hci_uart_3wire/README.rst @@ -0,0 +1,192 @@ +.. _bluetooth-hci-uart-3wire-sample: + +Bluetooth: HCI UART 3WIRE +######################### + +Overview +********* + +Expose the Zephyr Bluetooth controller support over UART to another device/CPU +using the H:5 HCI transport protocol. + +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) disabled + +Building and Running +******************** + +This sample can be found under :zephyr_file:`samples/bluetooth/hci_uart_3wire` 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 BLE controller can also +provide a modern BLE 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 nRF52840 Development Kit: + +.. zephyr-app-commands:: + :zephyr-app: samples/bluetooth/hci_uart_3wire + :board: nrf52840dk/nrf52840 + :goals: build flash + +.. _bluetooth-hci-uart-3wire-qemu-posix: + +Using the controller with QEMU or native_sim +============================================ + +In order to use the HCI UART H:5 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 hciattach -n /dev/ttyACM0 3wire 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``. + +.. 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-3wire-bluez: + +Using the controller with BlueZ +=============================== + +In order to use the HCI UART H:5 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 hciattach -n /dev/ttyACM0 3wire 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_3wire -- -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 BLE 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_3wire -b nrf52833dk/nrf52833@df -- -DCONFIG_BT_CTLR_DF=y + +You can use following targets: + +* ``nrf5340dk/nrf5340/cpunet@df`` +* ``nrf52833dk/nrf52833@df`` + +Check the :ref:`bluetooth_direction_finding_connectionless_rx` and the :ref:`bluetooth_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_3wire/boards/nrf52840dongle_nrf52840.conf` and :zephyr_file:`samples/bluetooth/hci_uart_3wire/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 H5 +driver instead of the built-in controller: + +.. code-block:: kconfig + + CONFIG_BT_HCI=y + CONFIG_BT_CTLR=n + CONFIG_BT_H5=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_3wire/boards/96b_nitrogen.overlay b/samples/bluetooth/hci_uart_3wire/boards/96b_nitrogen.overlay new file mode 100644 index 00000000000..f3050da1747 --- /dev/null +++ b/samples/bluetooth/hci_uart_3wire/boards/96b_nitrogen.overlay @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +&uart0 { + compatible = "nordic,nrf-uart"; + current-speed = <1000000>; + status = "okay"; +}; diff --git a/samples/bluetooth/hci_uart_3wire/boards/bbc_microbit.conf b/samples/bluetooth/hci_uart_3wire/boards/bbc_microbit.conf new file mode 100644 index 00000000000..4ec62e167cc --- /dev/null +++ b/samples/bluetooth/hci_uart_3wire/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/samples/bluetooth/hci_uart_3wire/boards/bbc_microbit_v2.overlay b/samples/bluetooth/hci_uart_3wire/boards/bbc_microbit_v2.overlay new file mode 100644 index 00000000000..f3050da1747 --- /dev/null +++ b/samples/bluetooth/hci_uart_3wire/boards/bbc_microbit_v2.overlay @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +&uart0 { + compatible = "nordic,nrf-uart"; + current-speed = <1000000>; + status = "okay"; +}; diff --git a/samples/bluetooth/hci_uart_3wire/boards/nrf52833dk_nrf52833.overlay b/samples/bluetooth/hci_uart_3wire/boards/nrf52833dk_nrf52833.overlay new file mode 100644 index 00000000000..4763dc348cc --- /dev/null +++ b/samples/bluetooth/hci_uart_3wire/boards/nrf52833dk_nrf52833.overlay @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + + &uart0 { + compatible = "nordic,nrf-uarte"; + current-speed = <1000000>; + status = "okay"; +}; diff --git a/samples/bluetooth/hci_uart_3wire/boards/nrf52833dk_nrf52833_df.overlay b/samples/bluetooth/hci_uart_3wire/boards/nrf52833dk_nrf52833_df.overlay new file mode 100644 index 00000000000..5bfb3a2a4b1 --- /dev/null +++ b/samples/bluetooth/hci_uart_3wire/boards/nrf52833dk_nrf52833_df.overlay @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + + &uart0 { + compatible = "nordic,nrf-uarte"; + current-speed = <1000000>; + status = "okay"; +}; + +&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/samples/bluetooth/hci_uart_3wire/boards/nrf52840dk_nrf52840.overlay b/samples/bluetooth/hci_uart_3wire/boards/nrf52840dk_nrf52840.overlay new file mode 100644 index 00000000000..f3050da1747 --- /dev/null +++ b/samples/bluetooth/hci_uart_3wire/boards/nrf52840dk_nrf52840.overlay @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +&uart0 { + compatible = "nordic,nrf-uart"; + current-speed = <1000000>; + status = "okay"; +}; diff --git a/samples/bluetooth/hci_uart_3wire/boards/nrf52840dongle_nrf52840.conf b/samples/bluetooth/hci_uart_3wire/boards/nrf52840dongle_nrf52840.conf new file mode 100644 index 00000000000..f87530dd3c2 --- /dev/null +++ b/samples/bluetooth/hci_uart_3wire/boards/nrf52840dongle_nrf52840.conf @@ -0,0 +1,4 @@ +CONFIG_USB_DEVICE_STACK=y +CONFIG_USB_DEVICE_PRODUCT="Zephyr HCI UART 3Wire sample" +CONFIG_USB_CDC_ACM=y +CONFIG_USB_DEVICE_INITIALIZE_AT_BOOT=n diff --git a/samples/bluetooth/hci_uart_3wire/boards/nrf52_blenano2.overlay b/samples/bluetooth/hci_uart_3wire/boards/nrf52_blenano2.overlay new file mode 100644 index 00000000000..f3050da1747 --- /dev/null +++ b/samples/bluetooth/hci_uart_3wire/boards/nrf52_blenano2.overlay @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +&uart0 { + compatible = "nordic,nrf-uart"; + current-speed = <1000000>; + status = "okay"; +}; diff --git a/samples/bluetooth/hci_uart_3wire/boards/nrf52dk_nrf52832.overlay b/samples/bluetooth/hci_uart_3wire/boards/nrf52dk_nrf52832.overlay new file mode 100644 index 00000000000..70a51fae78b --- /dev/null +++ b/samples/bluetooth/hci_uart_3wire/boards/nrf52dk_nrf52832.overlay @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +&uart0 { + compatible = "nordic,nrf-uart"; + current-speed = <115200>; + status = "okay"; +}; diff --git a/samples/bluetooth/hci_uart_3wire/boards/nrf5340dk_nrf5340_cpuapp.overlay b/samples/bluetooth/hci_uart_3wire/boards/nrf5340dk_nrf5340_cpuapp.overlay new file mode 100644 index 00000000000..69855b64d7b --- /dev/null +++ b/samples/bluetooth/hci_uart_3wire/boards/nrf5340dk_nrf5340_cpuapp.overlay @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +&uart0 { + compatible = "nordic,nrf-uarte"; + current-speed = <1000000>; + status = "okay"; +}; diff --git a/samples/bluetooth/hci_uart_3wire/boards/nrf5340dk_nrf5340_cpunet.overlay b/samples/bluetooth/hci_uart_3wire/boards/nrf5340dk_nrf5340_cpunet.overlay new file mode 100644 index 00000000000..69855b64d7b --- /dev/null +++ b/samples/bluetooth/hci_uart_3wire/boards/nrf5340dk_nrf5340_cpunet.overlay @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +&uart0 { + compatible = "nordic,nrf-uarte"; + current-speed = <1000000>; + status = "okay"; +}; diff --git a/samples/bluetooth/hci_uart_3wire/boards/nrf5340dk_nrf5340_cpunet_df.overlay b/samples/bluetooth/hci_uart_3wire/boards/nrf5340dk_nrf5340_cpunet_df.overlay new file mode 100644 index 00000000000..b065708f0b6 --- /dev/null +++ b/samples/bluetooth/hci_uart_3wire/boards/nrf5340dk_nrf5340_cpunet_df.overlay @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + + &uart0 { + compatible = "nordic,nrf-uarte"; + current-speed = <1000000>; + status = "okay"; +}; + +&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/samples/bluetooth/hci_uart_3wire/boards/nrf5340pdk_nrf5340_cpuapp.overlay b/samples/bluetooth/hci_uart_3wire/boards/nrf5340pdk_nrf5340_cpuapp.overlay new file mode 100644 index 00000000000..69855b64d7b --- /dev/null +++ b/samples/bluetooth/hci_uart_3wire/boards/nrf5340pdk_nrf5340_cpuapp.overlay @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +&uart0 { + compatible = "nordic,nrf-uarte"; + current-speed = <1000000>; + status = "okay"; +}; diff --git a/samples/bluetooth/hci_uart_3wire/boards/nrf9160dk_nrf52840.conf b/samples/bluetooth/hci_uart_3wire/boards/nrf9160dk_nrf52840.conf new file mode 100644 index 00000000000..764798d6ef1 --- /dev/null +++ b/samples/bluetooth/hci_uart_3wire/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/samples/bluetooth/hci_uart_3wire/boards/nrf9160dk_nrf52840.overlay b/samples/bluetooth/hci_uart_3wire/boards/nrf9160dk_nrf52840.overlay new file mode 100644 index 00000000000..b8ad454a58a --- /dev/null +++ b/samples/bluetooth/hci_uart_3wire/boards/nrf9160dk_nrf52840.overlay @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include + +&uart1 { + current-speed = <1000000>; +}; + +/ { + chosen { + zephyr,bt-c2h-uart=&uart1; + }; +}; diff --git a/samples/bluetooth/hci_uart_3wire/boards/nrf9160dk_nrf52840_0_14_0.overlay b/samples/bluetooth/hci_uart_3wire/boards/nrf9160dk_nrf52840_0_14_0.overlay new file mode 100644 index 00000000000..409d3bada63 --- /dev/null +++ b/samples/bluetooth/hci_uart_3wire/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/samples/bluetooth/hci_uart_3wire/debug.conf b/samples/bluetooth/hci_uart_3wire/debug.conf new file mode 100644 index 00000000000..7d0c43dab62 --- /dev/null +++ b/samples/bluetooth/hci_uart_3wire/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/samples/bluetooth/hci_uart_3wire/overlay-all-bt_ll_sw_split.conf b/samples/bluetooth/hci_uart_3wire/overlay-all-bt_ll_sw_split.conf new file mode 100644 index 00000000000..75542eff670 --- /dev/null +++ b/samples/bluetooth/hci_uart_3wire/overlay-all-bt_ll_sw_split.conf @@ -0,0 +1,108 @@ +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_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=251 +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=251 +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=251 +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_3wire/prj.conf b/samples/bluetooth/hci_uart_3wire/prj.conf new file mode 100644 index 00000000000..02f16a24138 --- /dev/null +++ b/samples/bluetooth/hci_uart_3wire/prj.conf @@ -0,0 +1,22 @@ +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_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/samples/bluetooth/hci_uart_3wire/sample.yaml b/samples/bluetooth/hci_uart_3wire/sample.yaml new file mode 100644 index 00000000000..19e799cf853 --- /dev/null +++ b/samples/bluetooth/hci_uart_3wire/sample.yaml @@ -0,0 +1,60 @@ +sample: + name: Bluetooth HCI UART 3Wire + description: Allows Zephyr to provide Bluetooth connectivity via UART 3Wire +tests: + sample.bluetooth.hci_uart_3wire.nrf5: + harness: bluetooth + platform_allow: + - nrf52dk/nrf52832 + tags: + - uart + - bluetooth + sample.bluetooth.hci_uart_3wire.nrf52833.df: + harness: bluetooth + platform_allow: nrf52833dk/nrf52833 + extra_args: DTC_OVERLAY_FILE=./boards/nrf52833dk_nrf52833_df.overlay + extra_configs: + - CONFIG_BT_CTLR_DF=y + tags: + - uart + - bluetooth + sample.bluetooth.hci_uart_3wire.nrf5340_netcore.df: + harness: bluetooth + platform_allow: nrf5340dk/nrf5340/cpunet + extra_args: DTC_OVERLAY_FILE=./boards/nrf5340dk_nrf5340_cpunet_df.overlay + extra_configs: + - CONFIG_BT_CTLR_DF=y + tags: + - uart + - bluetooth + sample.bluetooth.hci_uart_3wire.nrf52833.df.iq_report: + harness: bluetooth + platform_allow: nrf52833dk/nrf52833 + extra_args: DTC_OVERLAY_FILE=./boards/nrf52833dk_nrf52833_df.overlay + extra_configs: + - CONFIG_BT_CTLR_DF=y + - CONFIG_BT_CTLR_DTM_HCI_DF_IQ_REPORT=y + tags: + - uart + - bluetooth + sample.bluetooth.hci_uart_3wire.nrf5340_netcore.df.iq_report: + harness: bluetooth + platform_allow: nrf5340dk/nrf5340/cpunet + extra_args: DTC_OVERLAY_FILE=./boards/nrf5340dk_nrf5340_cpunet_df.overlay + extra_configs: + - CONFIG_BT_CTLR_DF=y + - CONFIG_BT_CTLR_DTM_HCI_DF_IQ_REPORT=y + tags: + - uart + - bluetooth + sample.bluetooth.hci_uart_3wire.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 + tags: + - uart + - bluetooth diff --git a/samples/bluetooth/hci_uart_3wire/src/main.c b/samples/bluetooth/hci_uart_3wire/src/main.c new file mode 100644 index 00000000000..9c8a66e6a3c --- /dev/null +++ b/samples/bluetooth/hci_uart_3wire/src/main.c @@ -0,0 +1,821 @@ +/* + * Copyright (c) 2024 Xiaomi Coopration + * 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 +#include + +#define LOG_MODULE_NAME hci_uart_3wire +LOG_MODULE_REGISTER(LOG_MODULE_NAME); + +static K_KERNEL_STACK_DEFINE(tx_stack, CONFIG_BT_HCI_TX_STACK_SIZE); +static K_KERNEL_STACK_DEFINE(rx_stack, CONFIG_BT_RX_STACK_SIZE); + +static struct k_thread tx_thread_data; +static struct k_thread rx_thread_data; + +static struct k_work_delayable ack_work; +static struct k_work_delayable retx_work; + +#define HCI_3WIRE_ACK_PKT 0x00 +#define HCI_COMMAND_PKT 0x01 +#define HCI_ACLDATA_PKT 0x02 +#define HCI_SCODATA_PKT 0x03 +#define HCI_EVENT_PKT 0x04 +#define HCI_ISODATA_PKT 0x05 +#define HCI_3WIRE_LINK_PKT 0x0f +#define HCI_VENDOR_PKT 0xff + +static bool reliable_packet(uint8_t type) +{ + switch (type) { + case HCI_COMMAND_PKT: + case HCI_ACLDATA_PKT: + case HCI_EVENT_PKT: + case HCI_ISODATA_PKT: + return true; + default: + return false; + } +} + +/* FIXME: Correct timeout */ +#define H5_RX_ACK_TIMEOUT K_MSEC(250) +#define H5_TX_ACK_TIMEOUT K_MSEC(250) + +#define SLIP_DELIMITER 0xc0 +#define SLIP_ESC 0xdb +#define SLIP_ESC_DELIM 0xdc +#define SLIP_ESC_ESC 0xdd + +#define H5_RX_ESC 1 +#define H5_TX_ACK_PEND 2 + +#define H5_HDR_SEQ(hdr) ((hdr)[0] & 0x07) +#define H5_HDR_ACK(hdr) (((hdr)[0] >> 3) & 0x07) +#define H5_HDR_CRC(hdr) (((hdr)[0] >> 6) & 0x01) +#define H5_HDR_RELIABLE(hdr) (((hdr)[0] >> 7) & 0x01) +#define H5_HDR_PKT_TYPE(hdr) ((hdr)[1] & 0x0f) +#define H5_HDR_LEN(hdr) ((((hdr)[1] >> 4) & 0x0f) + ((hdr)[2] << 4)) + +#define H5_SET_SEQ(hdr, seq) ((hdr)[0] |= (seq)) +#define H5_SET_ACK(hdr, ack) ((hdr)[0] |= (ack) << 3) +#define H5_SET_RELIABLE(hdr) ((hdr)[0] |= 1 << 7) +#define H5_SET_TYPE(hdr, type) ((hdr)[1] |= type) +#define H5_SET_LEN(hdr, len) (((hdr)[1] |= ((len) & 0x0f) << 4), \ + ((hdr)[2] |= (len) >> 4)) + +#define H5_TX_WIN 4 + +static struct h5 { + struct net_buf *rx_buf; + + struct k_fifo tx_queue; + struct k_fifo rx_queue; + struct k_fifo unack_queue; + + uint8_t tx_win; + uint8_t tx_ack; + uint8_t tx_seq; + + uint8_t rx_ack; + + enum { + UNINIT, + INIT, + ACTIVE, + } link_state; + + enum { + START, + HEADER, + PAYLOAD, + END, + } rx_state; +} h5; + +static uint8_t unack_queue_len; + +static const uint8_t sync_req[] = { 0x01, 0x7e }; +static const uint8_t sync_rsp[] = { 0x02, 0x7d }; +/* Third byte may change */ +static const uint8_t conf_req[] = { 0x03, 0xfc }; +static uint8_t conf_rsp[3] = { 0x04, 0x7b,}; + +/* H5 signal buffers pool */ +#define MAX_SIG_LEN 3 +#define SIGNAL_COUNT 2 +#define SIG_BUF_SIZE (BT_BUF_RESERVE + MAX_SIG_LEN) +NET_BUF_POOL_DEFINE(h5_pool, SIGNAL_COUNT, SIG_BUF_SIZE, 0, NULL); + +static const struct device *const h5_dev = + DEVICE_DT_GET(DT_CHOSEN(zephyr_bt_c2h_uart)); + +static K_FIFO_DEFINE(tx_queue); + +static struct k_poll_signal tx_queue_change = + K_POLL_SIGNAL_INITIALIZER(tx_queue_change); + +static void h5_reset_rx(void) +{ + if (h5.rx_buf) { + net_buf_unref(h5.rx_buf); + h5.rx_buf = NULL; + } + + h5.rx_state = START; +} + +static int h5_unslip_byte(uint8_t *byte) +{ + int count; + + if (*byte != SLIP_ESC) { + return 0; + } + + do { + count = uart_fifo_read(h5_dev, byte, sizeof(*byte)); + } while (!count); + + switch (*byte) { + case SLIP_ESC_DELIM: + *byte = SLIP_DELIMITER; + break; + case SLIP_ESC_ESC: + *byte = SLIP_ESC; + break; + default: + LOG_ERR("Invalid escape byte %x\n", *byte); + return -EIO; + } + + return 0; +} + +static void process_unack(void) +{ + uint8_t next_seq = h5.tx_seq; + uint8_t number_removed = unack_queue_len; + + if (!unack_queue_len) { + return; + } + + LOG_DBG("rx_ack %u tx_ack %u tx_seq %u unack_queue_len %u", h5.rx_ack, h5.tx_ack, h5.tx_seq, + unack_queue_len); + + while (unack_queue_len > 0) { + if (next_seq == h5.rx_ack) { + /* Next sequence number is the same as last received + * ack number + */ + break; + } + + number_removed--; + /* Similar to (n - 1) % 8 with unsigned conversion */ + next_seq = (next_seq - 1) & 0x07; + } + + if (next_seq != h5.rx_ack) { + LOG_ERR("Wrong sequence: rx_ack %u tx_seq %u next_seq %u", h5.rx_ack, h5.tx_seq, + next_seq); + } + + LOG_DBG("Need to remove %u packet from the queue", number_removed); + + while (number_removed) { + struct net_buf *buf = net_buf_get(&h5.unack_queue, K_NO_WAIT); + + if (!buf) { + LOG_ERR("Unack queue is empty"); + break; + } + + /* TODO: print or do something with packet */ + LOG_DBG("Remove buf from the unack_queue"); + + net_buf_unref(buf); + unack_queue_len--; + number_removed--; + } +} + +static void h5_print_header(const uint8_t *hdr, const char *str) +{ + if (H5_HDR_RELIABLE(hdr)) { + LOG_DBG("%s REL: seq %u ack %u crc %u type %u len %u", str, H5_HDR_SEQ(hdr), + H5_HDR_ACK(hdr), H5_HDR_CRC(hdr), H5_HDR_PKT_TYPE(hdr), H5_HDR_LEN(hdr)); + } else { + LOG_DBG("%s UNREL: ack %u crc %u type %u len %u", str, H5_HDR_ACK(hdr), + H5_HDR_CRC(hdr), H5_HDR_PKT_TYPE(hdr), H5_HDR_LEN(hdr)); + } +} + +static void hexdump(const char *str, const uint8_t *packet, size_t length) +{ + int n = 0; + + if (!length) { + printk("%s zero-length signal packet\n", str); + return; + } + + while (length--) { + if (n % 16 == 0) { + printk("%s %08X ", str, n); + } + + printk("%02X ", *packet++); + + n++; + if (n % 8 == 0) { + if (n % 16 == 0) { + printk("\n"); + } else { + printk(" "); + } + } + } + + if (n % 16) { + printk("\n"); + } +} + +static uint8_t h5_slip_byte(uint8_t byte) +{ + switch (byte) { + case SLIP_DELIMITER: + uart_poll_out(h5_dev, SLIP_ESC); + uart_poll_out(h5_dev, SLIP_ESC_DELIM); + return 2; + case SLIP_ESC: + uart_poll_out(h5_dev, SLIP_ESC); + uart_poll_out(h5_dev, SLIP_ESC_ESC); + return 2; + default: + uart_poll_out(h5_dev, byte); + return 1; + } +} + +static void h5_send(const uint8_t *payload, uint8_t type, int len) +{ + uint8_t hdr[4]; + int i; + + hexdump("<= ", payload, len); + + (void)memset(hdr, 0, sizeof(hdr)); + + /* Set ACK for outgoing packet and stop delayed work */ + H5_SET_ACK(hdr, h5.tx_ack); + /* If cancel fails we may ack the same seq number twice, this is OK. */ + (void)k_work_cancel_delayable(&ack_work); + + if (reliable_packet(type)) { + H5_SET_RELIABLE(hdr); + H5_SET_SEQ(hdr, h5.tx_seq); + h5.tx_seq = (h5.tx_seq + 1) % 8; + } + + H5_SET_TYPE(hdr, type); + H5_SET_LEN(hdr, len); + + /* Calculate CRC */ + hdr[3] = ~((hdr[0] + hdr[1] + hdr[2]) & 0xff); + + h5_print_header(hdr, "TX: <"); + + uart_poll_out(h5_dev, SLIP_DELIMITER); + + for (i = 0; i < 4; i++) { + h5_slip_byte(hdr[i]); + } + + for (i = 0; i < len; i++) { + h5_slip_byte(payload[i]); + } + + uart_poll_out(h5_dev, SLIP_DELIMITER); +} + +/* Delayed work taking care about retransmitting packets */ +static void retx_timeout(struct k_work *work) +{ + ARG_UNUSED(work); + + LOG_DBG("unack_queue_len %u", unack_queue_len); + + if (unack_queue_len) { + struct k_fifo tmp_queue; + struct net_buf *buf; + + k_fifo_init(&tmp_queue); + + /* Queue to temporary queue */ + while ((buf = net_buf_get(&h5.tx_queue, K_NO_WAIT))) { + net_buf_put(&tmp_queue, buf); + } + + /* Queue unack packets to the beginning of the queue */ + while ((buf = net_buf_get(&h5.unack_queue, K_NO_WAIT))) { + /* include also packet type */ + net_buf_push(buf, sizeof(uint8_t)); + net_buf_put(&h5.tx_queue, buf); + h5.tx_seq = (h5.tx_seq - 1) & 0x07; + unack_queue_len--; + } + + /* Queue saved packets from temp queue */ + while ((buf = net_buf_get(&tmp_queue, K_NO_WAIT))) { + net_buf_put(&h5.tx_queue, buf); + } + } + + k_poll_signal_raise(&tx_queue_change, 0); +} + +static void ack_timeout(struct k_work *work) +{ + ARG_UNUSED(work); + + LOG_DBG(""); + + h5_send(NULL, HCI_3WIRE_ACK_PKT, 0); +} + +static void h5_process_complete_packet(uint8_t *hdr) +{ + struct net_buf *buf; + + LOG_DBG(""); + + /* rx_ack should be in every packet */ + h5.rx_ack = H5_HDR_ACK(hdr); + + if (reliable_packet(H5_HDR_PKT_TYPE(hdr))) { + /* For reliable packet increment next transmit ack number */ + h5.tx_ack = (h5.tx_ack + 1) % 8; + /* Submit delayed work to ack the packet */ + k_work_reschedule(&ack_work, H5_RX_ACK_TIMEOUT); + } + + h5_print_header(hdr, "RX: >"); + + process_unack(); + + buf = h5.rx_buf; + h5.rx_buf = NULL; + + switch (H5_HDR_PKT_TYPE(hdr)) { + case HCI_3WIRE_ACK_PKT: + net_buf_unref(buf); + break; + case HCI_3WIRE_LINK_PKT: + net_buf_put(&h5.rx_queue, buf); + break; + case HCI_COMMAND_PKT: + case HCI_ACLDATA_PKT: + case HCI_ISODATA_PKT: + hexdump("=> ", buf->data, buf->len); + net_buf_put(&tx_queue, buf); + break; + } +} + +static void bt_uart_isr(const struct device *unused, void *user_data) +{ + static int remaining; + uint8_t byte, type; + int ret; + static uint8_t hdr[4]; + size_t buf_tailroom; + + ARG_UNUSED(unused); + ARG_UNUSED(user_data); + + while (uart_irq_update(h5_dev) && + uart_irq_is_pending(h5_dev)) { + + if (!uart_irq_rx_ready(h5_dev)) { + if (uart_irq_tx_ready(h5_dev)) { + LOG_DBG("transmit ready"); + } else { + LOG_DBG("spurious interrupt"); + } + /* Only the UART RX path is interrupt-enabled */ + break; + } + + ret = uart_fifo_read(h5_dev, &byte, sizeof(byte)); + if (!ret) { + continue; + } + + switch (h5.rx_state) { + case START: + if (byte == SLIP_DELIMITER) { + h5.rx_state = HEADER; + remaining = sizeof(hdr); + } + break; + case HEADER: + /* In a case we confuse ending slip delimiter + * with starting one. + */ + if (byte == SLIP_DELIMITER) { + remaining = sizeof(hdr); + continue; + } + + if (h5_unslip_byte(&byte) < 0) { + h5_reset_rx(); + continue; + } + + memcpy(&hdr[sizeof(hdr) - remaining], &byte, 1); + remaining--; + + if (remaining) { + break; + } + + remaining = H5_HDR_LEN(hdr); + type = H5_HDR_PKT_TYPE(hdr); + + switch (type) { + case HCI_COMMAND_PKT: + case HCI_ACLDATA_PKT: + case HCI_ISODATA_PKT: + h5.rx_buf = bt_buf_get_tx(BT_BUF_H4, K_NO_WAIT, + &type, sizeof(type)); + if (!h5.rx_buf) { + LOG_WRN("No available data buffers"); + h5_reset_rx(); + continue; + } + + h5.rx_state = PAYLOAD; + break; + case HCI_3WIRE_LINK_PKT: + case HCI_3WIRE_ACK_PKT: + h5.rx_buf = net_buf_alloc(&h5_pool, K_NO_WAIT); + if (!h5.rx_buf) { + LOG_WRN("No available signal buffers"); + h5_reset_rx(); + continue; + } + + h5.rx_state = PAYLOAD; + break; + default: + LOG_ERR("Wrong packet type %u", type); + h5.rx_state = END; + break; + } + if (!remaining) { + h5.rx_state = END; + } + break; + case PAYLOAD: + if (byte == SLIP_DELIMITER) { + LOG_WRN("Unexpected ending delimiter"); + h5_reset_rx(); + continue; + } + + if (h5_unslip_byte(&byte) < 0) { + h5_reset_rx(); + continue; + } + + buf_tailroom = net_buf_tailroom(h5.rx_buf); + if (buf_tailroom < sizeof(byte)) { + LOG_ERR("Not enough space in buffer %zu/%zu", sizeof(byte), + buf_tailroom); + h5_reset_rx(); + break; + } + + net_buf_add_mem(h5.rx_buf, &byte, sizeof(byte)); + remaining--; + if (!remaining) { + h5.rx_state = END; + } + break; + case END: + if (byte != SLIP_DELIMITER) { + LOG_ERR("Missing ending SLIP_DELIMITER"); + h5_reset_rx(); + break; + } + + LOG_DBG("Received full packet: type %u", H5_HDR_PKT_TYPE(hdr)); + + /* Check when full packet is received, it can be done + * when parsing packet header but we need to receive + * full packet anyway to clear UART. + */ + if (H5_HDR_RELIABLE(hdr) && + H5_HDR_SEQ(hdr) != h5.tx_ack) { + LOG_ERR("Seq expected %u got %u. Drop packet", h5.tx_ack, + H5_HDR_SEQ(hdr)); + h5_reset_rx(); + break; + } + + h5_process_complete_packet(hdr); + h5.rx_state = START; + break; + } + } +} + +static int h5_queue(struct net_buf *buf) +{ + LOG_DBG("buf %p type %u len %u", buf, bt_buf_get_type(buf), buf->len); + + net_buf_put(&h5.tx_queue, buf); + + return 0; +} + +static uint8_t h5_get_type(struct net_buf *buf) +{ + return net_buf_pull_u8(buf); +} + +static void process_events(struct k_poll_event *ev, int count) +{ + struct net_buf *buf; + uint8_t type; + int err; + + LOG_DBG("count %d", count); + + for (; count; ev++, count--) { + LOG_DBG("ev->state %u", ev->state); + + switch (ev->state) { + case K_POLL_STATE_SIGNALED: + break; + case K_POLL_STATE_SEM_AVAILABLE: + /* After this fn is exec'd, `bt_conn_prepare_events()` + * will be called once again, and this time buffers will + * be available, so the FIFO will be added to the poll + * list instead of the ctlr buffers semaphore. + */ + break; + case K_POLL_STATE_FIFO_DATA_AVAILABLE: + if (ev->tag == 0) { + /* Wait until a buffer is available */ + buf = net_buf_get(&tx_queue, K_NO_WAIT); + __ASSERT_NO_MSG(buf); + + /* Pass buffer to the stack */ + err = bt_send(buf); + if (err) { + LOG_ERR("Unable to send (err %d)", err); + net_buf_unref(buf); + } + } else if (ev->tag == 2) { + buf = net_buf_get(&h5.tx_queue, K_FOREVER); + __ASSERT_NO_MSG(buf); + + type = h5_get_type(buf); + h5_send(buf->data, type, buf->len); + + /* buf is dequeued from tx_queue and queued to unack + * queue. + */ + net_buf_put(&h5.unack_queue, buf); + unack_queue_len++; + + k_work_reschedule(&retx_work, H5_TX_ACK_TIMEOUT); + } + break; + case K_POLL_STATE_NOT_READY: + break; + default: + LOG_WRN("Unexpected k_poll event state %u", ev->state); + break; + } + } +} + +static void tx_thread(void *p1, void *p2, void *p3) +{ + static struct k_poll_event events[] = { + K_POLL_EVENT_STATIC_INITIALIZER(K_POLL_TYPE_FIFO_DATA_AVAILABLE, + K_POLL_MODE_NOTIFY_ONLY, + &tx_queue, 0), + K_POLL_EVENT_STATIC_INITIALIZER(K_POLL_TYPE_SIGNAL, + K_POLL_MODE_NOTIFY_ONLY, + &tx_queue_change, 1), + K_POLL_EVENT_STATIC_INITIALIZER(K_POLL_TYPE_FIFO_DATA_AVAILABLE, + K_POLL_MODE_NOTIFY_ONLY, + &h5.tx_queue, 2), + }; + + ARG_UNUSED(p1); + ARG_UNUSED(p2); + ARG_UNUSED(p3); + + LOG_DBG(""); + + while (true) { + int err, ev_count = 2; + + events[0].state = K_POLL_STATE_NOT_READY; + events[1].state = K_POLL_STATE_NOT_READY; + tx_queue_change.signaled = 0U; + + if (h5.link_state == ACTIVE && unack_queue_len < h5.tx_win) { + events[2].state = K_POLL_STATE_NOT_READY; + ev_count++; + } + + err = k_poll(events, ev_count, K_FOREVER); + process_events(events, ev_count); + + /* Make sure we don't hog the CPU if there's all the time + * some ready events. + */ + k_yield(); + } +} + +static void rx_thread(void *p1, void *p2, void *p3) +{ + ARG_UNUSED(p1); + ARG_UNUSED(p2); + ARG_UNUSED(p3); + + LOG_DBG(""); + + while (true) { + struct net_buf *buf, *cache; + + buf = net_buf_get(&h5.rx_queue, K_FOREVER); + + hexdump("=> ", buf->data, buf->len); + + if (!memcmp(buf->data, sync_req, sizeof(sync_req))) { + if (h5.link_state == ACTIVE) { + while ((cache = net_buf_get(&h5.unack_queue, K_NO_WAIT))) { + net_buf_unref(cache); + } + + unack_queue_len = 0; + + while ((cache = net_buf_get(&h5.tx_queue, K_NO_WAIT))) { + net_buf_unref(cache); + } + + h5_reset_rx(); + + h5.rx_ack = 0; + h5.link_state = INIT; + h5.tx_ack = 0; + h5.tx_seq = 0; + } + + h5_send(sync_rsp, HCI_3WIRE_LINK_PKT, sizeof(sync_rsp)); + } else if (!memcmp(buf->data, conf_req, 2)) { + if (buf->len > 2) { + uint8_t tx_win = buf->data[2] & 0x07; + + /* Configuration field present */ + h5.tx_win = MIN(h5.tx_win, tx_win); + } + + conf_rsp[2] = h5.tx_win; + + /* + * The Host sends Config Response messages with a + * Configuration Field. + */ + h5_send(conf_rsp, HCI_3WIRE_LINK_PKT, sizeof(conf_rsp)); + + LOG_DBG("Finished H5 configuration, tx_win %u", h5.tx_win); + + h5.link_state = ACTIVE; + } else { + LOG_ERR("Not handled yet %x %x", buf->data[0], buf->data[1]); + } + + net_buf_unref(buf); + + /* Make sure we don't hog the CPU if the rx_queue never + * gets empty. + */ + k_yield(); + } +} + +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(h5_dev)) { + LOG_ERR("HCI UART %s is not ready", h5_dev->name); + return -EINVAL; + } + + uart_irq_rx_disable(h5_dev); + uart_irq_tx_disable(h5_dev); + + uart_irq_callback_set(h5_dev, bt_uart_isr); + + uart_irq_rx_enable(h5_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(h5_dev, "UART device is NULL"); + + /* Enable the raw interface, this will in turn open the HCI driver */ + bt_enable_raw(&rx_queue); + + /* TX thread */ + k_fifo_init(&h5.tx_queue); + k_thread_create(&tx_thread_data, tx_stack, + K_KERNEL_STACK_SIZEOF(tx_stack), + tx_thread, NULL, NULL, NULL, + K_PRIO_COOP(CONFIG_BT_HCI_TX_PRIO), + 0, K_NO_WAIT); + k_thread_name_set(&tx_thread_data, "tx_thread"); + + k_fifo_init(&h5.rx_queue); + k_thread_create(&rx_thread_data, rx_stack, + K_KERNEL_STACK_SIZEOF(rx_stack), + rx_thread, NULL, NULL, NULL, + K_PRIO_COOP(CONFIG_BT_RX_PRIO), + 0, K_NO_WAIT); + k_thread_name_set(&rx_thread_data, "rx_thread"); + + /* Unack queue */ + k_fifo_init(&h5.unack_queue); + + /* Init delayed work */ + k_work_init_delayable(&ack_work, ack_timeout); + k_work_init_delayable(&retx_work, retx_timeout); + + h5.tx_win = H5_TX_WIN; + + while (1) { + struct net_buf *buf; + + buf = net_buf_get(&rx_queue, K_FOREVER); + err = h5_queue(buf); + if (err) { + LOG_ERR("Failed to send"); + } + } + + return 0; +}