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:
parent
4948b594da
commit
81d34ecd19
14 changed files with 2085 additions and 150 deletions
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
256
include/bluetooth/iso.h
Normal 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_ */
|
|
@ -87,3 +87,5 @@ if(CONFIG_BT_CONN_DISABLE_SECURITY)
|
|||
Do not use in production."
|
||||
)
|
||||
endif()
|
||||
|
||||
add_subdirectory_ifdef(CONFIG_BT_AUDIO audio)
|
||||
|
|
|
@ -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
|
||||
|
|
6
subsys/bluetooth/host/audio/CMakeLists.txt
Normal file
6
subsys/bluetooth/host/audio/CMakeLists.txt
Normal 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)
|
107
subsys/bluetooth/host/audio/Kconfig
Normal file
107
subsys/bluetooth/host/audio/Kconfig
Normal 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
|
1086
subsys/bluetooth/host/audio/iso.c
Normal file
1086
subsys/bluetooth/host/audio/iso.c
Normal file
File diff suppressed because it is too large
Load diff
110
subsys/bluetooth/host/audio/iso_internal.h
Normal file
110
subsys/bluetooth/host/audio/iso_internal.h
Normal 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);
|
|
@ -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;
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue