Bluetooth: Audio: Initial ISO channel support

This adds initial code for handling ISO channels.

Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This commit is contained in:
Luiz Augusto von Dentz 2020-05-11 15:13:59 -07:00 committed by Carles Cufí
commit 81d34ecd19
14 changed files with 2085 additions and 150 deletions

View file

@ -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,
};

View file

@ -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 */

View file

@ -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))

256
include/bluetooth/iso.h Normal file
View file

@ -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 <sys/atomic.h>
#include <bluetooth/buf.h>
#include <bluetooth/conn.h>
#include <bluetooth/hci.h>
/** @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_ */

View file

@ -87,3 +87,5 @@ if(CONFIG_BT_CONN_DISABLE_SECURITY)
Do not use in production."
)
endif()
add_subdirectory_ifdef(CONFIG_BT_AUDIO audio)

View file

@ -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

View file

@ -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)

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,110 @@
/** @file
* @brief Internal APIs for Bluetooth ISO handling.
*/
/*
* Copyright (c) 2020 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <bluetooth/iso.h>
#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);

View file

@ -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;

View file

@ -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. */

View file

@ -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) {

View file

@ -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)