samples: bluetooth: Add support for hci 3wire
Add support 3wire hci uart. Signed-off-by: Lingao Meng <menglingao@xiaomi.com>
This commit is contained in:
parent
adfa3346c2
commit
b50e1d5ec8
23 changed files with 1406 additions and 0 deletions
8
samples/bluetooth/hci_uart_3wire/CMakeLists.txt
Normal file
8
samples/bluetooth/hci_uart_3wire/CMakeLists.txt
Normal file
|
@ -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)
|
192
samples/bluetooth/hci_uart_3wire/README.rst
Normal file
192
samples/bluetooth/hci_uart_3wire/README.rst
Normal file
|
@ -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 <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 <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;
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,7 @@
|
||||||
|
/* SPDX-License-Identifier: Apache-2.0 */
|
||||||
|
|
||||||
|
&uart0 {
|
||||||
|
compatible = "nordic,nrf-uart";
|
||||||
|
current-speed = <1000000>;
|
||||||
|
status = "okay";
|
||||||
|
};
|
|
@ -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
|
|
@ -0,0 +1,7 @@
|
||||||
|
/* SPDX-License-Identifier: Apache-2.0 */
|
||||||
|
|
||||||
|
&uart0 {
|
||||||
|
compatible = "nordic,nrf-uart";
|
||||||
|
current-speed = <1000000>;
|
||||||
|
status = "okay";
|
||||||
|
};
|
|
@ -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";
|
||||||
|
};
|
|
@ -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>;
|
||||||
|
};
|
|
@ -0,0 +1,7 @@
|
||||||
|
/* SPDX-License-Identifier: Apache-2.0 */
|
||||||
|
|
||||||
|
&uart0 {
|
||||||
|
compatible = "nordic,nrf-uart";
|
||||||
|
current-speed = <1000000>;
|
||||||
|
status = "okay";
|
||||||
|
};
|
|
@ -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
|
|
@ -0,0 +1,7 @@
|
||||||
|
/* SPDX-License-Identifier: Apache-2.0 */
|
||||||
|
|
||||||
|
&uart0 {
|
||||||
|
compatible = "nordic,nrf-uart";
|
||||||
|
current-speed = <1000000>;
|
||||||
|
status = "okay";
|
||||||
|
};
|
|
@ -0,0 +1,7 @@
|
||||||
|
/* SPDX-License-Identifier: Apache-2.0 */
|
||||||
|
|
||||||
|
&uart0 {
|
||||||
|
compatible = "nordic,nrf-uart";
|
||||||
|
current-speed = <115200>;
|
||||||
|
status = "okay";
|
||||||
|
};
|
|
@ -0,0 +1,7 @@
|
||||||
|
/* SPDX-License-Identifier: Apache-2.0 */
|
||||||
|
|
||||||
|
&uart0 {
|
||||||
|
compatible = "nordic,nrf-uarte";
|
||||||
|
current-speed = <1000000>;
|
||||||
|
status = "okay";
|
||||||
|
};
|
|
@ -0,0 +1,7 @@
|
||||||
|
/* SPDX-License-Identifier: Apache-2.0 */
|
||||||
|
|
||||||
|
&uart0 {
|
||||||
|
compatible = "nordic,nrf-uarte";
|
||||||
|
current-speed = <1000000>;
|
||||||
|
status = "okay";
|
||||||
|
};
|
|
@ -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>;
|
||||||
|
};
|
|
@ -0,0 +1,7 @@
|
||||||
|
/* SPDX-License-Identifier: Apache-2.0 */
|
||||||
|
|
||||||
|
&uart0 {
|
||||||
|
compatible = "nordic,nrf-uarte";
|
||||||
|
current-speed = <1000000>;
|
||||||
|
status = "okay";
|
||||||
|
};
|
|
@ -0,0 +1,4 @@
|
||||||
|
# Override prj.conf defaults
|
||||||
|
CONFIG_CONSOLE=y
|
||||||
|
CONFIG_STDOUT_CONSOLE=y
|
||||||
|
CONFIG_UART_CONSOLE=y
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 Nordic Semiconductor ASA
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <nrf9160dk_nrf52840_reset_on_if5.dtsi>
|
||||||
|
|
||||||
|
#include <nrf9160dk_uart1_on_if0_3.dtsi>
|
||||||
|
|
||||||
|
&uart1 {
|
||||||
|
current-speed = <1000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/ {
|
||||||
|
chosen {
|
||||||
|
zephyr,bt-c2h-uart=&uart1;
|
||||||
|
};
|
||||||
|
};
|
|
@ -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 <nrf9160dk_nrf52840_reset_on_if9.dtsi>
|
21
samples/bluetooth/hci_uart_3wire/debug.conf
Normal file
21
samples/bluetooth/hci_uart_3wire/debug.conf
Normal file
|
@ -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
|
108
samples/bluetooth/hci_uart_3wire/overlay-all-bt_ll_sw_split.conf
Normal file
108
samples/bluetooth/hci_uart_3wire/overlay-all-bt_ll_sw_split.conf
Normal file
|
@ -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
|
22
samples/bluetooth/hci_uart_3wire/prj.conf
Normal file
22
samples/bluetooth/hci_uart_3wire/prj.conf
Normal file
|
@ -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
|
60
samples/bluetooth/hci_uart_3wire/sample.yaml
Normal file
60
samples/bluetooth/hci_uart_3wire/sample.yaml
Normal file
|
@ -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
|
821
samples/bluetooth/hci_uart_3wire/src/main.c
Normal file
821
samples/bluetooth/hci_uart_3wire/src/main.c
Normal file
|
@ -0,0 +1,821 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Xiaomi Coopration
|
||||||
|
* Copyright (c) 2015-2016 Intel Corporation
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/arch/cpu.h>
|
||||||
|
#include <zephyr/sys/byteorder.h>
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
#include <zephyr/sys/util.h>
|
||||||
|
|
||||||
|
#include <zephyr/device.h>
|
||||||
|
#include <zephyr/init.h>
|
||||||
|
#include <zephyr/drivers/uart.h>
|
||||||
|
|
||||||
|
#include <zephyr/usb/usb_device.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/drivers/bluetooth/hci_driver.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue