diff --git a/include/bluetooth/buf.h b/include/bluetooth/buf.h index 1d092d5724b..595cbbbd9b1 100644 --- a/include/bluetooth/buf.h +++ b/include/bluetooth/buf.h @@ -32,6 +32,10 @@ enum bt_buf_type { BT_BUF_ACL_OUT, /** Incoming ACL data */ BT_BUF_ACL_IN, + /** Outgoing ISO data */ + BT_BUF_ISO_OUT, + /** Incoming ISO data */ + BT_BUF_ISO_IN, /** H:4 data */ BT_BUF_H4, }; diff --git a/include/bluetooth/conn.h b/include/bluetooth/conn.h index 62d3008c514..44cb8672bf9 100644 --- a/include/bluetooth/conn.h +++ b/include/bluetooth/conn.h @@ -262,8 +262,11 @@ enum { BT_CONN_TYPE_BR = BIT(1), /** SCO Connection Type */ BT_CONN_TYPE_SCO = BIT(2), + /** ISO Connection Type */ + BT_CONN_TYPE_ISO = BIT(3), /** All Connection Type */ - BT_CONN_TYPE_ALL = BT_CONN_TYPE_LE | BT_CONN_TYPE_BR | BT_CONN_TYPE_SCO, + BT_CONN_TYPE_ALL = BT_CONN_TYPE_LE | BT_CONN_TYPE_BR | + BT_CONN_TYPE_SCO | BT_CONN_TYPE_ISO, }; /** LE Connection Info Structure */ diff --git a/include/bluetooth/hci.h b/include/bluetooth/hci.h index 03c4207b7ac..6f10f1328d4 100644 --- a/include/bluetooth/hci.h +++ b/include/bluetooth/hci.h @@ -66,9 +66,10 @@ struct bt_hci_acl_hdr { #define bt_iso_handle(h) ((h) & 0x0fff) #define bt_iso_flags(h) ((h) >> 12) -#define bt_iso_flags_pb(f) (f & 0x0003) -#define bt_iso_flags_ts(f) ((f >> 2) & 0x0001) -#define bt_iso_pack_flags(pb, ts) ((pb & 0x0003) | ((ts & 0x0001) << 2)) +#define bt_iso_flags_pb(f) ((f) & 0x0003) +#define bt_iso_flags_ts(f) (((f) >> 2) & 0x0001) +#define bt_iso_pack_flags(pb, ts) \ + (((pb) & 0x0003) | (((ts) & 0x0001) << 2)) #define bt_iso_handle_pack(h, pb, ts) \ ((h) | (bt_iso_pack_flags(pb, ts) << 12)) diff --git a/include/bluetooth/iso.h b/include/bluetooth/iso.h new file mode 100644 index 00000000000..f04de6f8372 --- /dev/null +++ b/include/bluetooth/iso.h @@ -0,0 +1,256 @@ +/** @file + * @brief Bluetooth ISO handling + */ + +/* + * Copyright (c) 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_ISO_H_ +#define ZEPHYR_INCLUDE_BLUETOOTH_ISO_H_ + +/** + * @brief ISO + * @defgroup bt_iso ISO + * @ingroup bluetooth + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include + +/** @def BT_ISO_CHAN_SEND_RESERVE + * @brief Headroom needed for outgoing buffers + */ +#define BT_ISO_CHAN_SEND_RESERVE (CONFIG_BT_HCI_RESERVE + \ + BT_HCI_ISO_HDR_SIZE + \ + BT_HCI_ISO_DATA_HDR_SIZE) + +struct bt_iso_chan; + +/** @brief Life-span states of ISO channel. Used only by internal APIs + * dealing with setting channel to proper state depending on operational + * context. + */ +enum { + /** Channel disconnected */ + BT_ISO_DISCONNECTED, + /** Channel bound to a connection */ + BT_ISO_BOUND, + /** Channel in connecting state */ + BT_ISO_CONNECT, + /** Channel ready for upper layer traffic on it */ + BT_ISO_CONNECTED, + /** Channel in disconnecting state */ + BT_ISO_DISCONNECT, +}; + +/** @brief ISO Channel structure. */ +struct bt_iso_chan { + /** Channel connection reference */ + struct bt_conn *conn; + /** Channel operations reference */ + struct bt_iso_chan_ops *ops; + /** Channel QoS reference */ + struct bt_iso_chan_qos *qos; + /** Channel data path reference*/ + struct bt_iso_chan_path *path; + sys_snode_t node; + uint8_t state; + bt_security_t required_sec_level; +}; + +/** @brief Audio QoS direction */ +enum { + BT_ISO_CHAN_QOS_IN, + BT_ISO_CHAN_QOS_OUT, + BT_ISO_CHAN_QOS_INOUT +}; + +/** @brief ISO Channel QoS structure. */ +struct bt_iso_chan_qos { + /** @brief Channel direction + * + * Possible values: BT_ISO_CHAN_QOS_IN, BT_ISO_CHAN_QOS_OUT or + * BT_ISO_CHAN_QOS_INOUT. + */ + uint8_t dir; + /** Channel interval */ + uint32_t interval; + /** Channel SCA */ + uint8_t sca; + /** Channel packing mode */ + uint8_t packing; + /** Channel framing mode */ + uint8_t framing; + /** Channel Latency */ + uint16_t latency; + /** Channel SDU */ + uint8_t sdu; + /** Channel PHY */ + uint8_t phy; + /** Channel Retransmission Number */ + uint8_t rtn; +}; + +/** @brief ISO Channel Data Path structure. */ +struct bt_iso_chan_path { + /** Default path ID */ + uint8_t pid; + /** Coding Format */ + uint8_t format; + /** Company ID */ + uint16_t cid; + /** Vendor-defined Codec ID */ + uint16_t vid; + /** Controller Delay */ + uint32_t delay; + /** Codec Configuration length*/ + uint8_t cc_len; + /** Codec Configuration */ + uint8_t cc[0]; +}; + +/** @brief ISO Channel operations structure. */ +struct bt_iso_chan_ops { + /** @brief Channel connected callback + * + * If this callback is provided it will be called whenever the + * connection completes. + * + * @param chan The channel that has been connected + */ + void (*connected)(struct bt_iso_chan *chan); + + /** @brief Channel disconnected callback + * + * If this callback is provided it will be called whenever the + * channel is disconnected, including when a connection gets + * rejected. + * + * @param chan The channel that has been Disconnected + */ + void (*disconnected)(struct bt_iso_chan *chan); + + /** @brief Channel alloc_buf callback + * + * If this callback is provided the channel will use it to allocate + * buffers to store incoming data. + * + * @param chan The channel requesting a buffer. + * + * @return Allocated buffer. + */ + struct net_buf *(*alloc_buf)(struct bt_iso_chan *chan); + + /** @brief Channel recv callback + * + * @param chan The channel receiving data. + * @param buf Buffer containing incoming data. + */ + void (*recv)(struct bt_iso_chan *chan, struct net_buf *buf); +}; + +/** @brief ISO Server structure. */ +struct bt_iso_server { + /** Required minimim security level */ + bt_security_t sec_level; + + /** @brief Server accept callback + * + * This callback is called whenever a new incoming connection requires + * authorization. + * + * @param conn The connection that is requesting authorization + * @param chan Pointer to receive the allocated channel + * + * @return 0 in case of success or negative value in case of error. + */ + int (*accept)(struct bt_conn *conn, struct bt_iso_chan **chan); +}; + +/** @brief Register ISO server. + * + * Register ISO server, each new connection is authorized using the accept() + * callback which in case of success shall allocate the channel structure + * to be used by the new connection. + * + * @param server Server structure. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_iso_server_register(struct bt_iso_server *server); + +/** @brief Bind ISO channels + * + * Bind ISO channels with existing ACL connections, Channel objects passed + * (over an address of it) shouldn't be instantiated in application as + * standalone. + * + * @param conns Array of ACL connection objects + * @param num_conns Number of connection objects + * @param chans Array of ISO Channel objects to be created + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_iso_chan_bind(struct bt_conn **conns, uint8_t num_conns, + struct bt_iso_chan **chans); + +/** @brief Connect ISO channels + * + * Connect ISO channels, once the connection is completed each channel + * connected() callback will be called. If the connection is rejected + * disconnected() callback is called instead. + * Channel object passed (over an address of it) as second parameter shouldn't + * be instantiated in application as standalone. + * + * @param chans Array of ISO channel objects + * @param num_chans Number of channel objects + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_iso_chan_connect(struct bt_iso_chan **chans, uint8_t num_chans); + +/** @brief Disconnect ISO channel + * + * Disconnect ISO channel, if the connection is pending it will be + * canceled and as a result the channel disconnected() callback is called. + * Regarding to input parameter, to get details see reference description + * to bt_iso_chan_connect() API above. + * + * @param chan Channel object. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_iso_chan_disconnect(struct bt_iso_chan *chan); + +/** @brief Send data to ISO channel + * + * Send data from buffer to the channel. If credits are not available, buf will + * be queued and sent as and when credits are received from peer. + * Regarding to first input parameter, to get details see reference description + * to bt_iso_chan_connect() API above. + * + * @param chan Channel object. + * @param buf Buffer containing data to be sent. + * + * @return Bytes sent in case of success or negative value in case of error. + */ +int bt_iso_chan_send(struct bt_iso_chan *chan, struct net_buf *buf); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_ISO_H_ */ diff --git a/subsys/bluetooth/host/CMakeLists.txt b/subsys/bluetooth/host/CMakeLists.txt index 93b2dd7a16c..8e46a36382f 100644 --- a/subsys/bluetooth/host/CMakeLists.txt +++ b/subsys/bluetooth/host/CMakeLists.txt @@ -87,3 +87,5 @@ if(CONFIG_BT_CONN_DISABLE_SECURITY) Do not use in production." ) endif() + +add_subdirectory_ifdef(CONFIG_BT_AUDIO audio) diff --git a/subsys/bluetooth/host/Kconfig b/subsys/bluetooth/host/Kconfig index 80d35fbe805..d2a6e17d4a1 100644 --- a/subsys/bluetooth/host/Kconfig +++ b/subsys/bluetooth/host/Kconfig @@ -166,6 +166,8 @@ config BT_DRIVER_RX_HIGH_PRIO if BT_HCI_HOST +source "subsys/bluetooth/host/audio/Kconfig" + config BT_HOST_CRYPTO # Hidden option that compiles in random number generation and AES # encryption support using TinyCrypt library if this is not provided diff --git a/subsys/bluetooth/host/audio/CMakeLists.txt b/subsys/bluetooth/host/audio/CMakeLists.txt new file mode 100644 index 00000000000..24a92c44519 --- /dev/null +++ b/subsys/bluetooth/host/audio/CMakeLists.txt @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() +zephyr_library_link_libraries(subsys__bluetooth) + +zephyr_library_sources_ifdef(CONFIG_BT_ISO iso.c) diff --git a/subsys/bluetooth/host/audio/Kconfig b/subsys/bluetooth/host/audio/Kconfig new file mode 100644 index 00000000000..ad670138449 --- /dev/null +++ b/subsys/bluetooth/host/audio/Kconfig @@ -0,0 +1,107 @@ +# Bluetooth Audio configuration options + +# +# Copyright (c) 2020 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +menuconfig BT_AUDIO + bool "Bluetooth Audio support [Experimental]" + help + This option enables Bluetooth Audio support. The specific + features that are available may depend on other features + that have been enabled in the stack, such as Periodic + Advertisement for Broadcast and L2CAP Dynamic Channel + for Unicast. + +if BT_AUDIO + +config BT_ISO + bool + +if BT_CONN + +config BT_AUDIO_UNICAST + bool "Bluetooth Unicast Audio Support" + select BT_SMP + select BT_L2CAP_DYNAMIC_CHANNEL + select BT_ISO + select BT_GATT_DYNAMIC_DB + select BT_GATT_CACHING + select BT_EATT + help + This option enables support for Bluetooth Unicast Audio using + Isochronous channels. + +if BT_AUDIO_UNICAST + +config BT_MAX_ISO_CONN + int "Maximum number of simultaneous ISO connections" + depends on BT_ISO + default BT_MAX_CONN + range 1 64 + help + Maximum number of simultaneous Bluetooth isochronous connections + supported. + +config BT_ISO_TX_BUF_COUNT + int "Numer of Isochronous TX buffers" + default 1 + range 1 255 + help + Number of buffers available for outgoing Isochronous channel packets. + +config BT_ISO_TX_FRAG_COUNT + int "Number of ISO TX fragment buffers" + default 2 + range 0 255 + help + Number of buffers available for fragments of TX buffers. Warning: + setting this to 0 means that the application must ensure that + queued TX buffers never need to be fragmented, i.e. that the + controller's buffer size is large enough. If this is not ensured, + and there are no dedicated fragment buffers, a deadlock may occur. + In most cases the default value of 2 is a safe bet. + +config BT_ISO_TX_MTU + int "Maximum supported MTU for Isochronous TX buffers" + default 251 + range 23 2000 + help + Maximum MTU for Isochronous channels TX buffers. + +config BT_ISO_RX_BUF_COUNT + int "Numer of Isochronous RX buffers" + default 1 + range 1 255 + help + Number of buffers available for incoming Isochronous channel packets. + +config BT_ISO_RX_MTU + int "Maximum supported MTU for Isochronous RX buffers" + default 251 + range 23 2000 + help + Maximum MTU for Isochronous channels RX buffers. + +endif # BT_AUDIO_UNICAST +endif # BT_CONN + +config BT_AUDIO_DEBUG + bool "Enable debug logs" + depends on BT_DEBUG + help + Use this option to enable debug logs for the Bluetooth + Audio functionality. + +if BT_AUDIO_DEBUG + +config BT_AUDIO_DEBUG_ISO + bool "ISO channel debug" + help + Use this option to enable ISO channels debug logs for the + Bluetooth Audio functionality. + +endif # BT_AUDIO_DEBUG +endif # BT_AUDIO diff --git a/subsys/bluetooth/host/audio/iso.c b/subsys/bluetooth/host/audio/iso.c new file mode 100644 index 00000000000..7a39c3960b3 --- /dev/null +++ b/subsys/bluetooth/host/audio/iso.c @@ -0,0 +1,1086 @@ +/* Bluetooth ISO */ + +/* + * Copyright (c) 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include +#include +#include +#include + +#include "host/hci_core.h" +#include "host/conn_internal.h" +#include "iso_internal.h" + +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_AUDIO_DEBUG_ISO) +#define LOG_MODULE_NAME bt_iso +#include "common/log.h" + +NET_BUF_POOL_FIXED_DEFINE(iso_tx_pool, CONFIG_BT_ISO_TX_BUF_COUNT, + CONFIG_BT_ISO_TX_MTU, NULL); +NET_BUF_POOL_FIXED_DEFINE(iso_rx_pool, CONFIG_BT_ISO_RX_BUF_COUNT, + CONFIG_BT_ISO_RX_MTU, NULL); + +#if CONFIG_BT_ISO_TX_FRAG_COUNT > 0 +NET_BUF_POOL_FIXED_DEFINE(iso_frag_pool, CONFIG_BT_ISO_TX_FRAG_COUNT, + CONFIG_BT_ISO_TX_MTU, NULL); +#endif /* CONFIG_BT_ISO_TX_FRAG_COUNT > 0 */ + +/* TODO: Allow more than one server? */ +static struct bt_iso_server *iso_server; +struct bt_conn iso_conns[CONFIG_BT_MAX_ISO_CONN]; + +/* Audio Data Path direction */ +enum { + BT_ISO_CHAN_HOST_TO_CTRL, + BT_ISO_CHAN_CTRL_TO_HOST, +}; + +struct bt_iso_data_path { + /* Data Path direction */ + uint8_t dir; + /* Data Path ID */ + uint8_t pid; + /* Data Path param reference */ + struct bt_iso_chan_path *path; +}; + + +struct net_buf *bt_iso_get_rx(k_timeout_t timeout) +{ + struct net_buf *buf = net_buf_alloc(&iso_rx_pool, timeout); + + if (buf) { + net_buf_reserve(buf, BT_BUF_RESERVE); + bt_buf_set_type(buf, BT_BUF_ISO_IN); + } + + return buf; +} + +void hci_iso(struct net_buf *buf) +{ + struct bt_hci_iso_hdr *hdr; + uint16_t handle, len; + struct bt_conn *conn; + uint8_t flags; + + BT_DBG("buf %p", buf); + + BT_ASSERT(buf->len >= sizeof(*hdr)); + + hdr = net_buf_pull_mem(buf, sizeof(*hdr)); + len = sys_le16_to_cpu(hdr->len); + handle = sys_le16_to_cpu(hdr->handle); + flags = bt_iso_flags(handle); + + iso(buf)->handle = bt_iso_handle(handle); + iso(buf)->index = BT_CONN_INDEX_INVALID; + + BT_DBG("handle %u len %u flags %u", iso(buf)->handle, len, flags); + + if (buf->len != len) { + BT_ERR("ISO data length mismatch (%u != %u)", buf->len, len); + net_buf_unref(buf); + return; + } + + conn = bt_conn_lookup_handle(iso(buf)->handle); + if (!conn) { + BT_ERR("Unable to find conn for handle %u", iso(buf)->handle); + net_buf_unref(buf); + return; + } + + iso(buf)->index = bt_conn_index(conn); + + bt_conn_recv(conn, buf, flags); + bt_conn_unref(conn); +} + +void hci_le_cis_estabilished(struct net_buf *buf) +{ + struct bt_hci_evt_le_cis_established *evt = (void *)buf->data; + uint16_t handle = sys_le16_to_cpu(evt->conn_handle); + struct bt_conn *conn; + + BT_DBG("status %u handle %u", evt->status, handle); + + /* ISO connection handles are already assigned at this point */ + conn = bt_conn_lookup_handle(handle); + if (!conn) { + BT_ERR("No connection found for handle %u", handle); + return; + } + + __ASSERT(conn->type == BT_CONN_TYPE_ISO, "Invalid connection type"); + + if (!evt->status) { + /* TODO: Add CIG sync delay */ + bt_conn_set_state(conn, BT_CONN_CONNECTED); + bt_conn_unref(conn); + return; + } + + conn->err = evt->status; + bt_iso_disconnected(conn); + bt_conn_unref(conn); +} + +int hci_le_reject_cis(uint16_t handle, uint8_t reason) +{ + struct bt_hci_cp_le_reject_cis *cp; + struct net_buf *buf; + int err; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_REJECT_CIS, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->handle = sys_cpu_to_le16(handle); + cp->reason = reason; + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_REJECT_CIS, buf, NULL); + if (err) { + return err; + } + + return 0; +} + +int hci_le_accept_cis(uint16_t handle) +{ + struct bt_hci_cp_le_accept_cis *cp; + struct net_buf *buf; + int err; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_ACCEPT_CIS, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->handle = sys_cpu_to_le16(handle); + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_ACCEPT_CIS, buf, NULL); + if (err) { + return err; + } + + return 0; +} + +void hci_le_cis_req(struct net_buf *buf) +{ + struct bt_hci_evt_le_cis_req *evt = (void *)buf->data; + uint16_t acl_handle = sys_le16_to_cpu(evt->acl_handle); + uint16_t cis_handle = sys_le16_to_cpu(evt->cis_handle); + struct bt_conn *conn, *iso; + int err; + + BT_DBG("acl_handle %u cis_handle %u cig_id %u cis %u", + acl_handle, cis_handle, evt->cig_id, evt->cis_id); + + /* Lookup existing connection with same handle */ + iso = bt_conn_lookup_handle(cis_handle); + if (iso) { + BT_ERR("Invalid ISO handle %d", cis_handle); + hci_le_reject_cis(cis_handle, BT_HCI_ERR_CONN_LIMIT_EXCEEDED); + bt_conn_unref(iso); + return; + } + + /* Lookup ACL connection to attach */ + conn = bt_conn_lookup_handle(acl_handle); + if (!conn) { + BT_ERR("Invalid ACL handle %d", acl_handle); + hci_le_reject_cis(cis_handle, BT_HCI_ERR_UNKNOWN_CONN_ID); + return; + } + + /* Add ISO connection */ + iso = bt_conn_add_iso(conn); + + bt_conn_unref(conn); + + if (!iso) { + hci_le_reject_cis(cis_handle, + BT_HCI_ERR_INSUFFICIENT_RESOURCES); + return; + } + + /* Request application to accept */ + err = bt_iso_accept(iso); + if (err) { + bt_iso_cleanup(iso); + hci_le_reject_cis(cis_handle, + BT_HCI_ERR_INSUFFICIENT_RESOURCES); + return; + } + + iso->handle = cis_handle; + iso->role = BT_HCI_ROLE_SLAVE; + bt_conn_set_state(iso, BT_CONN_CONNECT); + + hci_le_accept_cis(cis_handle); +} + +int hci_le_remove_cig(uint8_t cig_id) +{ + struct bt_hci_cp_le_remove_cig *req; + struct net_buf *buf; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_REMOVE_CIG, sizeof(*req)); + if (!buf) { + return -ENOBUFS; + } + + req = net_buf_add(buf, sizeof(*req)); + + memset(req, 0, sizeof(*req)); + + req->cig_id = cig_id; + + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_REMOVE_CIG, buf, NULL); +} + +void bt_iso_cleanup(struct bt_conn *conn) +{ + int i; + + bt_conn_unref(conn->iso.acl); + conn->iso.acl = NULL; + + /* Check if conn is last of CIG */ + for (i = 0; i < CONFIG_BT_MAX_ISO_CONN; i++) { + if (conn == &iso_conns[i]) { + continue; + } + + if (atomic_get(&iso_conns[i].ref) && + iso_conns[i].iso.cig_id == conn->iso.cig_id) { + break; + } + } + + if (i == CONFIG_BT_MAX_ISO_CONN) { + hci_le_remove_cig(conn->iso.cig_id); + } + + bt_conn_unref(conn); + +} + +struct bt_conn *iso_new(void) +{ + return bt_conn_new(iso_conns, ARRAY_SIZE(iso_conns)); +} + +struct bt_conn *bt_conn_add_iso(struct bt_conn *acl) +{ + struct bt_conn *conn = iso_new(); + + if (!conn) { + return NULL; + } + + conn->iso.acl = bt_conn_ref(acl); + conn->type = BT_CONN_TYPE_ISO; + sys_slist_init(&conn->channels); + + return conn; +} + +#if defined(CONFIG_NET_BUF_LOG) +struct net_buf *bt_iso_create_pdu_timeout_debug(struct net_buf_pool *pool, + size_t reserve, + k_timeout_t timeout, + const char *func, int line) +#else +struct net_buf *bt_iso_create_pdu_timeout(struct net_buf_pool *pool, + size_t reserve, k_timeout_t timeout) +#endif +{ + if (!pool) { + pool = &iso_tx_pool; + } + + reserve += sizeof(struct bt_hci_iso_data_hdr); + +#if defined(CONFIG_NET_BUF_LOG) + return bt_conn_create_pdu_timeout_debug(pool, reserve, timeout, func, + line); +#else + return bt_conn_create_pdu_timeout(pool, reserve, timeout); +#endif +} + +#if defined(CONFIG_NET_BUF_LOG) +struct net_buf *bt_iso_create_frag_timeout_debug(size_t reserve, + k_timeout_t timeout, + const char *func, int line) +#else +struct net_buf *bt_iso_create_frag_timeout(size_t reserve, k_timeout_t timeout) +#endif +{ + struct net_buf_pool *pool = NULL; + +#if CONFIG_BT_L2CAP_TX_FRAG_COUNT > 0 + pool = &iso_frag_pool; +#endif + +#if defined(CONFIG_NET_BUF_LOG) + return bt_conn_create_pdu_timeout_debug(pool, reserve, timeout, func, + line); +#else + return bt_conn_create_pdu_timeout(pool, reserve, timeout); +#endif +} + +static struct net_buf *hci_le_set_cig_params(struct bt_iso_create_param *param) +{ + struct bt_hci_cis_params *cis; + struct bt_hci_cp_le_set_cig_params *req; + struct net_buf *buf; + struct net_buf *rsp; + int i, err; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_CIG_PARAMS, + sizeof(*req) + sizeof(*cis) * param->num_conns); + if (!buf) { + return NULL; + } + + req = net_buf_add(buf, sizeof(*req)); + + memset(req, 0, sizeof(*req)); + + req->cig_id = param->conns[0]->iso.cig_id; + sys_put_le24(param->chans[0]->qos->interval, req->m_interval); + sys_put_le24(param->chans[0]->qos->interval, req->s_interval); + req->sca = param->chans[0]->qos->sca; + req->packing = param->chans[0]->qos->packing; + req->framing = param->chans[0]->qos->framing; + req->m_latency = sys_cpu_to_le16(param->chans[0]->qos->latency); + req->s_latency = sys_cpu_to_le16(param->chans[0]->qos->latency); + req->num_cis = param->num_conns; + + /* Program the cis parameters */ + for (i = 0; i < param->num_conns; i++) { + cis = net_buf_add(buf, sizeof(*cis)); + + memset(cis, 0, sizeof(*cis)); + + cis->cis_id = param->conns[i]->iso.cis_id; + + switch (param->chans[i]->qos->dir) { + case BT_ISO_CHAN_QOS_IN: + cis->m_sdu = param->chans[i]->qos->sdu; + break; + case BT_ISO_CHAN_QOS_OUT: + cis->s_sdu = param->chans[i]->qos->sdu; + break; + case BT_ISO_CHAN_QOS_INOUT: + cis->m_sdu = param->chans[i]->qos->sdu; + cis->s_sdu = param->chans[i]->qos->sdu; + break; + } + + cis->m_phy = param->chans[i]->qos->phy; + cis->s_phy = param->chans[i]->qos->phy; + cis->m_rtn = param->chans[i]->qos->rtn; + cis->s_rtn = param->chans[i]->qos->rtn; + } + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_CIG_PARAMS, buf, &rsp); + if (err) { + return NULL; + } + + return rsp; +} + +int bt_conn_bind_iso(struct bt_iso_create_param *param) +{ + struct bt_conn *conn; + struct net_buf *rsp; + struct bt_hci_rp_le_set_cig_params *cig_rsp; + int i, err; + + /* Check if controller is ISO capable */ + if (!BT_FEAT_LE_CIS_MASTER(bt_dev.le.features)) { + return -ENOTSUP; + } + + if (!param->num_conns || param->num_conns > CONFIG_BT_MAX_ISO_CONN) { + return -EINVAL; + } + + /* Assign ISO connections to each LE connection */ + for (i = 0; i < param->num_conns; i++) { + conn = param->conns[i]; + + if (conn->type != BT_CONN_TYPE_LE) { + err = -EINVAL; + goto failed; + } + + conn = bt_conn_add_iso(conn); + if (!conn) { + err = -ENOMEM; + goto failed; + } + + conn->iso.cig_id = param->id; + conn->iso.cis_id = bt_conn_index(conn); + + param->conns[i] = conn; + } + + rsp = hci_le_set_cig_params(param); + if (!rsp) { + err = -EIO; + goto failed; + } + + cig_rsp = (void *)rsp->data; + + if (rsp->len < sizeof(cig_rsp) || + cig_rsp->num_handles != param->num_conns) { + BT_WARN("Unexpected response to hci_le_set_cig_params"); + err = -EIO; + } + + for (i = 0; i < cig_rsp->num_handles; i++) { + /* Assign the connection handle */ + param->conns[i]->handle = cig_rsp->handle[i]; + } + + net_buf_unref(rsp); + + return 0; + +failed: + for (i = 0; i < param->num_conns; i++) { + bt_iso_cleanup(param->conns[i]); + } + + return err; +} + +static int hci_le_create_cis(struct bt_conn **conn, uint8_t num_conns) +{ + struct bt_hci_cis *cis; + struct bt_hci_cp_le_create_cis *req; + struct net_buf *buf; + int i; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_CREATE_CIS, + sizeof(*req) + sizeof(*cis) * num_conns); + if (!buf) { + return -ENOBUFS; + } + + req = net_buf_add(buf, sizeof(*req)); + + memset(req, 0, sizeof(*req)); + + req->num_cis = num_conns; + + /* Program the cis parameters */ + for (i = 0; i < num_conns; i++) { + cis = net_buf_add(buf, sizeof(*cis)); + + memset(cis, 0, sizeof(*cis)); + + cis->cis_handle = sys_cpu_to_le16(conn[i]->handle); + cis->acl_handle = sys_cpu_to_le16(conn[i]->iso.acl->handle); + } + + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_CREATE_CIS, buf, NULL); +} + +int bt_conn_connect_iso(struct bt_conn **conns, uint8_t num_conns) +{ + int i, err; + + /* Check if controller is ISO capable */ + if (!BT_FEAT_LE_CIS_MASTER(bt_dev.le.features)) { + return -ENOTSUP; + } + + if (num_conns > CONFIG_BT_MAX_ISO_CONN) { + return -EINVAL; + } + + for (i = 0; i < num_conns; i++) { + if (conns[i]->type != BT_CONN_TYPE_ISO) { + return -EINVAL; + } + } + + err = hci_le_create_cis(conns, num_conns); + if (err) { + return err; + } + + /* Set connection state */ + for (i = 0; i < num_conns; i++) { + bt_conn_set_state(conns[i], BT_CONN_CONNECT); + } + + return 0; +} + +static int hci_le_setup_iso_data_path(struct bt_conn *conn, + struct bt_iso_data_path *path) +{ + struct bt_hci_cp_le_setup_iso_path *cp; + struct bt_hci_rp_le_setup_iso_path *rp; + struct net_buf *buf, *rsp; + uint8_t *cc; + int err; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SETUP_ISO_PATH, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->handle = sys_cpu_to_le16(conn->handle); + cp->path_dir = path->dir; + cp->path_id = path->pid; + cp->coding_format = path->path->format; + cp->company_id = sys_cpu_to_le16(path->path->cid); + cp->vendor_id = sys_cpu_to_le16(path->path->vid); + sys_put_le24(path->path->delay, cp->controller_delay); + cp->codec_config_len = path->path->cc_len; + cc = net_buf_add(buf, cp->codec_config_len); + memcpy(cc, path->path->cc, cp->codec_config_len); + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SETUP_ISO_PATH, buf, &rsp); + if (err) { + return err; + } + + rp = (void *)rsp->data; + if (rp->status || (rp->handle != conn->handle)) { + err = -EIO; + } + + net_buf_unref(rsp); + + return err; +} + +static int hci_le_remove_iso_data_path(struct bt_conn *conn, uint8_t dir) +{ + struct bt_hci_cp_le_remove_iso_path *cp; + struct bt_hci_rp_le_remove_iso_path *rp; + struct net_buf *buf, *rsp; + int err; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_REMOVE_ISO_PATH, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->handle = conn->handle; + cp->path_dir = dir; + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_REMOVE_ISO_PATH, buf, &rsp); + if (err) { + return err; + } + + rp = (void *)rsp->data; + if (rp->status || (rp->handle != conn->handle)) { + err = -EIO; + } + + net_buf_unref(rsp); + + return err; +} + +static void bt_iso_chan_add(struct bt_conn *conn, struct bt_iso_chan *chan) +{ + /* Attach channel to the connection */ + sys_slist_append(&conn->channels, &chan->node); + chan->conn = conn; + + BT_DBG("conn %p chan %p", conn, chan); +} + +int bt_iso_accept(struct bt_conn *conn) +{ + struct bt_iso_chan *chan; + int err; + + __ASSERT_NO_MSG(conn->type == BT_CONN_TYPE_ISO); + + BT_DBG("%p", conn); + + if (!iso_server) { + return -ENOMEM; + } + + err = iso_server->accept(conn, &chan); + if (err < 0) { + BT_ERR("err %d", err); + return err; + } + + bt_iso_chan_add(conn, chan); + bt_iso_chan_set_state(chan, BT_ISO_BOUND); + + return 0; +} + +static int bt_iso_setup_data_path(struct bt_conn *conn) +{ + int err; + struct bt_iso_chan *chan; + struct bt_iso_chan_path path = {}; + struct bt_iso_data_path out_path = { .dir = BT_ISO_CHAN_CTRL_TO_HOST }; + struct bt_iso_data_path in_path = { .dir = BT_ISO_CHAN_HOST_TO_CTRL }; + + chan = SYS_SLIST_PEEK_HEAD_CONTAINER(&conn->channels, chan, node); + if (!chan) { + return -EINVAL; + } + + in_path.path = chan->path ? chan->path : &path; + out_path.path = chan->path ? chan->path : &path; + + switch (chan->qos->dir) { + case BT_ISO_CHAN_QOS_IN: + in_path.pid = in_path.path->pid; + out_path.pid = BT_ISO_DATA_PATH_DISABLED; + break; + case BT_ISO_CHAN_QOS_OUT: + in_path.pid = BT_ISO_DATA_PATH_DISABLED; + out_path.pid = out_path.path->pid; + break; + case BT_ISO_CHAN_QOS_INOUT: + in_path.pid = in_path.path->pid; + out_path.pid = out_path.path->pid; + break; + default: + return -EINVAL; + } + + err = hci_le_setup_iso_data_path(conn, &in_path); + if (err) { + return err; + } + + return hci_le_setup_iso_data_path(conn, &out_path); +} + +void bt_iso_connected(struct bt_conn *conn) +{ + struct bt_iso_chan *chan; + + __ASSERT_NO_MSG(conn->type == BT_CONN_TYPE_ISO); + + BT_DBG("%p", conn); + + if (bt_iso_setup_data_path(conn)) { + BT_ERR("Unable to setup data path"); + bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + return; + } + + SYS_SLIST_FOR_EACH_CONTAINER(&conn->channels, chan, node) { + if (chan->ops->connected) { + chan->ops->connected(chan); + } + + bt_iso_chan_set_state(chan, BT_ISO_CONNECTED); + } +} + +static void bt_iso_remove_data_path(struct bt_conn *conn) +{ + BT_DBG("%p", conn); + + /* Remove both directions */ + hci_le_remove_iso_data_path(conn, BT_ISO_CHAN_CTRL_TO_HOST); + hci_le_remove_iso_data_path(conn, BT_ISO_CHAN_HOST_TO_CTRL); +} + +void bt_iso_disconnected(struct bt_conn *conn) +{ + struct bt_iso_chan *chan, *next; + + __ASSERT_NO_MSG(conn->type == BT_CONN_TYPE_ISO); + + BT_DBG("%p", conn); + + if (sys_slist_is_empty(&conn->channels)) { + return; + } + + bt_iso_remove_data_path(conn); + + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&conn->channels, chan, next, node) { + if (chan->ops->disconnected) { + chan->ops->disconnected(chan); + } + + chan->conn = NULL; + bt_iso_chan_set_state(chan, BT_ISO_DISCONNECTED); + } +} + +int bt_iso_server_register(struct bt_iso_server *server) +{ + __ASSERT_NO_MSG(server); + + /* Check if controller is ISO capable */ + if (!BT_FEAT_LE_CIS_SLAVE(bt_dev.le.features)) { + return -ENOTSUP; + } + + if (iso_server) { + return -EADDRINUSE; + } + + if (!server->accept) { + return -EINVAL; + } + + if (server->sec_level > BT_SECURITY_L3) { + return -EINVAL; + } else if (server->sec_level < BT_SECURITY_L1) { + /* Level 0 is only applicable for BR/EDR */ + server->sec_level = BT_SECURITY_L1; + } + + BT_DBG("%p", server); + + iso_server = server; + + return 0; +} + +#if defined(CONFIG_BT_AUDIO_DEBUG_ISO) +const char *bt_iso_chan_state_str(uint8_t state) +{ + switch (state) { + case BT_ISO_DISCONNECTED: + return "disconnected"; + case BT_ISO_BOUND: + return "bound"; + case BT_ISO_CONNECT: + return "connect"; + case BT_ISO_CONNECTED: + return "connected"; + case BT_ISO_DISCONNECT: + return "disconnect"; + default: + return "unknown"; + } +} + +void bt_iso_chan_set_state_debug(struct bt_iso_chan *chan, uint8_t state, + const char *func, int line) +{ + BT_DBG("chan %p conn %p %s -> %s", chan, chan->conn, + bt_iso_chan_state_str(chan->state), + bt_iso_chan_state_str(state)); + + /* check transitions validness */ + switch (state) { + case BT_ISO_DISCONNECTED: + /* regardless of old state always allows this state */ + break; + case BT_ISO_BOUND: + if (chan->state != BT_ISO_DISCONNECTED) { + BT_WARN("%s()%d: invalid transition", func, line); + } + break; + case BT_ISO_CONNECT: + if (chan->state != BT_ISO_BOUND) { + BT_WARN("%s()%d: invalid transition", func, line); + } + break; + case BT_ISO_CONNECTED: + if (chan->state != BT_ISO_BOUND && + chan->state != BT_ISO_CONNECT) { + BT_WARN("%s()%d: invalid transition", func, line); + } + break; + case BT_ISO_DISCONNECT: + if (chan->state != BT_ISO_CONNECTED) { + BT_WARN("%s()%d: invalid transition", func, line); + } + break; + default: + BT_ERR("%s()%d: unknown (%u) state was set", func, line, state); + return; + } + + chan->state = state; +} +#else +void bt_iso_chan_set_state(struct bt_iso_chan *chan, uint8_t state) +{ + chan->state = state; +} +#endif /* CONFIG_BT_AUDIO_DEBUG_ISO */ + +void bt_iso_chan_remove(struct bt_conn *conn, struct bt_iso_chan *chan) +{ + struct bt_iso_chan *c; + sys_snode_t *prev = NULL; + + SYS_SLIST_FOR_EACH_CONTAINER(&conn->channels, c, node) { + if (c == chan) { + sys_slist_remove(&conn->channels, prev, &chan->node); + return; + } + + prev = &chan->node; + } +} + +int bt_iso_chan_bind(struct bt_conn **conns, uint8_t num_conns, + struct bt_iso_chan **chans) +{ + struct bt_iso_create_param param; + int i, err; + static uint8_t id; + + __ASSERT_NO_MSG(conns); + __ASSERT_NO_MSG(num_conns); + __ASSERT_NO_MSG(chans); + + memset(¶m, 0, sizeof(param)); + + param.id = id++; + param.num_conns = num_conns; + param.conns = conns; + param.chans = chans; + + err = bt_conn_bind_iso(¶m); + if (err) { + return err; + } + + /* Bind respective connection to channel */ + for (i = 0; i < num_conns; i++) { + bt_iso_chan_add(conns[i], chans[i]); + bt_iso_chan_set_state(chans[i], BT_ISO_BOUND); + } + + return 0; +} + +int bt_iso_chan_connect(struct bt_iso_chan **chans, uint8_t num_chans) +{ + struct bt_conn *conns[CONFIG_BT_MAX_ISO_CONN]; + int i, err; + + __ASSERT_NO_MSG(chans); + __ASSERT_NO_MSG(num_chans); + + for (i = 0; i < num_chans; i++) { + if (!chans[i]->conn) { + return -ENOTCONN; + } + + conns[i] = chans[i]->conn; + } + + err = bt_conn_connect_iso(conns, num_chans); + if (err) { + return err; + } + + for (i = 0; i < num_chans; i++) { + bt_iso_chan_set_state(chans[i], BT_ISO_CONNECT); + } + + return 0; +} + +int bt_iso_chan_disconnect(struct bt_iso_chan *chan) +{ + __ASSERT_NO_MSG(chan); + + if (!chan->conn) { + return -ENOTCONN; + } + + if (chan->state == BT_ISO_BOUND) { + bt_iso_chan_set_state(chan, BT_ISO_DISCONNECTED); + bt_conn_unref(chan->conn); + chan->conn = NULL; + return 0; + } + + return bt_conn_disconnect(chan->conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); +} + +void bt_iso_recv(struct bt_conn *conn, struct net_buf *buf, uint8_t flags) +{ + struct bt_hci_iso_data_hdr *hdr; + struct bt_iso_chan *chan; + uint8_t pb, ts; + uint16_t len; + + pb = bt_iso_flags_pb(flags); + ts = bt_iso_flags_ts(flags); + + BT_DBG("handle %u len %u flags 0x%02x pb 0x%02x ts 0x%02x", + conn->handle, buf->len, flags, pb, ts); + + /* When the PB_Flag does not equal 0b00, the fields Time_Stamp, + * Packet_Sequence_Number, Packet_Status_Flag and ISO_SDU_Length + * are omitted from the HCI ISO Data packet. + */ + switch (pb) { + case BT_ISO_START: + case BT_ISO_SINGLE: + /* The ISO_Data_Load field contains either the first fragment + * of an SDU or a complete SDU. + */ + if (ts) { + struct bt_hci_iso_ts_data_hdr *ts_hdr; + + ts_hdr = net_buf_pull_mem(buf, sizeof(*ts_hdr)); + iso(buf)->ts = sys_le32_to_cpu(ts_hdr->ts); + + hdr = &ts_hdr->data; + } else { + hdr = net_buf_pull_mem(buf, sizeof(*hdr)); + /* TODO: Generate a timestamp? */ + iso(buf)->ts = 0x00000000; + } + + len = sys_le16_to_cpu(hdr->slen); + flags = bt_iso_pkt_flags(len); + len = bt_iso_pkt_len(len); + + /* TODO: Drop the packet if NOP? */ + + BT_DBG("%s, len %u total %u flags 0x%02x timestamp %u", + pb == BT_ISO_START ? "Start" : "Single", buf->len, len, + flags, iso(buf)->ts); + + if (conn->rx_len) { + BT_ERR("Unexpected ISO %s fragment", + pb == BT_ISO_START ? "Start" : "Single"); + bt_conn_reset_rx_state(conn); + } + + conn->rx_len = len - buf->len; + if (conn->rx_len) { + if (pb == BT_ISO_SINGLE) { + BT_ERR("Unexpected ISO single fragment"); + bt_conn_reset_rx_state(conn); + } + conn->rx = buf; + return; + } + break; + + case BT_ISO_CONT: + /* The ISO_Data_Load field contains a continuation fragment of + * an SDU. + */ + if (!conn->rx_len) { + BT_ERR("Unexpected ISO continuation fragment"); + bt_conn_reset_rx_state(conn); + net_buf_unref(buf); + return; + } + + BT_DBG("Cont, len %u rx_len %u", buf->len, conn->rx_len); + + if (buf->len > net_buf_tailroom(conn->rx)) { + BT_ERR("Not enough buffer space for ISO data"); + bt_conn_reset_rx_state(conn); + net_buf_unref(buf); + return; + } + + net_buf_add_mem(conn->rx, buf->data, buf->len); + conn->rx_len -= buf->len; + net_buf_unref(buf); + return; + + case BT_ISO_END: + /* The ISO_Data_Load field contains the last fragment of an + * SDU. + */ + BT_DBG("End, len %u rx_len %u", buf->len, conn->rx_len); + + if (buf->len > net_buf_tailroom(conn->rx)) { + BT_ERR("Not enough buffer space for ISO data"); + bt_conn_reset_rx_state(conn); + net_buf_unref(buf); + return; + } + + net_buf_add_mem(conn->rx, buf->data, buf->len); + conn->rx_len -= buf->len; + net_buf_unref(buf); + + if (conn->rx_len) { + BT_ERR("Unexpected ISO end fragment"); + return; + } + + buf = conn->rx; + conn->rx = NULL; + conn->rx_len = 0U; + + break; + default: + BT_ERR("Unexpected ISO pb flags (0x%02x)", pb); + bt_conn_reset_rx_state(conn); + net_buf_unref(buf); + return; + } + + SYS_SLIST_FOR_EACH_CONTAINER(&conn->channels, chan, node) { + if (chan->ops->recv) { + chan->ops->recv(chan, buf); + } + } +} + +int bt_iso_chan_send(struct bt_iso_chan *chan, struct net_buf *buf) +{ + struct bt_hci_iso_data_hdr *hdr; + static uint16_t sn; + + __ASSERT_NO_MSG(chan); + __ASSERT_NO_MSG(buf); + + BT_DBG("chan %p len %zu", chan, net_buf_frags_len(buf)); + + if (!chan->conn) { + return -ENOTCONN; + } + + hdr = net_buf_push(buf, sizeof(*hdr)); + hdr->sn = sys_cpu_to_le16(sn++); + hdr->slen = sys_cpu_to_le16(bt_iso_pkt_len_pack(net_buf_frags_len(buf) + - sizeof(*hdr), + BT_ISO_DATA_VALID)); + + return bt_conn_send(chan->conn, buf); +} diff --git a/subsys/bluetooth/host/audio/iso_internal.h b/subsys/bluetooth/host/audio/iso_internal.h new file mode 100644 index 00000000000..4c1d22ec3f7 --- /dev/null +++ b/subsys/bluetooth/host/audio/iso_internal.h @@ -0,0 +1,110 @@ +/** @file + * @brief Internal APIs for Bluetooth ISO handling. + */ + +/* + * Copyright (c) 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#define BT_ISO_DATA_PATH_DISABLED 0xFF + +struct iso_data { + /** BT_BUF_ISO_IN */ + uint8_t type; + + /* Index into the bt_conn storage array */ + uint8_t index; + + /** ISO connection handle */ + uint16_t handle; + + /** ISO timestamp */ + uint32_t ts; +}; + +#define iso(buf) ((struct iso_data *)net_buf_user_data(buf)) + +#if defined(CONFIG_BT_MAX_ISO_CONN) +extern struct bt_conn iso_conns[CONFIG_BT_MAX_ISO_CONN]; +#endif + +/* Process ISO buffer */ +void hci_iso(struct net_buf *buf); + +/* Allocates RX buffer */ +struct net_buf *bt_iso_get_rx(k_timeout_t timeout); + +/* Create new ISO connecting */ +struct bt_conn *iso_new(void); + +/* Process CIS Estabilished event */ +void hci_le_cis_estabilished(struct net_buf *buf); + +/* Process CIS Request event */ +void hci_le_cis_req(struct net_buf *buf); + +/* Notify ISO channels of a new connection */ +int bt_iso_accept(struct bt_conn *conn); + +/* Notify ISO channels of a new connection */ +void bt_iso_connected(struct bt_conn *conn); + +/* Notify ISO channels of a disconnect event */ +void bt_iso_disconnected(struct bt_conn *conn); + +/* Allocate ISO PDU */ +#if defined(CONFIG_NET_BUF_LOG) +struct net_buf *bt_iso_create_pdu_timeout_debug(struct net_buf_pool *pool, + size_t reserve, + k_timeout_t timeout, + const char *func, int line); +#define bt_iso_create_pdu_timeout(_pool, _reserve, _timeout) \ + bt_iso_create_pdu_timeout_debug(_pool, _reserve, _timeout, \ + __func__, __LINE__) + +#define bt_iso_create_pdu(_pool, _reserve) \ + bt_iso_create_pdu_timeout_debug(_pool, _reserve, K_FOREVER, \ + __func__, __line__) +#else +struct net_buf *bt_iso_create_pdu_timeout(struct net_buf_pool *pool, + size_t reserve, k_timeout_t timeout); + +#define bt_iso_create_pdu(_pool, _reserve) \ + bt_iso_create_pdu_timeout(_pool, _reserve, K_FOREVER) +#endif + +/* Allocate ISO Fragment */ +#if defined(CONFIG_NET_BUF_LOG) +struct net_buf *bt_iso_create_frag_timeout_debug(size_t reserve, + k_timeout_t timeout, + const char *func, int line); + +#define bt_iso_create_frag_timeout(_reserve, _timeout) \ + bt_iso_create_frag_timeout_debug(_reserve, _timeout, \ + __func__, __LINE__) + +#define bt_iso_create_frag(_reserve) \ + bt_iso_create_frag_timeout_debug(_reserve, K_FOREVER, \ + __func__, __LINE__) +#else +struct net_buf *bt_iso_create_frag_timeout(size_t reserve, k_timeout_t timeout); + +#define bt_iso_create_frag(_reserve) \ + bt_iso_create_frag_timeout(_reserve, K_FOREVER) +#endif + +#if defined(CONFIG_BT_AUDIO_DEBUG_ISO) +void bt_iso_chan_set_state_debug(struct bt_iso_chan *chan, uint8_t state, + const char *func, int line); +#define bt_iso_chan_set_state(_chan, _state) \ + bt_iso_chan_set_state_debug(_chan, _state, __func__, __LINE__) +#else +void bt_iso_chan_set_state(struct bt_iso_chan *chan, uint8_t state); +#endif /* CONFIG_BT_AUDIO_DEBUG_ISO */ + +/* Process incoming data for a connection */ +void bt_iso_recv(struct bt_conn *conn, struct net_buf *buf, uint8_t flags); diff --git a/subsys/bluetooth/host/conn.c b/subsys/bluetooth/host/conn.c index 97a745e885c..b4234afead7 100644 --- a/subsys/bluetooth/host/conn.c +++ b/subsys/bluetooth/host/conn.c @@ -35,6 +35,7 @@ #include "ssp.h" #include "att_internal.h" #include "gatt_internal.h" +#include "audio/iso_internal.h" struct tx_meta { struct bt_conn_tx *tx; @@ -69,7 +70,7 @@ NET_BUF_POOL_FIXED_DEFINE(frag_pool, CONFIG_BT_L2CAP_TX_FRAG_COUNT, FRAG_SIZE, const struct bt_conn_auth_cb *bt_auth; #endif /* CONFIG_BT_SMP || CONFIG_BT_BREDR */ -static struct bt_conn conns[CONFIG_BT_MAX_CONN]; +static struct bt_conn acl_conns[CONFIG_BT_MAX_CONN]; static struct bt_conn_cb *callback_list; static struct bt_conn_tx conn_tx[CONFIG_BT_CONN_TX_MAX]; @@ -82,12 +83,18 @@ static struct bt_conn sco_conns[CONFIG_BT_MAX_SCO_CONN]; struct k_sem *bt_conn_get_pkts(struct bt_conn *conn) { #if defined(CONFIG_BT_BREDR) - if (conn->type == BT_CONN_TYPE_BR || !bt_dev.le.mtu) { + if (conn->type == BT_CONN_TYPE_BR || !bt_dev.le.acl_mtu) { return &bt_dev.br.pkts; } #endif /* CONFIG_BT_BREDR */ - - return &bt_dev.le.pkts; +#if defined(CONFIG_BT_ISO) + if (conn->type == BT_CONN_TYPE_ISO || bt_dev.le.iso_mtu) { + if (bt_dev.le.iso_pkts.limit) { + return &bt_dev.le.iso_pkts; + } + } +#endif /* CONFIG_BT_ISO */ + return &bt_dev.le.acl_pkts; } static inline const char *state2str(bt_conn_state_t state) @@ -392,12 +399,12 @@ static void conn_update_timeout(struct k_work *work) atomic_set_bit(conn->flags, BT_CONN_SLAVE_PARAM_UPDATE); } -static struct bt_conn *conn_new(void) +struct bt_conn *bt_conn_new(struct bt_conn *conns, size_t size) { struct bt_conn *conn = NULL; int i; - for (i = 0; i < ARRAY_SIZE(conns); i++) { + for (i = 0; i < size; i++) { if (!atomic_get(&conns[i].ref)) { conn = &conns[i]; break; @@ -409,12 +416,25 @@ static struct bt_conn *conn_new(void) } (void)memset(conn, 0, sizeof(*conn)); + + atomic_set(&conn->ref, 1); + + return conn; +} + +static struct bt_conn *acl_conn_new(void) +{ + struct bt_conn *conn; + + conn = bt_conn_new(acl_conns, ARRAY_SIZE(acl_conns)); + if (!conn) { + return conn; + } + k_delayed_work_init(&conn->update_work, conn_update_timeout); k_work_init(&conn->tx_complete_work, tx_complete_work); - atomic_set(&conn->ref, 1); - return conn; } @@ -428,25 +448,7 @@ void bt_sco_cleanup(struct bt_conn *sco_conn) static struct bt_conn *sco_conn_new(void) { - struct bt_conn *sco_conn = NULL; - int i; - - for (i = 0; i < ARRAY_SIZE(sco_conns); i++) { - if (!atomic_get(&sco_conns[i].ref)) { - sco_conn = &sco_conns[i]; - break; - } - } - - if (!sco_conn) { - return NULL; - } - - (void)memset(sco_conn, 0, sizeof(*sco_conn)); - - atomic_set(&sco_conn->ref, 1); - - return sco_conn; + return bt_conn_new(sco_conns, ARRAY_SIZE(sco_conns)); } struct bt_conn *bt_conn_create_br(const bt_addr_t *peer, @@ -586,17 +588,17 @@ struct bt_conn *bt_conn_lookup_addr_br(const bt_addr_t *peer) { int i; - for (i = 0; i < ARRAY_SIZE(conns); i++) { - if (!atomic_get(&conns[i].ref)) { + for (i = 0; i < ARRAY_SIZE(acl_conns); i++) { + if (!atomic_get(&acl_conns[i].ref)) { continue; } - if (conns[i].type != BT_CONN_TYPE_BR) { + if (acl_conns[i].type != BT_CONN_TYPE_BR) { continue; } - if (!bt_addr_cmp(peer, &conns[i].br.dst)) { - return bt_conn_ref(&conns[i]); + if (!bt_addr_cmp(peer, &acl_conns[i].br.dst)) { + return bt_conn_ref(&acl_conns[i]); } } @@ -632,7 +634,7 @@ struct bt_conn *bt_conn_add_sco(const bt_addr_t *peer, int link_type) struct bt_conn *bt_conn_add_br(const bt_addr_t *peer) { - struct bt_conn *conn = conn_new(); + struct bt_conn *conn = acl_conn_new(); if (!conn) { return NULL; @@ -872,7 +874,7 @@ void bt_conn_cb_register(struct bt_conn_cb *cb) callback_list = cb; } -static void bt_conn_reset_rx_state(struct bt_conn *conn) +void bt_conn_reset_rx_state(struct bt_conn *conn) { if (!conn->rx_len) { return; @@ -895,6 +897,12 @@ void bt_conn_recv(struct bt_conn *conn, struct net_buf *buf, uint8_t flags) BT_DBG("handle %u len %u flags %02x", conn->handle, buf->len, flags); + if (IS_ENABLED(CONFIG_BT_ISO) && + conn->type == BT_CONN_TYPE_ISO) { + bt_iso_recv(conn, buf, flags); + return; + } + /* Check packet boundary flags */ switch (flags) { case BT_ACL_START: @@ -1044,11 +1052,74 @@ int bt_conn_send_cb(struct bt_conn *conn, struct net_buf *buf, return 0; } +enum { + FRAG_START, + FRAG_CONT, + FRAG_SINGLE, + FRAG_END +}; + +static int send_acl(struct bt_conn *conn, struct net_buf *buf, uint8_t flags) +{ + struct bt_hci_acl_hdr *hdr; + + switch (flags) { + case FRAG_START: + case FRAG_SINGLE: + flags = BT_ACL_START_NO_FLUSH; + break; + case FRAG_CONT: + case FRAG_END: + flags = BT_ACL_CONT; + break; + default: + return -EINVAL; + } + + hdr = net_buf_push(buf, sizeof(*hdr)); + hdr->handle = sys_cpu_to_le16(bt_acl_handle_pack(conn->handle, flags)); + hdr->len = sys_cpu_to_le16(buf->len - sizeof(*hdr)); + + bt_buf_set_type(buf, BT_BUF_ACL_OUT); + + return bt_send(buf); +} + +static int send_iso(struct bt_conn *conn, struct net_buf *buf, uint8_t flags) +{ + struct bt_hci_iso_hdr *hdr; + + switch (flags) { + case FRAG_START: + flags = BT_ISO_START; + break; + case FRAG_CONT: + flags = BT_ISO_CONT; + break; + case FRAG_SINGLE: + flags = BT_ISO_SINGLE; + break; + case FRAG_END: + flags = BT_ISO_END; + break; + default: + return -EINVAL; + } + + hdr = net_buf_push(buf, sizeof(*hdr)); + hdr->handle = sys_cpu_to_le16(bt_iso_handle_pack(conn->handle, flags, + 0)); + hdr->len = sys_cpu_to_le16(buf->len - sizeof(*hdr)); + + bt_buf_set_type(buf, BT_BUF_ISO_OUT); + + return bt_send(buf); +} + static bool send_frag(struct bt_conn *conn, struct net_buf *buf, uint8_t flags, bool always_consume) { struct bt_conn_tx *tx = tx_data(buf)->tx; - struct bt_hci_acl_hdr *hdr; uint32_t *pending_no_cb; unsigned int key; int err; @@ -1064,10 +1135,6 @@ static bool send_frag(struct bt_conn *conn, struct net_buf *buf, uint8_t flags, goto fail; } - hdr = net_buf_push(buf, sizeof(*hdr)); - hdr->handle = sys_cpu_to_le16(bt_acl_handle_pack(conn->handle, flags)); - hdr->len = sys_cpu_to_le16(buf->len - sizeof(*hdr)); - /* Add to pending, it must be done before bt_buf_set_type */ key = irq_lock(); if (tx) { @@ -1086,9 +1153,12 @@ static bool send_frag(struct bt_conn *conn, struct net_buf *buf, uint8_t flags, } irq_unlock(key); - bt_buf_set_type(buf, BT_BUF_ACL_OUT); + if (IS_ENABLED(CONFIG_BT_ISO) && conn->type == BT_CONN_TYPE_ISO) { + err = send_iso(conn, buf, flags); + } else { + err = send_acl(conn, buf, flags); + } - err = bt_send(buf); if (err) { BT_ERR("Unable to send to driver (err %d)", err); key = irq_lock(); @@ -1120,12 +1190,16 @@ fail: static inline uint16_t conn_mtu(struct bt_conn *conn) { #if defined(CONFIG_BT_BREDR) - if (conn->type == BT_CONN_TYPE_BR || !bt_dev.le.mtu) { + if (conn->type == BT_CONN_TYPE_BR || !bt_dev.le.acl_mtu) { return bt_dev.br.mtu; } #endif /* CONFIG_BT_BREDR */ - - return bt_dev.le.mtu; +#if defined(CONFIG_BT_ISO) + if (conn->type == BT_CONN_TYPE_ISO && bt_dev.le.iso_mtu) { + return bt_dev.le.iso_mtu; + } +#endif /* CONFIG_BT_ISO */ + return bt_dev.le.acl_mtu; } static struct net_buf *create_frag(struct bt_conn *conn, struct net_buf *buf) @@ -1133,7 +1207,15 @@ static struct net_buf *create_frag(struct bt_conn *conn, struct net_buf *buf) struct net_buf *frag; uint16_t frag_len; - frag = bt_conn_create_frag(0); + switch (conn->type) { +#if defined(CONFIG_BT_ISO) + case BT_CONN_TYPE_ISO: + frag = bt_iso_create_frag(0); + break; +#endif + default: + frag = bt_conn_create_frag(0); + } if (conn->state != BT_CONN_CONNECTED) { net_buf_unref(frag); @@ -1159,7 +1241,7 @@ static bool send_buf(struct bt_conn *conn, struct net_buf *buf) /* Send directly if the packet fits the ACL MTU */ if (buf->len <= conn_mtu(conn)) { - return send_frag(conn, buf, BT_ACL_START_NO_FLUSH, false); + return send_frag(conn, buf, FRAG_SINGLE, false); } /* Create & enqueue first fragment */ @@ -1168,7 +1250,7 @@ static bool send_buf(struct bt_conn *conn, struct net_buf *buf) return false; } - if (!send_frag(conn, frag, BT_ACL_START_NO_FLUSH, true)) { + if (!send_frag(conn, frag, FRAG_START, true)) { return false; } @@ -1182,12 +1264,12 @@ static bool send_buf(struct bt_conn *conn, struct net_buf *buf) return false; } - if (!send_frag(conn, frag, BT_ACL_CONT, true)) { + if (!send_frag(conn, frag, FRAG_CONT, true)) { return false; } } - return send_frag(conn, buf, BT_ACL_CONT, false); + return send_frag(conn, buf, FRAG_END, false); } static struct k_poll_signal conn_change = @@ -1214,9 +1296,38 @@ static void conn_cleanup(struct bt_conn *conn) k_delayed_work_submit(&conn->update_work, K_NO_WAIT); } +static int conn_prepare_events(struct bt_conn *conn, + struct k_poll_event *events) +{ + if (!atomic_get(&conn->ref)) { + return -ENOTCONN; + } + + if (conn->state == BT_CONN_DISCONNECTED && + atomic_test_and_clear_bit(conn->flags, BT_CONN_CLEANUP)) { + conn_cleanup(conn); + return -ENOTCONN; + } + + if (conn->state != BT_CONN_CONNECTED) { + return -ENOTCONN; + } + + BT_DBG("Adding conn %p to poll list", conn); + + k_poll_event_init(&events[0], + K_POLL_TYPE_FIFO_DATA_AVAILABLE, + K_POLL_MODE_NOTIFY_ONLY, + &conn->tx_queue); + events[0].tag = BT_EVENT_CONN_TX_QUEUE; + + return 0; +} + int bt_conn_prepare_events(struct k_poll_event events[]) { int i, ev_count = 0; + struct bt_conn *conn; BT_DBG(""); @@ -1224,32 +1335,24 @@ int bt_conn_prepare_events(struct k_poll_event events[]) k_poll_event_init(&events[ev_count++], K_POLL_TYPE_SIGNAL, K_POLL_MODE_NOTIFY_ONLY, &conn_change); - for (i = 0; i < ARRAY_SIZE(conns); i++) { - struct bt_conn *conn = &conns[i]; + for (i = 0; i < ARRAY_SIZE(acl_conns); i++) { + conn = &acl_conns[i]; - if (!atomic_get(&conn->ref)) { - continue; + if (!conn_prepare_events(conn, &events[ev_count])) { + ev_count++; } - - if (conn->state == BT_CONN_DISCONNECTED && - atomic_test_and_clear_bit(conn->flags, BT_CONN_CLEANUP)) { - conn_cleanup(conn); - continue; - } - - if (conn->state != BT_CONN_CONNECTED) { - continue; - } - - BT_DBG("Adding conn %p to poll list", conn); - - k_poll_event_init(&events[ev_count], - K_POLL_TYPE_FIFO_DATA_AVAILABLE, - K_POLL_MODE_NOTIFY_ONLY, - &conn->tx_queue); - events[ev_count++].tag = BT_EVENT_CONN_TX_QUEUE; } +#if defined(CONFIG_BT_ISO) + for (i = 0; i < ARRAY_SIZE(iso_conns); i++) { + conn = &iso_conns[i]; + + if (!conn_prepare_events(conn, &events[ev_count])) { + ev_count++; + } + } +#endif + return ev_count; } @@ -1297,7 +1400,7 @@ bool bt_conn_exists_le(uint8_t id, const bt_addr_le_t *peer) struct bt_conn *bt_conn_add_le(uint8_t id, const bt_addr_le_t *peer) { - struct bt_conn *conn = conn_new(); + struct bt_conn *conn = acl_conn_new(); if (!conn) { return NULL; @@ -1353,6 +1456,54 @@ static void process_unack_tx(struct bt_conn *conn) } } +struct bt_conn *conn_lookup_handle(struct bt_conn *conns, size_t size, + uint16_t handle) +{ + int i; + + for (i = 0; i < size; i++) { + if (!atomic_get(&conns[i].ref)) { + continue; + } + + /* We only care about connections with a valid handle */ + if (!bt_conn_is_handle_valid(&conns[i])) { + continue; + } + + if (conns[i].handle == handle) { + return bt_conn_ref(&conns[i]); + } + } + + return NULL; +} + +struct bt_conn *conn_lookup_iso(struct bt_conn *conn) +{ +#if defined(CONFIG_BT_ISO) + int i; + + for (i = 0; i < ARRAY_SIZE(iso_conns); i++) { + if (!atomic_get(&iso_conns[i].ref)) { + continue; + } + + if (&iso_conns[i] == conn) { + return bt_conn_ref(conn); + } + + if (iso_conns[i].iso.acl == conn) { + return bt_conn_ref(&iso_conns[i]); + } + } + + return NULL; +#else + return NULL; +#endif /* CONFIG_BT_ISO */ +} + void bt_conn_set_state(struct bt_conn *conn, bt_conn_state_t state) { bt_conn_state_t old_state; @@ -1396,6 +1547,12 @@ void bt_conn_set_state(struct bt_conn *conn, bt_conn_state_t state) k_fifo_init(&conn->tx_queue); k_poll_signal_raise(&conn_change, 0); + if (IS_ENABLED(CONFIG_BT_ISO) && + conn->type == BT_CONN_TYPE_ISO) { + bt_iso_connected(conn); + break; + } + sys_slist_init(&conn->channels); bt_l2cap_connected(conn); @@ -1408,6 +1565,17 @@ void bt_conn_set_state(struct bt_conn *conn, bt_conn_state_t state) break; } + if (IS_ENABLED(CONFIG_BT_ISO)) { + struct bt_conn *iso; + + iso = conn_lookup_iso(conn); + if (iso) { + bt_iso_disconnected(iso); + bt_iso_cleanup(iso); + bt_conn_unref(iso); + } + } + /* Notify disconnection and queue a dummy buffer to wake * up and stop the tx thread for states where it was * running. @@ -1514,37 +1682,24 @@ void bt_conn_set_state(struct bt_conn *conn, bt_conn_state_t state) struct bt_conn *bt_conn_lookup_handle(uint16_t handle) { - int i; + struct bt_conn *conn; - for (i = 0; i < ARRAY_SIZE(conns); i++) { - if (!atomic_get(&conns[i].ref)) { - continue; - } - - /* We only care about connections with a valid handle */ - if (!bt_conn_is_handle_valid(&conns[i])) { - continue; - } - - if (conns[i].handle == handle) { - return bt_conn_ref(&conns[i]); - } + conn = conn_lookup_handle(acl_conns, ARRAY_SIZE(acl_conns), handle); + if (conn) { + return conn; } -#if defined(CONFIG_BT_BREDR) - for (i = 0; i < ARRAY_SIZE(sco_conns); i++) { - if (!atomic_get(&sco_conns[i].ref)) { - continue; - } +#if defined(CONFIG_BT_ISO) + conn = conn_lookup_handle(iso_conns, ARRAY_SIZE(iso_conns), handle); + if (conn) { + return conn; + } +#endif - /* We only care about connections with a valid handle */ - if (!bt_conn_is_handle_valid(&conns[i])) { - continue; - } - - if (sco_conns[i].handle == handle) { - return bt_conn_ref(&sco_conns[i]); - } + #if defined(CONFIG_BT_BREDR) + conn = conn_lookup_handle(sco_conns, ARRAY_SIZE(sco_conns), handle); + if (conn) { + return conn; } #endif @@ -1575,17 +1730,17 @@ struct bt_conn *bt_conn_lookup_addr_le(uint8_t id, const bt_addr_le_t *peer) { int i; - for (i = 0; i < ARRAY_SIZE(conns); i++) { - if (!atomic_get(&conns[i].ref)) { + for (i = 0; i < ARRAY_SIZE(acl_conns); i++) { + if (!atomic_get(&acl_conns[i].ref)) { continue; } - if (conns[i].type != BT_CONN_TYPE_LE) { + if (acl_conns[i].type != BT_CONN_TYPE_LE) { continue; } - if (bt_conn_is_peer_addr_le(&conns[i], id, peer)) { - return bt_conn_ref(&conns[i]); + if (bt_conn_is_peer_addr_le(&acl_conns[i], id, peer)) { + return bt_conn_ref(&acl_conns[i]); } } @@ -1597,21 +1752,21 @@ struct bt_conn *bt_conn_lookup_state_le(uint8_t id, const bt_addr_le_t *peer, { int i; - for (i = 0; i < ARRAY_SIZE(conns); i++) { - if (!atomic_get(&conns[i].ref)) { + for (i = 0; i < ARRAY_SIZE(acl_conns); i++) { + if (!atomic_get(&acl_conns[i].ref)) { continue; } - if (conns[i].type != BT_CONN_TYPE_LE) { + if (acl_conns[i].type != BT_CONN_TYPE_LE) { continue; } - if (peer && !bt_conn_is_peer_addr_le(&conns[i], id, peer)) { + if (peer && !bt_conn_is_peer_addr_le(&acl_conns[i], id, peer)) { continue; } - if (conns[i].state == state && conns[i].id == id) { - return bt_conn_ref(&conns[i]); + if (acl_conns[i].state == state && acl_conns[i].id == id) { + return bt_conn_ref(&acl_conns[i]); } } @@ -1623,16 +1778,16 @@ void bt_conn_foreach(int type, void (*func)(struct bt_conn *conn, void *data), { int i; - for (i = 0; i < ARRAY_SIZE(conns); i++) { - if (!atomic_get(&conns[i].ref)) { + for (i = 0; i < ARRAY_SIZE(acl_conns); i++) { + if (!atomic_get(&acl_conns[i].ref)) { continue; } - if (!(conns[i].type & type)) { + if (!(acl_conns[i].type & type)) { continue; } - func(&conns[i], data); + func(&acl_conns[i], data); } #if defined(CONFIG_BT_BREDR) if (type & BT_CONN_TYPE_SCO) { @@ -1645,6 +1800,18 @@ void bt_conn_foreach(int type, void (*func)(struct bt_conn *conn, void *data), } } #endif /* defined(CONFIG_BT_BREDR) */ + +#if defined(CONFIG_BT_ISO) + if (type & BT_CONN_TYPE_ISO) { + for (i = 0; i < ARRAY_SIZE(iso_conns); i++) { + if (!atomic_get(&iso_conns[i].ref)) { + continue; + } + + func(&iso_conns[i], data); + } + } +#endif /* defined(CONFIG_BT_ISO) */ } struct bt_conn *bt_conn_ref(struct bt_conn *conn) @@ -2405,9 +2572,29 @@ int bt_conn_auth_pairing_confirm(struct bt_conn *conn) uint8_t bt_conn_index(struct bt_conn *conn) { - uint8_t index = conn - conns; + uint8_t index; + + switch (conn->type) { +#if defined(CONFIG_BT_ISO) + case BT_CONN_TYPE_ISO: + index = conn - iso_conns; + __ASSERT(index < CONFIG_BT_MAX_ISO_CONN, + "Invalid bt_conn pointer"); + break; +#endif +#if defined(CONFIG_BT_BREDR) + case BT_CONN_TYPE_SCO: + index = conn - sco_conns; + __ASSERT(index < CONFIG_BT_MAX_SCO_CONN, + "Invalid bt_conn pointer"); + break; +#endif + default: + index = conn - acl_conns; + __ASSERT(index < CONFIG_BT_MAX_CONN, "Invalid bt_conn pointer"); + break; + } - __ASSERT(index < CONFIG_BT_MAX_CONN, "Invalid bt_conn pointer"); return index; } @@ -2415,11 +2602,11 @@ struct bt_conn *bt_conn_lookup_index(uint8_t index) { struct bt_conn *conn; - if (index >= ARRAY_SIZE(conns)) { + if (index >= ARRAY_SIZE(acl_conns)) { return NULL; } - conn = &conns[index]; + conn = &acl_conns[index]; if (!atomic_get(&conn->ref)) { return NULL; @@ -2447,8 +2634,8 @@ int bt_conn_init(void) /* Initialize background scan */ if (IS_ENABLED(CONFIG_BT_CENTRAL)) { - for (i = 0; i < ARRAY_SIZE(conns); i++) { - struct bt_conn *conn = &conns[i]; + for (i = 0; i < ARRAY_SIZE(acl_conns); i++) { + struct bt_conn *conn = &acl_conns[i]; if (!atomic_get(&conn->ref)) { continue; diff --git a/subsys/bluetooth/host/conn_internal.h b/subsys/bluetooth/host/conn_internal.h index 9802de0701a..6420c485726 100644 --- a/subsys/bluetooth/host/conn_internal.h +++ b/subsys/bluetooth/host/conn_internal.h @@ -96,6 +96,15 @@ struct bt_conn_sco { }; #endif +struct bt_conn_iso { + /* Reference to ACL Connection */ + struct bt_conn *acl; + /* CIG ID */ + uint8_t cig_id; + /* CIS ID */ + uint8_t cis_id; +}; + typedef void (*bt_conn_tx_cb_t)(struct bt_conn *conn, void *user_data); struct bt_conn_tx { @@ -147,7 +156,7 @@ struct bt_conn { /* Queue for outgoing ACL data */ struct k_fifo tx_queue; - /* Active L2CAP channels */ + /* Active L2CAP/ISO channels */ sys_slist_t channels; atomic_t ref; @@ -160,6 +169,9 @@ struct bt_conn { #if defined(CONFIG_BT_BREDR) struct bt_conn_br br; struct bt_conn_sco sco; +#endif +#if defined(CONFIG_BT_AUDIO) + struct bt_conn_iso iso; #endif }; @@ -172,6 +184,8 @@ struct bt_conn { #endif }; +void bt_conn_reset_rx_state(struct bt_conn *conn); + /* Process incoming data for a connection */ void bt_conn_recv(struct bt_conn *conn, struct net_buf *buf, uint8_t flags); @@ -190,6 +204,26 @@ bool bt_conn_exists_le(uint8_t id, const bt_addr_le_t *peer); /* Add a new LE connection */ struct bt_conn *bt_conn_add_le(uint8_t id, const bt_addr_le_t *peer); +/** Connection parameters for ISO connections */ +struct bt_iso_create_param { + uint8_t id; + uint8_t num_conns; + struct bt_conn **conns; + struct bt_iso_chan **chans; +}; + +/* Bind ISO connections parameters */ +int bt_conn_bind_iso(struct bt_iso_create_param *param); + +/* Connect ISO connections */ +int bt_conn_connect_iso(struct bt_conn **conns, uint8_t num_conns); + +/* Add a new ISO connection */ +struct bt_conn *bt_conn_add_iso(struct bt_conn *acl); + +/* Cleanup ISO references */ +void bt_iso_cleanup(struct bt_conn *iso_conn); + /* Add a new BR/EDR connection */ struct bt_conn *bt_conn_add_br(const bt_addr_t *peer); @@ -207,14 +241,29 @@ struct bt_conn *bt_conn_lookup_addr_br(const bt_addr_t *peer); void bt_conn_disconnect_all(uint8_t id); +/* Allocate new connection object */ +struct bt_conn *bt_conn_new(struct bt_conn *conns, size_t size); + /* Look up an existing connection */ struct bt_conn *bt_conn_lookup_handle(uint16_t handle); static inline bool bt_conn_is_handle_valid(struct bt_conn *conn) { - return conn->state == BT_CONN_CONNECTED || - conn->state == BT_CONN_DISCONNECT || - conn->state == BT_CONN_DISCONNECT_COMPLETE; + switch (conn->state) { + case BT_CONN_CONNECTED: + case BT_CONN_DISCONNECT: + case BT_CONN_DISCONNECT_COMPLETE: + return true; + case BT_CONN_CONNECT: + /* ISO connection handle assigned at connect state */ + if (IS_ENABLED(CONFIG_BT_ISO) && + conn->type == BT_CONN_TYPE_ISO) { + return true; + } + __fallthrough; + default: + return false; + } } /* Check if the connection is with the given peer. */ diff --git a/subsys/bluetooth/host/hci_core.c b/subsys/bluetooth/host/hci_core.c index 0c2c917161b..24f379a211b 100644 --- a/subsys/bluetooth/host/hci_core.c +++ b/subsys/bluetooth/host/hci_core.c @@ -40,6 +40,7 @@ #include "ecc.h" #include "conn_internal.h" +#include "audio/iso_internal.h" #include "l2cap_internal.h" #include "gatt_internal.h" #include "smp.h" @@ -4974,6 +4975,12 @@ static const struct event_handler meta_events[] = { sizeof(struct bt_hci_evt_le_per_adv_sync_lost)), #endif /* defined(CONFIG_BT_PER_ADV_SYNC) */ #endif /* defined(CONFIG_BT_EXT_ADV) */ +#if defined(CONFIG_BT_ISO) + EVENT_HANDLER(BT_HCI_EVT_LE_CIS_ESTABLISHED, hci_le_cis_estabilished, + sizeof(struct bt_hci_evt_le_cis_established)), + EVENT_HANDLER(BT_HCI_EVT_LE_CIS_REQ, hci_le_cis_req, + sizeof(struct bt_hci_evt_le_cis_req)), +#endif /* (CONFIG_BT_ISO) */ }; static void hci_le_meta_event(struct net_buf *buf) @@ -5146,12 +5153,17 @@ static void process_events(struct k_poll_event *ev, int count) } #if defined(CONFIG_BT_CONN) +#if defined(CONFIG_BT_ISO) +/* command FIFO + conn_change signal + MAX_CONN + MAX_ISO_CONN */ +#define EV_COUNT (2 + CONFIG_BT_MAX_CONN + CONFIG_BT_MAX_ISO_CONN) +#else /* command FIFO + conn_change signal + MAX_CONN */ #define EV_COUNT (2 + CONFIG_BT_MAX_CONN) +#endif /* CONFIG_BT_ISO */ #else /* command FIFO */ #define EV_COUNT 1 -#endif +#endif /* CONFIG_BT_CONN */ static void hci_tx_thread(void *p1, void *p2, void *p3) { @@ -5235,16 +5247,16 @@ static void read_buffer_size_complete(struct net_buf *buf) BT_DBG("status 0x%02x", rp->status); /* If LE-side has buffers we can ignore the BR/EDR values */ - if (bt_dev.le.mtu) { + if (bt_dev.le.acl_mtu) { return; } - bt_dev.le.mtu = sys_le16_to_cpu(rp->acl_max_len); + bt_dev.le.acl_mtu = sys_le16_to_cpu(rp->acl_max_len); pkts = sys_le16_to_cpu(rp->acl_max_num); - BT_DBG("ACL BR/EDR buffers: pkts %u mtu %u", pkts, bt_dev.le.mtu); + BT_DBG("ACL BR/EDR buffers: pkts %u mtu %u", pkts, bt_dev.le.acl_mtu); - k_sem_init(&bt_dev.le.pkts, pkts, pkts); + k_sem_init(&bt_dev.le.acl_pkts, pkts, pkts); } #endif @@ -5255,16 +5267,72 @@ static void le_read_buffer_size_complete(struct net_buf *buf) BT_DBG("status 0x%02x", rp->status); - bt_dev.le.mtu = sys_le16_to_cpu(rp->le_max_len); - if (!bt_dev.le.mtu) { + bt_dev.le.acl_mtu = sys_le16_to_cpu(rp->le_max_len); + if (!bt_dev.le.acl_mtu) { return; } - BT_DBG("ACL LE buffers: pkts %u mtu %u", rp->le_max_num, bt_dev.le.mtu); + BT_DBG("ACL LE buffers: pkts %u mtu %u", rp->le_max_num, + bt_dev.le.acl_mtu); - k_sem_init(&bt_dev.le.pkts, rp->le_max_num, rp->le_max_num); + k_sem_init(&bt_dev.le.acl_pkts, rp->le_max_num, rp->le_max_num); } -#endif + +static void read_buffer_size_v2_complete(struct net_buf *buf) +{ +#if defined(CONFIG_BT_ISO) + struct bt_hci_rp_le_read_buffer_size_v2 *rp = (void *)buf->data; + uint8_t max_num; + + BT_DBG("status %u", rp->status); + + bt_dev.le.acl_mtu = sys_le16_to_cpu(rp->acl_mtu); + if (!bt_dev.le.acl_mtu) { + return; + } + + BT_DBG("ACL LE buffers: pkts %u mtu %u", rp->acl_max_pkt, + bt_dev.le.acl_mtu); + + max_num = MIN(rp->acl_max_pkt, CONFIG_BT_CONN_TX_MAX); + k_sem_init(&bt_dev.le.acl_pkts, max_num, max_num); + + bt_dev.le.iso_mtu = sys_le16_to_cpu(rp->iso_mtu); + if (!bt_dev.le.iso_mtu) { + BT_ERR("ISO buffer size not set"); + return; + } + + BT_DBG("ISO buffers: pkts %u mtu %u", rp->iso_max_pkt, + bt_dev.le.iso_mtu); + + max_num = MIN(rp->iso_max_pkt, CONFIG_BT_ISO_TX_BUF_COUNT); + k_sem_init(&bt_dev.le.iso_pkts, max_num, max_num); +#endif /* CONFIG_BT_ISO */ +} + +static int le_set_host_feature(uint8_t bit_number, uint8_t bit_value) +{ +#if defined(CONFIG_BT_ISO) + struct bt_hci_cp_le_set_host_feature *cp; + struct net_buf *buf; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_HOST_FEATURE, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->bit_number = bit_number; + cp->bit_value = bit_value; + + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_HOST_FEATURE, buf, NULL); +#else + return -ENOTSUP; +#endif /* CONFIG_BT_ISO */ +} + +#endif /* CONFIG_BT_CONN */ static void read_supported_commands_complete(struct net_buf *buf) { @@ -5454,6 +5522,18 @@ static int le_set_event_mask(void) mask |= BT_EVT_MASK_LE_GENERATE_DHKEY_COMPLETE; } + /* + * Enable CIS events only if ISO connections are enabled and controller + * support them. + */ + if (IS_ENABLED(CONFIG_BT_ISO) && + BT_FEAT_LE_CIS(bt_dev.le.features)) { + mask |= BT_EVT_MASK_LE_CIS_ESTABLISHED; + if (BT_FEAT_LE_CIS_SLAVE(bt_dev.le.features)) { + mask |= BT_EVT_MASK_LE_CIS_REQ; + } + } + sys_put_le64(mask, cp_mask->events); return bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_EVENT_MASK, buf, NULL); } @@ -5481,15 +5561,32 @@ static int le_init(void) net_buf_unref(rsp); #if defined(CONFIG_BT_CONN) - /* Read LE Buffer Size */ - err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_BUFFER_SIZE, - NULL, &rsp); - if (err) { - return err; + if (IS_ENABLED(CONFIG_BT_ISO) && + BT_FEAT_LE_ISO(bt_dev.le.features)) { + /* Set Isochronus Channels - Host support */ + err = le_set_host_feature(BT_LE_FEAT_BIT_ISO_CHANNELS, 1); + if (err) { + return err; + } + /* Read ISO Buffer Size V2 */ + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_BUFFER_SIZE_V2, + NULL, &rsp); + if (err) { + return err; + } + read_buffer_size_v2_complete(rsp); + net_buf_unref(rsp); + } else { + /* Read LE Buffer Size */ + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_BUFFER_SIZE, + NULL, &rsp); + if (err) { + return err; + } + le_read_buffer_size_complete(rsp); + net_buf_unref(rsp); } - le_read_buffer_size_complete(rsp); - net_buf_unref(rsp); -#endif +#endif /* CONFIG_BT_CONN */ if (BT_FEAT_BREDR(bt_dev.features)) { buf = bt_hci_cmd_create(BT_HCI_OP_LE_WRITE_LE_HOST_SUPP, @@ -5782,7 +5879,7 @@ static int br_init(void) struct net_buf *rsp; int err; - if (bt_dev.le.mtu) { + if (bt_dev.le.acl_mtu) { return 0; } @@ -6257,7 +6354,17 @@ int bt_recv(struct net_buf *buf) } #endif return 0; + } +#if defined(CONFIG_BT_ISO) + case BT_BUF_ISO_IN: +#if defined(CONFIG_BT_RECV_IS_RX_THREAD) + hci_iso(buf); +#else + net_buf_put(&bt_dev.rx_queue, buf); +#endif + return 0; +#endif /* CONFIG_BT_ISO */ default: BT_ERR("Invalid buf type %u", bt_buf_get_type(buf)); net_buf_unref(buf); @@ -6372,6 +6479,11 @@ static void hci_rx_thread(void) hci_acl(buf); break; #endif /* CONFIG_BT_CONN */ +#if defined(CONFIG_BT_ISO) + case BT_BUF_ISO_IN: + hci_iso(buf); + break; +#endif /* CONFIG_BT_ISO */ case BT_BUF_EVT: hci_event(buf); break; @@ -8613,8 +8725,12 @@ struct net_buf *bt_buf_get_rx(enum bt_buf_type type, k_timeout_t timeout) { struct net_buf *buf; - __ASSERT(type == BT_BUF_EVT || type == BT_BUF_ACL_IN, - "Invalid buffer type requested"); + __ASSERT(type == BT_BUF_EVT || type == BT_BUF_ACL_IN || + type == BT_BUF_ISO_IN, "Invalid buffer type requested"); + + if (IS_ENABLED(CONFIG_BT_ISO) && type == BT_BUF_ISO_IN) { + return bt_iso_get_rx(timeout); + } #if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL) if (type == BT_BUF_EVT) { diff --git a/subsys/bluetooth/host/hci_core.h b/subsys/bluetooth/host/hci_core.h index f533abe6273..1b4c217e067 100644 --- a/subsys/bluetooth/host/hci_core.h +++ b/subsys/bluetooth/host/hci_core.h @@ -180,8 +180,14 @@ struct bt_dev_le { #if defined(CONFIG_BT_CONN) /* Controller buffer information */ - uint16_t mtu; + uint16_t mtu; struct k_sem pkts; + uint16_t acl_mtu; + struct k_sem acl_pkts; +#if defined(CONFIG_BT_ISO) + uint16_t iso_mtu; + struct k_sem iso_pkts; +#endif /* CONFIG_BT_ISO */ #endif /* CONFIG_BT_CONN */ #if defined(CONFIG_BT_SMP)