2020-11-13 14:28:27 +01:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2020 Demant
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <zephyr.h>
|
2021-02-04 12:58:13 +05:30
|
|
|
#include <soc.h>
|
2020-11-13 14:28:27 +01:00
|
|
|
|
2021-02-04 12:58:13 +05:30
|
|
|
#include "hal/cpu.h"
|
2020-11-26 15:47:39 +01:00
|
|
|
#include "hal/ccm.h"
|
2021-02-04 12:58:13 +05:30
|
|
|
|
2020-11-26 15:47:39 +01:00
|
|
|
#include "util/memq.h"
|
|
|
|
#include "util/mem.h"
|
|
|
|
#include "util/mfifo.h"
|
2021-02-04 12:58:13 +05:30
|
|
|
|
2020-11-26 15:47:39 +01:00
|
|
|
#include "pdu.h"
|
2021-02-04 12:58:13 +05:30
|
|
|
|
2020-11-26 15:47:39 +01:00
|
|
|
#include "lll.h"
|
|
|
|
#include "lll_conn.h" /* for `struct lll_tx` */
|
|
|
|
|
2021-02-04 12:58:13 +05:30
|
|
|
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER)
|
|
|
|
#define LOG_MODULE_NAME bt_ctlr_ull_iso
|
|
|
|
#include "common/log.h"
|
|
|
|
#include "hal/debug.h"
|
|
|
|
|
2021-02-04 16:11:06 +01:00
|
|
|
#include "lll_conn_iso.h"
|
|
|
|
#include "ull_conn_iso_internal.h"
|
|
|
|
#include "ull_conn_iso_types.h"
|
|
|
|
#include "isoal.h"
|
|
|
|
#include "ull_iso_types.h"
|
|
|
|
|
|
|
|
#if defined(CONFIG_BT_CTLR_CONN_ISO_STREAMS)
|
|
|
|
static struct ll_iso_datapath datapath_pool[CONFIG_BT_CTLR_CONN_ISO_STREAMS];
|
|
|
|
#endif
|
|
|
|
static void *datapath_free;
|
|
|
|
|
2020-11-26 15:47:39 +01:00
|
|
|
static int init_reset(void);
|
|
|
|
|
|
|
|
static MFIFO_DEFINE(iso_tx, sizeof(struct lll_tx),
|
|
|
|
CONFIG_BT_CTLR_ISO_TX_BUFFERS);
|
|
|
|
|
|
|
|
static struct {
|
|
|
|
void *free;
|
|
|
|
uint8_t pool[CONFIG_BT_CTLR_ISO_TX_BUFFER_SIZE *
|
|
|
|
CONFIG_BT_CTLR_ISO_TX_BUFFERS];
|
|
|
|
} mem_iso_tx;
|
2020-11-13 14:28:27 +01:00
|
|
|
|
2021-02-04 16:11:06 +01:00
|
|
|
/* must be implemented by vendor */
|
|
|
|
__weak bool ll_data_path_configured(uint8_t data_path_dir,
|
|
|
|
uint8_t data_path_id)
|
|
|
|
{
|
|
|
|
ARG_UNUSED(data_path_dir);
|
|
|
|
ARG_UNUSED(data_path_id);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-11-13 14:28:27 +01:00
|
|
|
/* Contains vendor specific argument, function to be implemented by vendors */
|
|
|
|
__weak uint8_t ll_configure_data_path(uint8_t data_path_dir,
|
2021-02-04 16:11:06 +01:00
|
|
|
uint8_t data_path_id,
|
|
|
|
uint8_t vs_config_len,
|
|
|
|
uint8_t *vs_config)
|
2020-11-13 14:28:27 +01:00
|
|
|
{
|
|
|
|
ARG_UNUSED(data_path_dir);
|
|
|
|
ARG_UNUSED(data_path_id);
|
|
|
|
ARG_UNUSED(vs_config_len);
|
|
|
|
ARG_UNUSED(vs_config);
|
|
|
|
|
|
|
|
return BT_HCI_ERR_CMD_DISALLOWED;
|
|
|
|
}
|
|
|
|
|
2020-11-23 15:28:22 +01:00
|
|
|
uint8_t ll_read_iso_tx_sync(uint16_t handle, uint16_t *seq,
|
|
|
|
uint32_t *timestamp, uint32_t *offset)
|
|
|
|
{
|
|
|
|
ARG_UNUSED(handle);
|
|
|
|
ARG_UNUSED(seq);
|
|
|
|
ARG_UNUSED(timestamp);
|
|
|
|
ARG_UNUSED(offset);
|
|
|
|
|
|
|
|
return BT_HCI_ERR_CMD_DISALLOWED;
|
|
|
|
}
|
|
|
|
|
2021-02-04 16:11:06 +01:00
|
|
|
static inline bool path_is_vendor_specific(uint8_t path_id)
|
|
|
|
{
|
|
|
|
return (path_id >= BT_HCI_DATAPATH_ID_VS &&
|
|
|
|
path_id <= BT_HCI_DATAPATH_ID_VS_END);
|
|
|
|
}
|
|
|
|
|
2020-11-13 14:28:27 +01:00
|
|
|
uint8_t ll_setup_iso_path(uint16_t handle, uint8_t path_dir, uint8_t path_id,
|
|
|
|
uint8_t coding_format, uint16_t company_id,
|
2020-11-27 14:39:42 +01:00
|
|
|
uint16_t vs_codec_id, uint32_t controller_delay,
|
2020-11-13 14:28:27 +01:00
|
|
|
uint8_t codec_config_len, uint8_t *codec_config)
|
|
|
|
{
|
|
|
|
ARG_UNUSED(controller_delay);
|
|
|
|
ARG_UNUSED(codec_config_len);
|
|
|
|
ARG_UNUSED(codec_config);
|
|
|
|
|
2021-02-04 16:11:06 +01:00
|
|
|
isoal_sink_handle_t sink_hdl;
|
|
|
|
isoal_status_t err = 0;
|
|
|
|
|
|
|
|
if (path_id == BT_HCI_DATAPATH_ID_DISABLED) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ll_conn_iso_stream *cis = ll_conn_iso_stream_get(handle);
|
|
|
|
|
|
|
|
if (path_dir > BT_HCI_DATAPATH_DIR_CTLR_TO_HOST) {
|
|
|
|
return BT_HCI_ERR_CMD_DISALLOWED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TBD If the Host attempts to set a data path with a Connection Handle
|
|
|
|
* that does not exist or that is not for a CIS or a BIS, the Controller
|
|
|
|
* shall return the error code Unknown Connection Identifier (0x02)
|
|
|
|
*/
|
|
|
|
|
|
|
|
if ((path_dir == BT_HCI_DATAPATH_DIR_HOST_TO_CTLR &&
|
|
|
|
cis->datapath_in) ||
|
|
|
|
(path_dir == BT_HCI_DATAPATH_DIR_CTLR_TO_HOST &&
|
|
|
|
cis->datapath_out)) {
|
|
|
|
/* Data path has been set up, can only do setup once */
|
|
|
|
return BT_HCI_ERR_CMD_DISALLOWED;
|
|
|
|
}
|
|
|
|
if (path_is_vendor_specific(path_id) &&
|
|
|
|
!ll_data_path_configured(path_dir, path_id)) {
|
|
|
|
/* Data path must be configured prior to setup */
|
|
|
|
return BT_HCI_ERR_CMD_DISALLOWED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If Codec_Configuration_Length non-zero and Codec_ID set to
|
|
|
|
* transparent air mode, the Controller shall return the error code
|
|
|
|
* Invalid HCI Command Parameters (0x12).
|
|
|
|
*/
|
|
|
|
if (codec_config_len && vs_codec_id == BT_HCI_CODING_FORMAT_TRANSPARENT) {
|
|
|
|
return BT_HCI_ERR_INVALID_PARAM;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate and configure datapath */
|
|
|
|
struct ll_iso_datapath *dp = mem_acquire(&datapath_free);
|
|
|
|
|
|
|
|
dp->path_dir = path_dir;
|
|
|
|
dp->path_id = path_id;
|
|
|
|
dp->coding_format = coding_format;
|
|
|
|
dp->company_id = company_id;
|
|
|
|
|
|
|
|
/* TBD dp->sync_delay = controller_delay; ?*/
|
|
|
|
|
|
|
|
if (path_dir == BT_HCI_DATAPATH_DIR_HOST_TO_CTLR) {
|
|
|
|
cis->datapath_in = dp;
|
|
|
|
} else {
|
|
|
|
cis->datapath_out = dp;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (path_id == BT_HCI_DATAPATH_ID_HCI) {
|
|
|
|
/* Not vendor specific, thus alloc and emit functions known */
|
|
|
|
err = isoal_sink_create(&sink_hdl, cis, sink_sdu_alloc_hci,
|
|
|
|
sink_sdu_emit_hci, sink_sdu_write_hci);
|
|
|
|
} else {
|
|
|
|
/* TBD call vendor specific function to set up ISO path */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!err) {
|
|
|
|
dp->sink_hdl = sink_hdl;
|
|
|
|
isoal_sink_enable(sink_hdl);
|
|
|
|
} else {
|
|
|
|
return BT_HCI_ERR_CMD_DISALLOWED;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2020-11-13 14:28:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t ll_remove_iso_path(uint16_t handle, uint8_t path_dir)
|
|
|
|
{
|
2021-02-04 16:11:06 +01:00
|
|
|
struct ll_conn_iso_stream *cis = ll_conn_iso_stream_get(handle);
|
|
|
|
/* TBD: If the Host issues this command with a Connection_Handle that does not exist
|
|
|
|
* or is not for a CIS or a BIS, the Controller shall return the error code Unknown
|
|
|
|
* Connection Identifier (0x02).
|
|
|
|
*/
|
|
|
|
struct ll_iso_datapath *dp;
|
|
|
|
|
|
|
|
if (path_dir == BT_HCI_DATAPATH_DIR_HOST_TO_CTLR) {
|
|
|
|
dp = cis->datapath_in;
|
|
|
|
if (dp) {
|
|
|
|
cis->datapath_in = NULL;
|
|
|
|
mem_release(dp, &datapath_free);
|
|
|
|
}
|
|
|
|
} else if (path_dir == BT_HCI_DATAPATH_DIR_CTLR_TO_HOST) {
|
|
|
|
dp = cis->datapath_out;
|
|
|
|
if (dp) {
|
|
|
|
cis->datapath_out = NULL;
|
|
|
|
mem_release(dp, &datapath_free);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Reserved for future use */
|
|
|
|
return BT_HCI_ERR_CMD_DISALLOWED;
|
|
|
|
}
|
2020-11-13 14:28:27 +01:00
|
|
|
|
2021-02-04 16:11:06 +01:00
|
|
|
if (!dp) {
|
|
|
|
/* Datapath was not previously set up */
|
|
|
|
return BT_HCI_ERR_CMD_DISALLOWED;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2020-11-13 14:28:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t ll_iso_receive_test(uint16_t handle, uint8_t payload_type)
|
|
|
|
{
|
|
|
|
ARG_UNUSED(handle);
|
|
|
|
ARG_UNUSED(payload_type);
|
|
|
|
|
|
|
|
return BT_HCI_ERR_CMD_DISALLOWED;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t ll_iso_transmit_test(uint16_t handle, uint8_t payload_type)
|
|
|
|
{
|
|
|
|
ARG_UNUSED(handle);
|
|
|
|
ARG_UNUSED(payload_type);
|
|
|
|
|
|
|
|
return BT_HCI_ERR_CMD_DISALLOWED;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t ll_iso_test_end(uint16_t handle, uint32_t *received_cnt,
|
|
|
|
uint32_t *missed_cnt, uint32_t *failed_cnt)
|
|
|
|
{
|
|
|
|
ARG_UNUSED(handle);
|
|
|
|
ARG_UNUSED(received_cnt);
|
|
|
|
ARG_UNUSED(missed_cnt);
|
|
|
|
ARG_UNUSED(failed_cnt);
|
|
|
|
|
|
|
|
return BT_HCI_ERR_CMD_DISALLOWED;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t ll_iso_read_test_counters(uint16_t handle, uint32_t *received_cnt,
|
|
|
|
uint32_t *missed_cnt,
|
|
|
|
uint32_t *failed_cnt)
|
|
|
|
{
|
|
|
|
ARG_UNUSED(handle);
|
|
|
|
ARG_UNUSED(received_cnt);
|
|
|
|
ARG_UNUSED(missed_cnt);
|
|
|
|
ARG_UNUSED(failed_cnt);
|
|
|
|
|
|
|
|
return BT_HCI_ERR_CMD_DISALLOWED;
|
|
|
|
}
|
2020-11-23 15:28:22 +01:00
|
|
|
|
|
|
|
uint8_t ll_read_iso_link_quality(uint16_t handle,
|
|
|
|
uint32_t *tx_unacked_packets,
|
|
|
|
uint32_t *tx_flushed_packets,
|
|
|
|
uint32_t *tx_last_subevent_packets,
|
|
|
|
uint32_t *retransmitted_packets,
|
|
|
|
uint32_t *crc_error_packets,
|
|
|
|
uint32_t *rx_unreceived_packets,
|
|
|
|
uint32_t *duplicate_packets)
|
|
|
|
{
|
|
|
|
ARG_UNUSED(handle);
|
|
|
|
ARG_UNUSED(tx_unacked_packets);
|
|
|
|
ARG_UNUSED(tx_flushed_packets);
|
|
|
|
ARG_UNUSED(tx_last_subevent_packets);
|
|
|
|
ARG_UNUSED(retransmitted_packets);
|
|
|
|
ARG_UNUSED(crc_error_packets);
|
|
|
|
ARG_UNUSED(rx_unreceived_packets);
|
|
|
|
ARG_UNUSED(duplicate_packets);
|
|
|
|
|
|
|
|
return BT_HCI_ERR_CMD_DISALLOWED;
|
|
|
|
}
|
2020-11-26 15:47:39 +01:00
|
|
|
|
|
|
|
int ull_iso_init(void)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = init_reset();
|
|
|
|
if (err) {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ull_iso_reset(void)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
/* Re-initialize the Tx mfifo */
|
|
|
|
MFIFO_INIT(iso_tx);
|
|
|
|
|
|
|
|
err = init_reset();
|
|
|
|
if (err) {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *ll_iso_tx_mem_acquire(void)
|
|
|
|
{
|
|
|
|
return mem_acquire(&mem_iso_tx.free);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ll_iso_tx_mem_release(void *tx)
|
|
|
|
{
|
|
|
|
mem_release(tx, &mem_iso_tx.free);
|
|
|
|
}
|
|
|
|
|
|
|
|
int ll_iso_tx_mem_enqueue(uint16_t handle, void *tx)
|
|
|
|
{
|
|
|
|
struct lll_tx *lll_tx;
|
|
|
|
uint8_t idx;
|
|
|
|
|
|
|
|
idx = MFIFO_ENQUEUE_GET(iso_tx, (void **) &lll_tx);
|
|
|
|
if (!lll_tx) {
|
|
|
|
return -ENOBUFS;
|
|
|
|
}
|
|
|
|
|
|
|
|
lll_tx->handle = handle;
|
|
|
|
lll_tx->node = tx;
|
|
|
|
|
|
|
|
MFIFO_ENQUEUE(iso_tx, idx);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int init_reset(void)
|
|
|
|
{
|
|
|
|
/* Initialize tx pool. */
|
|
|
|
mem_init(mem_iso_tx.pool, CONFIG_BT_CTLR_ISO_TX_BUFFER_SIZE,
|
|
|
|
CONFIG_BT_CTLR_ISO_TX_BUFFERS, &mem_iso_tx.free);
|
|
|
|
|
2021-02-04 16:11:06 +01:00
|
|
|
#if defined(CONFIG_BT_CTLR_CONN_ISO_STREAMS)
|
|
|
|
/* Initialize ISO Datapath pool */
|
|
|
|
mem_init(datapath_pool, sizeof(struct ll_iso_datapath),
|
|
|
|
sizeof(datapath_pool) / sizeof(struct ll_iso_datapath), &datapath_free);
|
|
|
|
#endif
|
|
|
|
|
2020-11-26 15:47:39 +01:00
|
|
|
return 0;
|
|
|
|
}
|