diff --git a/include/zephyr/bluetooth/hci_types.h b/include/zephyr/bluetooth/hci_types.h index e7d20350f48..5b178a99bb4 100644 --- a/include/zephyr/bluetooth/hci_types.h +++ b/include/zephyr/bluetooth/hci_types.h @@ -3456,6 +3456,32 @@ struct bt_hci_evt_le_subrate_change { uint16_t supervision_timeout; } __packed; +#define BT_HCI_EVT_LE_CIS_ESTABLISHED_V2 0x2a +struct bt_hci_evt_le_cis_established_v2 { + uint8_t status; + uint16_t conn_handle; + uint8_t cig_sync_delay[3]; + uint8_t cis_sync_delay[3]; + uint8_t c_latency[3]; + uint8_t p_latency[3]; + uint8_t c_phy; + uint8_t p_phy; + uint8_t nse; + uint8_t c_bn; + uint8_t p_bn; + uint8_t c_ft; + uint8_t p_ft; + uint16_t c_max_pdu; + uint16_t p_max_pdu; + uint16_t interval; + uint8_t sub_interval[3]; + uint16_t c_max_sdu; + uint16_t p_max_sdu; + uint8_t c_sdu_interval[3]; + uint8_t p_sdu_interval[3]; + uint8_t framing; +} __packed; + #define BT_HCI_LE_CS_INITIATOR_ROLE_MASK BIT(0) #define BT_HCI_LE_CS_REFLECTOR_ROLE_MASK BIT(1) @@ -3926,6 +3952,7 @@ struct bt_hci_evt_le_cs_procedure_enable_complete { #define BT_EVT_MASK_LE_PER_ADV_SUBEVENT_DATA_REQ BT_EVT_BIT(38) #define BT_EVT_MASK_LE_PER_ADV_RESPONSE_REPORT BT_EVT_BIT(39) #define BT_EVT_MASK_LE_ENH_CONN_COMPLETE_V2 BT_EVT_BIT(40) +#define BT_EVT_MASK_LE_CIS_ESTABLISHED_V2 BT_EVT_BIT(41) #define BT_EVT_MASK_LE_CS_READ_REMOTE_SUPPORTED_CAPABILITIES_COMPLETE BT_EVT_BIT(43) #define BT_EVT_MASK_LE_CS_READ_REMOTE_FAE_TABLE_COMPLETE BT_EVT_BIT(44) diff --git a/include/zephyr/bluetooth/iso.h b/include/zephyr/bluetooth/iso.h index fea94f6212c..ff62661b9e8 100644 --- a/include/zephyr/bluetooth/iso.h +++ b/include/zephyr/bluetooth/iso.h @@ -68,6 +68,8 @@ extern "C" { /** Value to set the ISO data path over HCi. */ #define BT_ISO_DATA_PATH_HCI 0x00 +/** Unknown SDU interval */ +#define BT_ISO_SDU_INTERVAL_UNKNOWN 0x000000U /** Minimum interval value in microseconds */ #define BT_ISO_SDU_INTERVAL_MIN 0x0000FFU /** Maximum interval value in microseconds */ @@ -140,6 +142,10 @@ extern "C" { #define BT_ISO_PTO_MIN 0x00U /** Maximum pre-transmission offset */ #define BT_ISO_PTO_MAX 0x0FU +/** No subinterval */ +#define BT_ISO_SUBINTERVAL_NONE 0x00000000U +/** Unknown subinterval */ +#define BT_ISO_SUBINTERVAL_UNKNOWN 0xFFFFFFFFU /** * @brief Check if ISO BIS bitfield is valid (BT_ISO_BIS_INDEX_BIT(1)|..|BT_ISO_BIS_INDEX_BIT(31)) @@ -967,6 +973,18 @@ struct bt_iso_unicast_tx_info { /** The burst number */ uint8_t bn; + + /** The maximum SDU size in octets + * + * May be set to @ref bt_iso_unicast_tx_info.max_pdu for peripherals if unknown + */ + uint16_t max_sdu; + + /** The SDU interval in microseconds + * + * May be set to @ref BT_ISO_SDU_INTERVAL_UNKNOWN for if unknown. + */ + uint32_t sdu_interval; }; /** @brief ISO Unicast Info Structure */ @@ -977,6 +995,14 @@ struct bt_iso_unicast_info { /** The maximum time in us for all PDUs of this CIS in a CIG event */ uint32_t cis_sync_delay; + /** + * @brief The subinterval in microseconds + * + * Will be @ref BT_ISO_SUBINTERVAL_NONE if there is no subinterval (NSE = 1). + * Will be @ref BT_ISO_SUBINTERVAL_UNKNOWN if unknown. + */ + uint32_t subinterval; + /** @brief TX information for the central to peripheral data path */ struct bt_iso_unicast_tx_info central; diff --git a/subsys/bluetooth/host/hci_core.c b/subsys/bluetooth/host/hci_core.c index f093963ab4a..f5aa584e4cd 100644 --- a/subsys/bluetooth/host/hci_core.c +++ b/subsys/bluetooth/host/hci_core.c @@ -7,6 +7,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include #include #include #include @@ -2794,6 +2795,8 @@ static const struct event_handler meta_events[] = { #if defined(CONFIG_BT_ISO_UNICAST) EVENT_HANDLER(BT_HCI_EVT_LE_CIS_ESTABLISHED, hci_le_cis_established, sizeof(struct bt_hci_evt_le_cis_established)), + EVENT_HANDLER(BT_HCI_EVT_LE_CIS_ESTABLISHED_V2, hci_le_cis_established_v2, + sizeof(struct bt_hci_evt_le_cis_established_v2)), #if defined(CONFIG_BT_ISO_PERIPHERAL) EVENT_HANDLER(BT_HCI_EVT_LE_CIS_REQ, hci_le_cis_req, sizeof(struct bt_hci_evt_le_cis_req)), @@ -3417,6 +3420,7 @@ static int le_set_event_mask(void) if (IS_ENABLED(CONFIG_BT_ISO) && BT_FEAT_LE_CIS(bt_dev.le.features)) { mask |= BT_EVT_MASK_LE_CIS_ESTABLISHED; + mask |= BT_EVT_MASK_LE_CIS_ESTABLISHED_V2; if (BT_FEAT_LE_CIS_PERIPHERAL(bt_dev.le.features)) { mask |= BT_EVT_MASK_LE_CIS_REQ; } diff --git a/subsys/bluetooth/host/iso.c b/subsys/bluetooth/host/iso.c index 28674f7e5b9..4b05178d906 100644 --- a/subsys/bluetooth/host/iso.c +++ b/subsys/bluetooth/host/iso.c @@ -1096,12 +1096,80 @@ void bt_iso_cleanup_acl(struct bt_conn *iso) } } -static void store_cis_info(const struct bt_hci_evt_le_cis_established *evt, - struct bt_iso_info *info) +static void store_cis_info(const struct bt_hci_evt_le_cis_established *evt, struct bt_conn *iso) { + struct bt_conn_iso *iso_conn = &iso->iso; + struct bt_iso_info *info = &iso_conn->info; struct bt_iso_unicast_info *unicast_info = &info->unicast; - struct bt_iso_unicast_tx_info *central = &unicast_info->central; struct bt_iso_unicast_tx_info *peripheral = &unicast_info->peripheral; + struct bt_iso_unicast_tx_info *central = &unicast_info->central; + const uint8_t c_phy = bt_get_phy(evt->c_phy); + const uint8_t p_phy = bt_get_phy(evt->p_phy); + struct bt_iso_chan_io_qos *tx; + struct bt_iso_chan_io_qos *rx; + struct bt_iso_chan *chan; + + iso_conn = &iso->iso; + chan = iso_conn->chan; + rx = chan->qos->rx; + tx = chan->qos->tx; + + LOG_DBG("iso_chan %p tx %p rx %p", chan, tx, rx); + + if (iso->role == BT_HCI_ROLE_PERIPHERAL) { + /* As of BT Core 6.0, we can only get the SDU size if the controller + * supports bt_hci_evt_le_cis_established_v2. Since this is not guaranteeds, + * we fallback to using the PDU size as the SDU size. + */ + if (rx != NULL) { + rx->phy = c_phy; + rx->sdu = sys_le16_to_cpu(evt->c_max_pdu); + } + + if (tx != NULL) { + tx->phy = p_phy; + tx->sdu = sys_le16_to_cpu(evt->p_max_pdu); + } + + iso_conn->info.type = BT_ISO_CHAN_TYPE_CONNECTED; + } else { + /* values are already set for central - Verify */ + if (tx != NULL && tx->phy != c_phy) { + LOG_WRN("Unexpected C to P PHY: %u != %u", c_phy, tx->phy); + /* We assume that tx->phy has become invalid, and will use the event from + * the controller as the truth + */ + tx->phy = c_phy; + } + + if (rx != NULL && rx->phy != p_phy) { + LOG_WRN("Unexpected P to C max SDU: %u != %u", p_phy, rx->phy); + /* We assume that rx->phy has become invalid, and will use the event from + * the controller as the truth + */ + rx->phy = p_phy; + } + } + + /* Verify if device can send */ + iso_conn->info.can_send = false; + if (tx != NULL) { + if (iso->role == BT_HCI_ROLE_PERIPHERAL && evt->p_bn > 0) { + iso_conn->info.can_send = true; + } else if (iso->role == BT_HCI_ROLE_CENTRAL && evt->c_bn > 0) { + iso_conn->info.can_send = true; + } + } + + /* Verify if device can recv */ + iso_conn->info.can_recv = false; + if (rx != NULL) { + if (iso->role == BT_HCI_ROLE_PERIPHERAL && evt->c_bn > 0) { + iso_conn->info.can_recv = true; + } else if (iso->role == BT_HCI_ROLE_CENTRAL && evt->p_bn > 0) { + iso_conn->info.can_recv = true; + } + } info->iso_interval = sys_le16_to_cpu(evt->interval); info->max_subevent = evt->nse; @@ -1122,6 +1190,88 @@ static void store_cis_info(const struct bt_hci_evt_le_cis_established *evt, peripheral->max_pdu = sys_le16_to_cpu(evt->p_max_pdu); /* Transform to n * 1.25ms */ peripheral->flush_timeout = info->iso_interval * evt->p_ft; + + /* The following values are only available with bt_hci_evt_le_cis_established_v2 so + * initialize them to the "unknown" values + */ + unicast_info->subinterval = BT_ISO_SUBINTERVAL_UNKNOWN; + + if (iso->role == BT_HCI_ROLE_PERIPHERAL) { + central->max_sdu = central->max_pdu; + central->sdu_interval = BT_ISO_SDU_INTERVAL_UNKNOWN; + + peripheral->max_sdu = peripheral->max_pdu; + peripheral->sdu_interval = BT_ISO_SDU_INTERVAL_UNKNOWN; + } else { + central->max_sdu = tx == NULL ? 0 : tx->sdu; + central->sdu_interval = BT_ISO_SDU_INTERVAL_UNKNOWN; + + peripheral->max_sdu = rx == NULL ? 0 : rx->sdu; + peripheral->sdu_interval = BT_ISO_SDU_INTERVAL_UNKNOWN; + } +} + +/** Only store information that is not stored by store_cis_info + * Assumes that store_cis_info has been called first + */ +static void store_cis_info_v2(const struct bt_hci_evt_le_cis_established_v2 *evt, + struct bt_conn *iso) +{ + struct bt_conn_iso *iso_conn = &iso->iso; + struct bt_iso_info *info = &iso_conn->info; + struct bt_iso_unicast_info *unicast_info = &info->unicast; + struct bt_iso_unicast_tx_info *peripheral = &unicast_info->peripheral; + struct bt_iso_unicast_tx_info *central = &unicast_info->central; + const uint16_t c_max_sdu = sys_le16_to_cpu(evt->c_max_sdu); + const uint16_t p_max_sdu = sys_le16_to_cpu(evt->p_max_sdu); + struct bt_iso_chan_io_qos *tx; + struct bt_iso_chan_io_qos *rx; + struct bt_iso_chan *chan; + + /* The v1 version of the event is a subset of the v2 version - We can thus use the + * store_cis_info function for the majority of the info + */ + store_cis_info((const struct bt_hci_evt_le_cis_established *)evt, iso); + + chan = iso_conn->chan; + rx = chan->qos->rx; + tx = chan->qos->tx; + + if (iso->role == BT_HCI_ROLE_PERIPHERAL) { + /* Update the SDU sizes in the IO QoS fields stored by store_cis_info */ + if (rx != NULL) { + rx->sdu = c_max_sdu; + } + + if (tx != NULL) { + tx->sdu = p_max_sdu; + } + } else { + /* values are already set for central - Verify */ + if (tx != NULL && tx->sdu != c_max_sdu) { + LOG_WRN("Unexpected C to P max SDU: %u != %u", c_max_sdu, tx->sdu); + /* We assume that tx->sdu has become invalid, and will use the event from + * the controller as the truth + */ + tx->sdu = c_max_sdu; + } + + if (rx != NULL && rx->sdu != p_max_sdu) { + LOG_WRN("Unexpected P to C max SDU: %u != %u", p_max_sdu, rx->sdu); + /* We assume that rx->sdu has become invalid, and will use the event from + * the controller as the truth + */ + rx->sdu = p_max_sdu; + } + } + + unicast_info->subinterval = sys_get_le24(evt->sub_interval); + + central->max_sdu = sys_le16_to_cpu(evt->c_max_sdu); + central->sdu_interval = sys_get_le24(evt->c_sdu_interval); + + peripheral->max_sdu = sys_le16_to_cpu(evt->p_max_sdu); + peripheral->sdu_interval = sys_get_le24(evt->p_sdu_interval); } void hci_le_cis_established(struct net_buf *buf) @@ -1146,69 +1296,43 @@ void hci_le_cis_established(struct net_buf *buf) return; } - if (!evt->status) { - struct bt_iso_chan_io_qos *tx; - struct bt_iso_chan_io_qos *rx; - struct bt_conn_iso *iso_conn; - struct bt_iso_chan *chan; - - iso_conn = &iso->iso; - chan = iso_conn->chan; - - __ASSERT(chan != NULL && chan->qos != NULL, "Invalid ISO chan"); - - tx = chan->qos->tx; - rx = chan->qos->rx; - - LOG_DBG("iso_chan %p tx %p rx %p", chan, tx, rx); - - if (iso->role == BT_HCI_ROLE_PERIPHERAL) { - rx = chan->qos->rx; - tx = chan->qos->tx; - - /* As of BT Core 5.4, there is no way for the peripheral to get the actual - * SDU size or SDU interval without the use of higher layer profiles such as - * the Basic Audio Profile (BAP). The best we can do is use the PDU size - * until https://bluetooth.atlassian.net/browse/ES-18552 has been resolved - * and incorporated - */ - if (rx != NULL) { - rx->phy = bt_get_phy(evt->c_phy); - rx->sdu = sys_le16_to_cpu(evt->c_max_pdu); - } - - if (tx != NULL) { - tx->phy = bt_get_phy(evt->p_phy); - tx->sdu = sys_le16_to_cpu(evt->p_max_pdu); - } - - iso_conn->info.type = BT_ISO_CHAN_TYPE_CONNECTED; - } /* values are already set for central */ - - /* Verify if device can send */ - iso_conn->info.can_send = false; - if (tx != NULL) { - if (iso->role == BT_HCI_ROLE_PERIPHERAL && evt->p_bn > 0) { - iso_conn->info.can_send = true; - } else if (iso->role == BT_HCI_ROLE_CENTRAL && evt->c_bn > 0) { - iso_conn->info.can_send = true; - } - } - - /* Verify if device can recv */ - iso_conn->info.can_recv = false; - if (rx != NULL) { - if (iso->role == BT_HCI_ROLE_PERIPHERAL && evt->c_bn > 0) { - iso_conn->info.can_recv = true; - } else if (iso->role == BT_HCI_ROLE_CENTRAL && evt->p_bn > 0) { - iso_conn->info.can_recv = true; - } - } - - store_cis_info(evt, &iso_conn->info); + if (evt->status == BT_HCI_ERR_SUCCESS) { + store_cis_info(evt, iso); bt_conn_set_state(iso, BT_CONN_CONNECTED); - bt_conn_unref(iso); + } else if (iso->role == BT_HCI_ROLE_PERIPHERAL || + evt->status != BT_HCI_ERR_OP_CANCELLED_BY_HOST) { + iso->err = evt->status; + bt_iso_disconnected(iso); + } /* else we wait for disconnect event */ + + bt_conn_unref(iso); +} + +void hci_le_cis_established_v2(struct net_buf *buf) +{ + struct bt_hci_evt_le_cis_established_v2 *evt = (void *)buf->data; + uint16_t handle = sys_le16_to_cpu(evt->conn_handle); + struct bt_conn *iso; + + LOG_DBG("status 0x%02x %s handle %u", evt->status, bt_hci_err_to_str(evt->status), handle); + + /* ISO connection handles are already assigned at this point */ + iso = bt_conn_lookup_handle(handle, BT_CONN_TYPE_ISO); + if (!iso) { + /* If it is a local disconnect, then we may have received the disconnect complete + * event before this event, and in which case we do not expect to find the CIS + * object + */ + if (evt->status != BT_HCI_ERR_OP_CANCELLED_BY_HOST) { + LOG_ERR("No connection found for handle %u", handle); + } + return; + } + + if (evt->status == BT_HCI_ERR_SUCCESS) { + store_cis_info_v2(evt, iso); + bt_conn_set_state(iso, BT_CONN_CONNECTED); } else if (iso->role == BT_HCI_ROLE_PERIPHERAL || evt->status != BT_HCI_ERR_OP_CANCELLED_BY_HOST) { iso->err = evt->status; diff --git a/subsys/bluetooth/host/iso_internal.h b/subsys/bluetooth/host/iso_internal.h index c01881bc302..538435f5155 100644 --- a/subsys/bluetooth/host/iso_internal.h +++ b/subsys/bluetooth/host/iso_internal.h @@ -99,6 +99,7 @@ void bt_iso_buf_rx_freed_cb_set(bt_iso_buf_rx_freed_cb_t cb); /* Process CIS Established event */ void hci_le_cis_established(struct net_buf *buf); +void hci_le_cis_established_v2(struct net_buf *buf); /* Process CIS Request event */ void hci_le_cis_req(struct net_buf *buf);