bluetooth: samples: Add hci_uart_async
This sample is an alternative implementation of hci_uart. The new sample differs from the existing sample in that it uses the async UART API instead of the interrupt driven API. Included in this commit is a new test for HCI UART flow control. It's enabled for hci_uart_async. The test can excercise also the existing hci_uart sample (with some minimal changes to allow compiling in the mock controller and test suite). The existing hci_uart sample currently fails the flow control test. Signed-off-by: Aleksander Wasaznik <aleksander.wasaznik@nordicsemi.no>
This commit is contained in:
parent
4d926ac041
commit
347ce7aa7f
14 changed files with 955 additions and 0 deletions
10
samples/bluetooth/hci_uart_async/CMakeLists.txt
Normal file
10
samples/bluetooth/hci_uart_async/CMakeLists.txt
Normal file
|
@ -0,0 +1,10 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
cmake_minimum_required(VERSION 3.20.0)
|
||||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
|
||||
project(hci_uart_async)
|
||||
target_sources(app PRIVATE
|
||||
src/hci_uart_async.c
|
||||
src/main.c
|
||||
)
|
158
samples/bluetooth/hci_uart_async/README.rst
Normal file
158
samples/bluetooth/hci_uart_async/README.rst
Normal file
|
@ -0,0 +1,158 @@
|
|||
.. _bluetooth-hci-uart-async-sample:
|
||||
|
||||
Bluetooth: HCI UART based on ASYNC UART
|
||||
#######################################
|
||||
|
||||
Expose a Zephyr Bluetooth Controller over a standard Bluetooth HCI UART interface.
|
||||
|
||||
This sample performs the same basic function as the HCI UART sample, but it uses the UART_ASYNC_API
|
||||
instead of UART_INTERRUPT_DRIVEN API. Not all boards implement both UART APIs, so the board support
|
||||
of the HCI UART sample may be different.
|
||||
|
||||
Requirements
|
||||
************
|
||||
|
||||
* A board with BLE support
|
||||
|
||||
Default UART settings
|
||||
*********************
|
||||
|
||||
By default the controller builds use the following settings:
|
||||
|
||||
* Baudrate: 1Mbit/s
|
||||
* 8 bits, no parity, 1 stop bit
|
||||
* Hardware Flow Control (RTS/CTS) enabled
|
||||
|
||||
Building and Running
|
||||
********************
|
||||
|
||||
This sample can be found under :zephyr_file:`samples/bluetooth/hci_uart_async`
|
||||
in the Zephyr tree and is built as a standard Zephyr application.
|
||||
|
||||
Using the controller with emulators and BlueZ
|
||||
*********************************************
|
||||
|
||||
The instructions below show how to use a Nordic nRF5x device as a Zephyr BLE
|
||||
controller and expose it to Linux's BlueZ.
|
||||
|
||||
First, make sure you have a recent BlueZ version installed by following the
|
||||
instructions in the :ref:`bluetooth_bluez` section.
|
||||
|
||||
Now build and flash the sample for the Nordic nRF5x board of your choice.
|
||||
All of the Nordic Development Kits come with a Segger IC that provides a
|
||||
debugger interface and a CDC ACM serial port bridge. More information can be
|
||||
found in :ref:`nordic_segger`.
|
||||
|
||||
For example, to build for the nRF52832 Development Kit:
|
||||
|
||||
.. zephyr-app-commands::
|
||||
:zephyr-app: samples/bluetooth/hci_uart_async
|
||||
:board: nrf52dk_nrf52832
|
||||
:goals: build flash
|
||||
|
||||
.. _bluetooth-hci-uart-async-qemu-posix:
|
||||
|
||||
Using the controller with QEMU and Native POSIX
|
||||
===============================================
|
||||
|
||||
In order to use the HCI UART controller with QEMU or Native POSIX you will need
|
||||
to attach it to the Linux Host first. To do so simply build the sample and
|
||||
connect the UART to the Linux machine, and then attach it with this command:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
sudo btattach -B /dev/ttyACM0 -S 1000000 -R
|
||||
|
||||
.. note::
|
||||
Depending on the serial port you are using you will need to modify the
|
||||
``/dev/ttyACM0`` string to point to the serial device your controller is
|
||||
connected to.
|
||||
|
||||
.. note::
|
||||
The ``-R`` flag passed to ``btattach`` instructs the kernel to avoid
|
||||
interacting with the controller and instead just be aware of it in order
|
||||
to proxy it to QEMU later.
|
||||
|
||||
If you are running :file:`btmon` you should see a brief log showing how the
|
||||
Linux kernel identifies the attached controller.
|
||||
|
||||
Once the controller is attached follow the instructions in the
|
||||
:ref:`bluetooth_qemu_posix` section to use QEMU with it.
|
||||
|
||||
.. _bluetooth-hci-uart-async-bluez:
|
||||
|
||||
Using the controller with BlueZ
|
||||
===============================
|
||||
|
||||
In order to use the HCI UART controller with BlueZ you will need to attach it
|
||||
to the Linux Host first. To do so simply build the sample and connect the
|
||||
UART to the Linux machine, and then attach it with this command:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
sudo btattach -B /dev/ttyACM0 -S 1000000
|
||||
|
||||
.. note::
|
||||
Depending on the serial port you are using you will need to modify the
|
||||
``/dev/ttyACM0`` string to point to the serial device your controller is
|
||||
connected to.
|
||||
|
||||
If you are running :file:`btmon` you should see a comprehensive log showing how
|
||||
BlueZ loads and initializes the attached controller.
|
||||
|
||||
Once the controller is attached follow the instructions in the
|
||||
:ref:`bluetooth_ctlr_bluez` section to use BlueZ with it.
|
||||
|
||||
Debugging the controller
|
||||
========================
|
||||
|
||||
The sample can be debugged using RTT since the UART is reserved used by this
|
||||
application. To enable debug over RTT the debug configuration file can be used.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
west build samples/bluetooth/hci_uart_async -- -DEXTRA_CONFIG='debug.mixin.conf'
|
||||
|
||||
Then attach RTT as described here: :ref:`Using Segger J-Link <Using Segger J-Link>`
|
||||
|
||||
Using the controller with the Zephyr host
|
||||
=========================================
|
||||
|
||||
This describes how to hook up a board running this sample to a board running
|
||||
an application that uses the Zephyr host.
|
||||
|
||||
On the controller side, the `zephyr,bt-c2h-uart` DTS property (in the `chosen`
|
||||
block) is used to select which uart device to use. For example if we want to
|
||||
keep the console logs, we can keep console on uart0 and the HCI on uart1 like
|
||||
so:
|
||||
|
||||
.. code-block:: dts
|
||||
|
||||
/ {
|
||||
chosen {
|
||||
zephyr,console = &uart0;
|
||||
zephyr,shell-uart = &uart0;
|
||||
zephyr,bt-c2h-uart = &uart1;
|
||||
};
|
||||
};
|
||||
|
||||
On the host application, some config options need to be used to select the H4
|
||||
driver instead of the built-in controller:
|
||||
|
||||
.. code-block:: kconfig
|
||||
|
||||
CONFIG_BT_HCI=y
|
||||
CONFIG_BT_CTLR=n
|
||||
CONFIG_BT_H4=y
|
||||
|
||||
Similarly, the `zephyr,bt-uart` DTS property selects which uart to use:
|
||||
|
||||
.. code-block:: dts
|
||||
|
||||
/ {
|
||||
chosen {
|
||||
zephyr,console = &uart0;
|
||||
zephyr,shell-uart = &uart0;
|
||||
zephyr,bt-uart = &uart1;
|
||||
};
|
||||
};
|
31
samples/bluetooth/hci_uart_async/app.overlay
Normal file
31
samples/bluetooth/hci_uart_async/app.overlay
Normal file
|
@ -0,0 +1,31 @@
|
|||
/* This is the default app device tree overlay. This file is ignored if
|
||||
* there is a board-specific overlay in `./boards`.
|
||||
*
|
||||
* Most boards define a convenient `&uart0`. It's used here to make the
|
||||
* sample 'just work' automatically for those boards.
|
||||
*/
|
||||
|
||||
bt_c2h_uart: &uart0 {
|
||||
status = "okay";
|
||||
current-speed = <1000000>;
|
||||
hw-flow-control;
|
||||
};
|
||||
|
||||
/ {
|
||||
chosen {
|
||||
zephyr,bt-c2h-uart = &bt_c2h_uart;
|
||||
};
|
||||
};
|
||||
|
||||
/* Some boards are by default assigning the &uart0 to these other
|
||||
* functions. Removing the assignments will ensure a compilation error
|
||||
* instead of accidental interference.
|
||||
*/
|
||||
/ {
|
||||
chosen {
|
||||
/delete-property/ zephyr,console;
|
||||
/delete-property/ zephyr,shell-uart;
|
||||
/delete-property/ zephyr,uart-mcumgr;
|
||||
/delete-property/ zephyr,bt-mon-uart;
|
||||
};
|
||||
};
|
15
samples/bluetooth/hci_uart_async/debug.mixin.conf
Normal file
15
samples/bluetooth/hci_uart_async/debug.mixin.conf
Normal file
|
@ -0,0 +1,15 @@
|
|||
CONFIG_ASSERT_ON_ERRORS=y
|
||||
CONFIG_ASSERT=y
|
||||
CONFIG_DEBUG_INFO=y
|
||||
CONFIG_DEBUG_OPTIMIZATIONS=y
|
||||
CONFIG_DEBUG_THREAD_INFO=y
|
||||
|
||||
# Enable RTT console
|
||||
CONFIG_RTT_CONSOLE=y
|
||||
|
||||
CONFIG_LOG=y
|
||||
CONFIG_LOG_BUFFER_SIZE=10000
|
||||
|
||||
# This outputs all HCI traffic to a separate RTT channel. Use `btmon
|
||||
# --jlink` to read it out. Add `--priority 7` for debug logs.
|
||||
CONFIG_BT_DEBUG_MONITOR_RTT=y
|
25
samples/bluetooth/hci_uart_async/prj.conf
Normal file
25
samples/bluetooth/hci_uart_async/prj.conf
Normal file
|
@ -0,0 +1,25 @@
|
|||
# hci_uart_async
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_UART_ASYNC_API=y
|
||||
|
||||
# hci_raw (dependency of hci_uart)
|
||||
CONFIG_BT=y
|
||||
CONFIG_BT_HCI_RAW=y
|
||||
CONFIG_BT_HCI_RAW_H4=y
|
||||
CONFIG_BT_HCI_RAW_H4_ENABLE=y
|
||||
|
||||
# Controller configuration. Modify these for your application's needs.
|
||||
CONFIG_BT_MAX_CONN=16
|
||||
CONFIG_BT_BUF_ACL_RX_SIZE=255
|
||||
CONFIG_BT_BUF_CMD_TX_SIZE=255
|
||||
CONFIG_BT_BUF_EVT_DISCARDABLE_SIZE=255
|
||||
|
||||
# Send an initial HCI_Command_Complete event on boot without waiting for
|
||||
# HCI_Reset. Make sure to use the same value for this setting in your
|
||||
# host application.
|
||||
#CONFIG_BT_WAIT_NOP=y
|
||||
|
||||
# See `overlay.app`. The 'zephyr,console' chosen node is deleted there
|
||||
# in case it has a interfering default. Those same boards set this
|
||||
# config and it must be undone or the build will fail.
|
||||
CONFIG_UART_CONSOLE=n
|
19
samples/bluetooth/hci_uart_async/sample.yaml
Normal file
19
samples/bluetooth/hci_uart_async/sample.yaml
Normal file
|
@ -0,0 +1,19 @@
|
|||
sample:
|
||||
name: Bluetooth HCI UART Async
|
||||
description:
|
||||
This sample is a batteries-included example of a Bluetooth HCI UART
|
||||
connectivity chip.
|
||||
|
||||
It demonstrates a possible implementation of an HCI UART (H4)
|
||||
interface on top of Zephyr's Bluetooth Raw API, and how to expose it
|
||||
over a UART.
|
||||
|
||||
This implementation is based on the Zephyr Asynchoronous UART API.
|
||||
tests:
|
||||
sample.bluetooth.hci_uart_async.nrf5:
|
||||
harness: bluetooth
|
||||
platform_allow:
|
||||
- nrf52dk_nrf52832
|
||||
tags:
|
||||
- uart
|
||||
- bluetooth
|
403
samples/bluetooth/hci_uart_async/src/hci_uart_async.c
Normal file
403
samples/bluetooth/hci_uart_async/src/hci_uart_async.c
Normal file
|
@ -0,0 +1,403 @@
|
|||
/* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/sys/__assert.h>
|
||||
#include <zephyr/sys/time_units.h>
|
||||
#include <zephyr/toolchain/common.h>
|
||||
#include <zephyr/drivers/bluetooth/hci_driver.h>
|
||||
#include <errno.h>
|
||||
#include <stddef.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>
|
||||
|
||||
LOG_MODULE_REGISTER(hci_uart_async, LOG_LEVEL_DBG);
|
||||
|
||||
static const struct device *const hci_uart_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_bt_c2h_uart));
|
||||
|
||||
static K_THREAD_STACK_DEFINE(h2c_thread_stack, CONFIG_BT_HCI_TX_STACK_SIZE);
|
||||
static struct k_thread h2c_thread;
|
||||
|
||||
enum h4_type {
|
||||
H4_CMD = 0x01,
|
||||
H4_ACL = 0x02,
|
||||
H4_SCO = 0x03,
|
||||
H4_EVT = 0x04,
|
||||
H4_ISO = 0x05,
|
||||
};
|
||||
|
||||
struct k_poll_signal uart_h2c_rx_sig;
|
||||
struct k_poll_signal uart_c2h_tx_sig;
|
||||
|
||||
static K_FIFO_DEFINE(c2h_queue);
|
||||
|
||||
/** Send raw data on c2h UART.
|
||||
*
|
||||
* Blocks until completion. Not thread-safe.
|
||||
*
|
||||
* @retval 0 on success
|
||||
* @retval -EBUSY Another transmission is in progress. This a
|
||||
* thread-safety violation.
|
||||
* @retval -errno @ref uart_tx error.
|
||||
*/
|
||||
static int uart_c2h_tx(const uint8_t *data, size_t size)
|
||||
{
|
||||
int err;
|
||||
struct k_poll_signal *sig = &uart_c2h_tx_sig;
|
||||
struct k_poll_event done[] = {
|
||||
K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_SIGNAL, K_POLL_MODE_NOTIFY_ONLY, sig),
|
||||
};
|
||||
|
||||
k_poll_signal_reset(sig);
|
||||
err = uart_tx(hci_uart_dev, data, size, SYS_FOREVER_US);
|
||||
|
||||
if (err) {
|
||||
LOG_ERR("uart c2h tx: err %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = k_poll(done, ARRAY_SIZE(done), K_FOREVER);
|
||||
__ASSERT_NO_MSG(err == 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Function expects that type is validated and only CMD, ISO or ACL will be used. */
|
||||
static uint32_t hci_payload_size(const uint8_t *hdr_buf, enum h4_type type)
|
||||
{
|
||||
switch (type) {
|
||||
case H4_CMD:
|
||||
return ((const struct bt_hci_cmd_hdr *)hdr_buf)->param_len;
|
||||
case H4_ACL:
|
||||
return sys_le16_to_cpu(((const struct bt_hci_acl_hdr *)hdr_buf)->len);
|
||||
case H4_ISO:
|
||||
return bt_iso_hdr_len(
|
||||
sys_le16_to_cpu(((const struct bt_hci_iso_hdr *)hdr_buf)->len));
|
||||
default:
|
||||
LOG_ERR("Invalid type: %u", type);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t hci_hdr_size(enum h4_type type)
|
||||
{
|
||||
switch (type) {
|
||||
case H4_CMD:
|
||||
return sizeof(struct bt_hci_cmd_hdr);
|
||||
case H4_ACL:
|
||||
return sizeof(struct bt_hci_acl_hdr);
|
||||
case H4_ISO:
|
||||
return sizeof(struct bt_hci_iso_hdr);
|
||||
default:
|
||||
LOG_ERR("Unexpected h4 type: %u", type);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** Send raw data on c2h UART.
|
||||
*
|
||||
* Blocks until either @p size has been received or special UART
|
||||
* condition occurs on the UART RX line, like an UART break or parity
|
||||
* error.
|
||||
*
|
||||
* Not thread-safe.
|
||||
*
|
||||
* @retval 0 on success
|
||||
* @retval -EBUSY Another transmission is in progress. This a
|
||||
* thread-safety violation.
|
||||
* @retval -errno @ref uart_rx_enable error.
|
||||
* @retval +stop_reason Special condition @ref uart_rx_stop_reason.
|
||||
*/
|
||||
static int uart_h2c_rx(uint8_t *dst, size_t size)
|
||||
{
|
||||
int err;
|
||||
struct k_poll_signal *sig = &uart_h2c_rx_sig;
|
||||
struct k_poll_event done[] = {
|
||||
K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_SIGNAL, K_POLL_MODE_NOTIFY_ONLY, sig),
|
||||
};
|
||||
|
||||
k_poll_signal_reset(sig);
|
||||
err = uart_rx_enable(hci_uart_dev, dst, size, SYS_FOREVER_US);
|
||||
|
||||
if (err) {
|
||||
LOG_ERR("uart h2c rx: err %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
k_poll(done, ARRAY_SIZE(done), K_FOREVER);
|
||||
return sig->result;
|
||||
}
|
||||
|
||||
/** Inject a HCI EVT Hardware error into the c2h packet stream.
|
||||
*
|
||||
* This uses `bt_recv`, just as if the controller is sending the error.
|
||||
*/
|
||||
static void send_hw_error(void)
|
||||
{
|
||||
const uint8_t err_code = 0;
|
||||
const uint8_t hci_evt_hw_err[] = {BT_HCI_EVT_HARDWARE_ERROR,
|
||||
sizeof(struct bt_hci_evt_hardware_error), err_code};
|
||||
|
||||
struct net_buf *buf = bt_buf_get_rx(BT_BUF_EVT, K_FOREVER);
|
||||
|
||||
net_buf_add_mem(buf, hci_evt_hw_err, sizeof(hci_evt_hw_err));
|
||||
|
||||
/* Inject the message into the c2h queue. */
|
||||
bt_recv(buf);
|
||||
|
||||
/* The c2h thread will send the message at some point. The host
|
||||
* will receive it and reset the controller.
|
||||
*/
|
||||
}
|
||||
|
||||
static void recover_sync_by_reset_pattern(void)
|
||||
{
|
||||
/* { H4_CMD, le_16(HCI_CMD_OP_RESET), len=0 } */
|
||||
const uint8_t h4_cmd_reset[] = {0x01, 0x03, 0x0C, 0x00};
|
||||
const uint32_t reset_pattern = sys_get_be32(h4_cmd_reset);
|
||||
int err;
|
||||
struct net_buf *h2c_cmd_reset;
|
||||
uint32_t shift_register = 0;
|
||||
|
||||
LOG_DBG("Looking for reset pattern");
|
||||
|
||||
while (shift_register != reset_pattern) {
|
||||
uint8_t read_byte;
|
||||
|
||||
uart_h2c_rx(&read_byte, sizeof(uint8_t));
|
||||
LOG_DBG("h2c: 0x%02x", read_byte);
|
||||
shift_register = (shift_register * 0x100) + read_byte;
|
||||
}
|
||||
|
||||
LOG_DBG("Pattern found");
|
||||
h2c_cmd_reset = bt_buf_get_tx(BT_BUF_H4, K_FOREVER, h4_cmd_reset, sizeof(h4_cmd_reset));
|
||||
LOG_DBG("Fowarding reset");
|
||||
|
||||
err = bt_send(h2c_cmd_reset);
|
||||
__ASSERT(!err, "Failed to send reset: %d", err);
|
||||
}
|
||||
|
||||
static void h2c_h4_transport(void)
|
||||
{
|
||||
/* When entering this function, the h2c stream should be
|
||||
* 'synchronized'. I.e. The stream should be at a H4 packet
|
||||
* boundary.
|
||||
*
|
||||
* This function returns to signal a desynchronization.
|
||||
* When this happens, the caller should resynchronize before
|
||||
* entering this function again. It's up to the caller to decide
|
||||
* how to resynchronize.
|
||||
*/
|
||||
|
||||
for (;;) {
|
||||
int err;
|
||||
struct net_buf *buf;
|
||||
uint8_t h4_type;
|
||||
uint8_t hdr_size;
|
||||
uint8_t *hdr_buf;
|
||||
uint16_t payload_size;
|
||||
|
||||
LOG_DBG("h2c: listening");
|
||||
|
||||
/* Read H4 type. */
|
||||
err = uart_h2c_rx(&h4_type, sizeof(uint8_t));
|
||||
|
||||
if (err) {
|
||||
return;
|
||||
}
|
||||
LOG_DBG("h2c: h4_type %d", h4_type);
|
||||
|
||||
/* Allocate buf. */
|
||||
buf = bt_buf_get_tx(BT_BUF_H4, K_FOREVER, &h4_type, sizeof(h4_type));
|
||||
LOG_DBG("h2c: buf %p", buf);
|
||||
|
||||
if (!buf) {
|
||||
/* `h4_type` was invalid. */
|
||||
__ASSERT_NO_MSG(hci_hdr_size(h4_type) == 0);
|
||||
|
||||
LOG_WRN("bt_buf_get_tx failed h4_type %d", h4_type);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Read HCI header. */
|
||||
hdr_size = hci_hdr_size(h4_type);
|
||||
hdr_buf = net_buf_add(buf, hdr_size);
|
||||
|
||||
err = uart_h2c_rx(hdr_buf, hdr_size);
|
||||
if (err) {
|
||||
net_buf_unref(buf);
|
||||
return;
|
||||
}
|
||||
LOG_HEXDUMP_DBG(hdr_buf, hdr_size, "h2c: hci hdr");
|
||||
|
||||
/* Read HCI payload. */
|
||||
payload_size = hci_payload_size(hdr_buf, h4_type);
|
||||
|
||||
LOG_DBG("h2c: payload_size %u", payload_size);
|
||||
|
||||
if (payload_size <= net_buf_tailroom(buf)) {
|
||||
uint8_t *payload_dst = net_buf_add(buf, payload_size);
|
||||
|
||||
err = uart_h2c_rx(payload_dst, payload_size);
|
||||
if (err) {
|
||||
net_buf_unref(buf);
|
||||
return;
|
||||
}
|
||||
LOG_HEXDUMP_DBG(payload_dst, payload_size, "h2c: hci payload");
|
||||
} else {
|
||||
/* Discard oversize packet. */
|
||||
uint8_t *discard_dst;
|
||||
uint16_t discard_size;
|
||||
|
||||
LOG_WRN("h2c: Discarding oversize h4_type %d payload_size %d.", h4_type,
|
||||
payload_size);
|
||||
|
||||
/* Reset `buf` so all of it is available. */
|
||||
net_buf_reset(buf);
|
||||
discard_dst = net_buf_tail(buf);
|
||||
discard_size = net_buf_max_len(buf);
|
||||
|
||||
while (payload_size) {
|
||||
uint16_t read_size = MIN(payload_size, discard_size);
|
||||
|
||||
err = uart_h2c_rx(discard_dst, read_size);
|
||||
if (err) {
|
||||
net_buf_unref(buf);
|
||||
return;
|
||||
}
|
||||
|
||||
payload_size -= read_size;
|
||||
}
|
||||
|
||||
net_buf_unref(buf);
|
||||
buf = NULL;
|
||||
}
|
||||
|
||||
LOG_DBG("h2c: packet done");
|
||||
|
||||
/* Route buf to Controller. */
|
||||
if (buf) {
|
||||
err = bt_send(buf);
|
||||
if (err) {
|
||||
/* This is not a transport error. */
|
||||
LOG_ERR("bt_send err %d", err);
|
||||
net_buf_unref(buf);
|
||||
buf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
k_yield();
|
||||
}
|
||||
}
|
||||
|
||||
static void h2c_thread_entry(void *p1, void *p2, void *p3)
|
||||
{
|
||||
k_thread_name_set(k_current_get(), "HCI TX (h2c)");
|
||||
|
||||
for (;;) {
|
||||
LOG_DBG("Synchronized");
|
||||
h2c_h4_transport();
|
||||
LOG_WRN("Desynchronized");
|
||||
send_hw_error();
|
||||
recover_sync_by_reset_pattern();
|
||||
}
|
||||
}
|
||||
|
||||
void callback(const struct device *dev, struct uart_event *evt, void *user_data)
|
||||
{
|
||||
ARG_UNUSED(user_data);
|
||||
|
||||
if (evt->type == UART_RX_DISABLED) {
|
||||
(void)k_poll_signal_raise(&uart_h2c_rx_sig, 0);
|
||||
} else if (evt->type == UART_RX_STOPPED) {
|
||||
(void)k_poll_signal_raise(&uart_h2c_rx_sig, evt->data.rx_stop.reason);
|
||||
} else if (evt->type == UART_TX_DONE) {
|
||||
(void)k_poll_signal_raise(&uart_c2h_tx_sig, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int hci_uart_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
k_poll_signal_init(&uart_h2c_rx_sig);
|
||||
k_poll_signal_init(&uart_c2h_tx_sig);
|
||||
|
||||
LOG_DBG("");
|
||||
|
||||
if (!device_is_ready(hci_uart_dev)) {
|
||||
LOG_ERR("HCI UART %s is not ready", hci_uart_dev->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
BUILD_ASSERT(IS_ENABLED(CONFIG_UART_ASYNC_API));
|
||||
err = uart_callback_set(hci_uart_dev, callback, NULL);
|
||||
|
||||
/* Note: Asserts if CONFIG_UART_ASYNC_API is not enabled for `hci_uart_dev`. */
|
||||
__ASSERT(!err, "err %d", err);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SYS_INIT(hci_uart_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);
|
||||
|
||||
const struct {
|
||||
uint8_t h4;
|
||||
struct bt_hci_evt_hdr hdr;
|
||||
struct bt_hci_evt_cmd_complete cc;
|
||||
} __packed cc_evt = {
|
||||
.h4 = H4_EVT,
|
||||
.hdr = {.evt = BT_HCI_EVT_CMD_COMPLETE, .len = sizeof(struct bt_hci_evt_cmd_complete)},
|
||||
.cc = {.ncmd = 1, .opcode = sys_cpu_to_le16(BT_OP_NOP)},
|
||||
};
|
||||
|
||||
static void c2h_thread_entry(void)
|
||||
{
|
||||
k_thread_name_set(k_current_get(), "HCI RX (c2h)");
|
||||
|
||||
if (IS_ENABLED(CONFIG_BT_WAIT_NOP)) {
|
||||
uart_c2h_tx((char *)&cc_evt, sizeof(cc_evt));
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
struct net_buf *buf;
|
||||
|
||||
buf = net_buf_get(&c2h_queue, K_FOREVER);
|
||||
uart_c2h_tx(buf->data, buf->len);
|
||||
net_buf_unref(buf);
|
||||
}
|
||||
}
|
||||
|
||||
void hci_uart_main(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = bt_enable_raw(&c2h_queue);
|
||||
__ASSERT_NO_MSG(!err);
|
||||
|
||||
/* TX thread. */
|
||||
k_thread_create(&h2c_thread, h2c_thread_stack, K_THREAD_STACK_SIZEOF(h2c_thread_stack),
|
||||
h2c_thread_entry, NULL, NULL, NULL, K_PRIO_COOP(7), 0, K_NO_WAIT);
|
||||
|
||||
/* Reuse current thread as RX thread. */
|
||||
c2h_thread_entry();
|
||||
}
|
11
samples/bluetooth/hci_uart_async/src/main.c
Normal file
11
samples/bluetooth/hci_uart_async/src/main.c
Normal file
|
@ -0,0 +1,11 @@
|
|||
/* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
extern int hci_uart_main(void);
|
||||
|
||||
int main(void)
|
||||
{
|
||||
hci_uart_main();
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue