diff --git a/include/bluetooth/iso.h b/include/bluetooth/iso.h index 7ee9973bf72..b2be17c9f83 100644 --- a/include/bluetooth/iso.h +++ b/include/bluetooth/iso.h @@ -4,6 +4,7 @@ /* * Copyright (c) 2020 Intel Corporation + * Copyright (c) 2021 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ @@ -70,10 +71,6 @@ struct bt_iso_chan { /** @brief ISO Channel IO QoS structure. */ struct bt_iso_chan_io_qos { - /** Channel interval in us. Value range 0x0000FF - 0x0FFFFFF. */ - uint32_t interval; - /** Channel Latency in ms. Value range 0x0005 - 0x0FA0. */ - uint16_t latency; /** Channel SDU. Value range 0x0000 - 0x0FFF. */ uint16_t sdu; /** Channel PHY - See BT_GAP_LE_PHY for values. @@ -92,16 +89,6 @@ struct bt_iso_chan_io_qos { /** @brief ISO Channel QoS structure. */ struct bt_iso_chan_qos { - /** @brief Channel peripherals sleep clock accuracy Only for CIS - * - * Shall be worst case sleep clock accuracy of all the peripherals. - * If unknown for the peripherals, this should be set to BT_GAP_SCA_UNKNOWN. - */ - uint8_t sca; - /** Channel packing mode. 0 for unpacked, 1 for packed. */ - uint8_t packing; - /** Channel framing mode. 0 for unframed, 1 for framed. */ - uint8_t framing; /** @brief Channel Receiving QoS. * * Setting NULL disables data path BT_HCI_DATAPATH_DIR_CTLR_TO_HOST. @@ -165,16 +152,75 @@ struct bt_iso_recv_info { uint8_t flags; }; + +/** Opaque type representing an Connected Isochronous Group (CIG). */ +struct bt_iso_cig; + +struct bt_iso_cig_create_param { + /** Array of pointers to CIS channels + * + * This array shall remain valid for the duration of the CIG. + */ + struct bt_iso_chan **cis_channels; + + /** Number channels in @p cis_channels */ + uint8_t num_cis; + + /** Channel interval in us. Value range 0x0000FF - 0xFFFFFF. */ + uint32_t interval; + + /** Channel Latency in ms. Value range 0x0005 - 0x0FA0. */ + uint16_t latency; + + /** @brief Channel peripherals sleep clock accuracy Only for CIS + * + * Shall be worst case sleep clock accuracy of all the peripherals. + * For possible values see the BT_GAP_SCA_* values. + * If unknown for the peripherals, this should be set to + * BT_GAP_SCA_UNKNOWN. + */ + uint8_t sca; + + /** Channel packing mode. 0 for unpacked, 1 for packed. */ + uint8_t packing; + + /** Channel framing mode. 0 for unframed, 1 for framed. */ + uint8_t framing; +}; + +struct bt_iso_connect_param { + /* The ISO channel to connect */ + struct bt_iso_chan *iso; + + /* The ACL connection */ + struct bt_conn *conn; +}; + /** Opaque type representing an Broadcast Isochronous Group (BIG). */ struct bt_iso_big; struct bt_iso_big_create_param { - /** Array of pointers to BIS channels */ + /** Array of pointers to BIS channels + * + * This array shall remain valid for the duration of the BIG. + */ struct bt_iso_chan **bis_channels; /** Number channels in @p bis_channels */ uint8_t num_bis; + /** Channel interval in us. Value range 0x0000FF - 0xFFFFFF. */ + uint32_t interval; + + /** Channel Latency in ms. Value range 0x0005 - 0x0FA0. */ + uint16_t latency; + + /** Channel packing mode. 0 for unpacked, 1 for packed. */ + uint8_t packing; + + /** Channel framing mode. 0 for unframed, 1 for framed. */ + uint8_t framing; + /** Whether or not to encrypt the streams. */ bool encryption; @@ -337,49 +383,51 @@ struct bt_iso_server { */ int bt_iso_server_register(struct bt_iso_server *server); -/** @brief Bind ISO channels +/** @brief Creates a CIG as a central * - * Bind ISO channels with existing ACL connections, Channel objects passed - * (over an address of it) shouldn't be instantiated in application as - * standalone. + * This can called at any time, even before connecting to a remote device. + * This must be called before any connected isochronous stream (CIS) channel + * can be connected. * - * @param conns Array of ACL connection objects - * @param num_conns Number of connection objects - * @param chans Array of ISO Channel objects to be created + * Once a CIG is created, the channels supplied in the @p param can be + * connected using bt_iso_chan_connect. + * + * @param[in] param The parameters used to create and enable the CIG. + * @param[out] out_cig Connected Isochronous Group object on success. * * @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); +int bt_iso_cig_create(const struct bt_iso_cig_create_param *param, + struct bt_iso_cig **out_cig); -/** @brief Unbind ISO channel +/** @brief Terminates a CIG as a central * - * Unbind ISO channel from ACL connection, channel must be in BT_ISO_BOUND - * state. + * All the CIS in the CIG shall be disconnected first. * - * Note: Channels which the ACL connection has been disconnected are unbind - * automatically. - * - * @param chan Channel object. + * @param cig Pointer to the CIG structure. * * @return 0 in case of success or negative value in case of error. */ -int bt_iso_chan_unbind(struct bt_iso_chan *chan); +int bt_iso_cig_terminate(struct bt_iso_cig *cig); -/** @brief Connect ISO channels +/** @brief Connect ISO channels on ACL connections * - * 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. + * Connect ISO channels. The ISO channels must have been initialized in a CIG + * first by calling bt_iso_cig_create. * - * @param chans Array of ISO channel objects - * @param num_chans Number of channel objects + * Once the connection is completed the channels' connected() callback will be + * called. If the connection is rejected disconnected() callback is called + * instead. + * + * This function will also setup the ISO data path based on the @p path + * parameter of the bt_iso_chan_io_qos for each channel. + * + * @param param Pointer to a connect parameter array with the ISO and ACL pointers. + * @param count Number of connect parameters. * * @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); +int bt_iso_chan_connect(const struct bt_iso_connect_param *param, size_t count); /** @brief Disconnect ISO channel * diff --git a/subsys/bluetooth/Kconfig b/subsys/bluetooth/Kconfig index 3b228e6f173..57bbd37b92f 100644 --- a/subsys/bluetooth/Kconfig +++ b/subsys/bluetooth/Kconfig @@ -1,6 +1,7 @@ # Bluetooth configuration options # Copyright (c) 2016 Intel Corporation +# Copyright (c) 2021 Nordic Semiconductor ASA # SPDX-License-Identifier: Apache-2.0 menuconfig BT @@ -307,6 +308,17 @@ config BT_ISO_RX_MTU help Maximum MTU for Isochronous channels RX buffers. +if BT_ISO_UNICAST + +config BT_ISO_MAX_CIG + int "Maximum number of Connected Isochronous Groups (CIGs) to support" + default 1 + help + Maximum number of CIGs that are supported by the host. A CIG can be + used for either transmitting or receiving. + +endif # BT_ISO_UNICAST + if BT_ISO_BROADCAST config BT_ISO_MAX_BIG diff --git a/subsys/bluetooth/host/conn_internal.h b/subsys/bluetooth/host/conn_internal.h index e12248bcc52..e994d7ba4e3 100644 --- a/subsys/bluetooth/host/conn_internal.h +++ b/subsys/bluetooth/host/conn_internal.h @@ -107,10 +107,10 @@ struct bt_conn_iso { }; union { - /* CIS ID */ + /* CIS ID within the CIG */ uint8_t cis_id; - /* BIS ID */ + /* BIS ID within the BIG*/ uint8_t bis_id; }; @@ -247,12 +247,6 @@ struct bt_iso_create_param { 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); diff --git a/subsys/bluetooth/host/iso.c b/subsys/bluetooth/host/iso.c index a2315e12ed8..d429ec63320 100644 --- a/subsys/bluetooth/host/iso.c +++ b/subsys/bluetooth/host/iso.c @@ -2,6 +2,7 @@ /* * Copyright (c) 2020 Intel Corporation + * Copyright (c) 2021 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ @@ -40,6 +41,7 @@ struct bt_conn iso_conns[CONFIG_BT_ISO_MAX_CHAN]; /* TODO: Allow more than one server? */ #if defined(CONFIG_BT_ISO_UNICAST) +struct bt_iso_cig cigs[CONFIG_BT_ISO_MAX_CIG]; static struct bt_iso_server *iso_server; #endif /* CONFIG_BT_ISO_UNICAST */ #if defined(CONFIG_BT_ISO_BROADCAST) @@ -48,7 +50,6 @@ struct bt_iso_big bigs[CONFIG_BT_ISO_MAX_BIG]; /* Prototype */ int hci_le_remove_cig(uint8_t cig_id); -int bt_iso_chan_unbind(struct bt_iso_chan *chan); struct bt_iso_data_path { /* Data Path direction */ @@ -373,28 +374,29 @@ void bt_iso_remove_data_path(struct bt_conn *conn) } } +bool bt_iso_chan_remove(struct bt_conn *conn, struct bt_iso_chan *chan) +{ + return sys_slist_find_and_remove(&conn->channels, &chan->node); +} + static void bt_iso_chan_disconnected(struct bt_iso_chan *chan, uint8_t reason) { BT_DBG("%p, reason 0x%02x", chan, reason); - if (!chan->conn) { - bt_iso_chan_set_state(chan, BT_ISO_DISCONNECTED); - return; - } + __ASSERT(chan->conn != NULL, "NULL conn for iso chan %p", chan); - if (chan->conn->iso.is_bis) { - bt_iso_chan_set_state(chan, BT_ISO_DISCONNECTED); - } else if (IS_ENABLED(CONFIG_BT_ISO_UNICAST)) { - bt_iso_chan_set_state(chan, BT_ISO_BOUND); + bt_iso_chan_set_state(chan, BT_ISO_DISCONNECTED); - /* Unbind if acting as slave or ACL has been disconnected */ - if (chan->conn->role == BT_HCI_ROLE_SLAVE || - chan->conn->iso.acl->state == BT_CONN_DISCONNECTED) { - bt_iso_chan_unbind(chan); + /* The peripheral does not have the concept of a CIG, so once a CIS + * disconnects it is completely freed by unref'ing it + */ + if (IS_ENABLED(CONFIG_BT_ISO_UNICAST) && + chan->conn->role == BT_HCI_ROLE_SLAVE) { + if (!bt_iso_chan_remove(chan->conn, chan)) { + BT_ERR("Could not remove chan from conn channels"); } - } else { - BT_ERR("Invalid ISO channel"); - return; + bt_conn_unref(chan->conn); + chan->conn = NULL; } if (chan->ops->disconnected) { @@ -485,11 +487,6 @@ void bt_iso_chan_set_state(struct bt_iso_chan *chan, uint8_t state) } #endif /* CONFIG_BT_DEBUG_ISO */ -bool bt_iso_chan_remove(struct bt_conn *conn, struct bt_iso_chan *chan) -{ - return sys_slist_find_and_remove(&conn->channels, &chan->node); -} - void bt_iso_recv(struct bt_conn *conn, struct net_buf *buf, uint8_t flags) { struct bt_hci_iso_data_hdr *hdr; @@ -672,37 +669,12 @@ struct bt_conn_iso *bt_conn_iso(struct bt_conn *conn) void bt_iso_cleanup(struct bt_conn *conn) { struct bt_conn_iso *iso = bt_conn_iso(conn); - int i; BT_DBG("%p", conn); if (iso->acl) { bt_conn_unref(iso->acl); iso->acl = NULL; - - if (conn->role == BT_CONN_ROLE_SLAVE) { - return; - } - - /* Check if conn is last of CIG */ - for (i = 0; i < CONFIG_BT_ISO_MAX_CHAN; i++) { - if (conn == &iso_conns[i]) { - continue; - } - - if (atomic_get(&iso_conns[i].ref) && - iso_conns[i].iso.cig_id == conn->iso.cig_id) { - break; - } - } - - if (i == CONFIG_BT_ISO_MAX_CHAN) { - int err = hci_le_remove_cig(conn->iso.cig_id); - - if (err != 0) { - BT_WARN("Failed to remove CIG: %d", err); - } - } } } @@ -882,21 +854,17 @@ struct bt_conn *bt_conn_add_iso(struct bt_conn *acl) return conn; } -static struct net_buf *hci_le_set_cig_params(struct bt_iso_create_param *param) +static struct net_buf *hci_le_set_cig_params(const struct bt_iso_cig *cig, + const struct bt_iso_cig_create_param *param) { - struct bt_hci_cis_params *cis; struct bt_hci_cp_le_set_cig_params *req; + struct bt_hci_cis_params *cis_param; struct net_buf *buf; struct net_buf *rsp; int i, err; - if (!param->chans[0]->qos->tx && !param->chans[0]->qos->rx) { - BT_ERR("Both TX and RX QoS are disabled"); - return NULL; - } - buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_CIG_PARAMS, - sizeof(*req) + sizeof(*cis) * param->num_conns); + sizeof(*req) + sizeof(*cis_param) * param->num_cis); if (!buf) { return NULL; } @@ -905,49 +873,27 @@ static struct net_buf *hci_le_set_cig_params(struct bt_iso_create_param *param) memset(req, 0, sizeof(*req)); - req->cig_id = param->conns[0]->iso.cig_id; - if (param->chans[0]->qos->tx) { - sys_put_le24(param->chans[0]->qos->tx->interval, req->m_interval); - req->m_latency = sys_cpu_to_le16(param->chans[0]->qos->tx->latency); - } else { - /* Use RX values if TX is disabled. - * If TX is disabled then the values don't have any meaning, as - * we will create a new CIG in case we want to change the - * directions, but will need to be set to the fields to some - * valid (nonzero) values for the controller to accept them. - */ - sys_put_le24(param->chans[0]->qos->rx->interval, req->m_interval); - req->m_latency = sys_cpu_to_le16(param->chans[0]->qos->rx->latency); - } + req->cig_id = cig->id; + req->m_latency = sys_cpu_to_le16(param->latency); + req->s_latency = sys_cpu_to_le16(param->latency); + sys_put_le24(param->interval, req->m_interval); + sys_put_le24(param->interval, req->s_interval); - if (param->chans[0]->qos->rx) { - sys_put_le24(param->chans[0]->qos->rx->interval, req->s_interval); - req->s_latency = sys_cpu_to_le16(param->chans[0]->qos->rx->latency); - } else { - /* Use TX values if RX is disabled. - * If RX is disabled then the values don't have any meaning, as - * we will create a new CIG in case we want to change the - * directions, but will need to be set to the fields to some - * valid (nonzero) values for the controller to accept them. - */ - sys_put_le24(param->chans[0]->qos->tx->interval, req->s_interval); - req->s_latency = sys_cpu_to_le16(param->chans[0]->qos->tx->latency); - } - - req->sca = param->chans[0]->qos->sca; - req->packing = param->chans[0]->qos->packing; - req->framing = param->chans[0]->qos->framing; - req->num_cis = param->num_conns; + req->sca = param->sca; + req->packing = param->packing; + req->framing = param->framing; + req->num_cis = param->num_cis; /* Program the cis parameters */ - for (i = 0; i < param->num_conns; i++) { - struct bt_iso_chan_qos *qos = param->chans[i]->qos; + for (i = 0; i < param->num_cis; i++) { + struct bt_iso_chan *cis = param->cis_channels[i]; + struct bt_iso_chan_qos *qos = cis->qos; - cis = net_buf_add(buf, sizeof(*cis)); + cis_param = net_buf_add(buf, sizeof(*cis_param)); - memset(cis, 0, sizeof(*cis)); + memset(cis_param, 0, sizeof(*cis_param)); - cis->cis_id = param->conns[i]->iso.cis_id; + cis_param->cis_id = cis->conn->iso.cis_id; if (!qos->tx && !qos->rx) { BT_ERR("Both TX and RX QoS are disabled"); @@ -956,21 +902,25 @@ static struct net_buf *hci_le_set_cig_params(struct bt_iso_create_param *param) } if (!qos->tx) { - /* Use RX PHY if TX is not set (disabled) */ - cis->m_phy = qos->rx->phy; + /* Use RX PHY if TX is not set (disabled) + * to avoid setting invalid values + */ + cis_param->m_phy = qos->rx->phy; } else { - cis->m_sdu = sys_cpu_to_le16(qos->tx->sdu); - cis->m_phy = qos->tx->phy; - cis->m_rtn = qos->tx->rtn; + cis_param->m_sdu = sys_cpu_to_le16(qos->tx->sdu); + cis_param->m_phy = qos->tx->phy; + cis_param->m_rtn = qos->tx->rtn; } if (!qos->rx) { - /* Use TX PHY if RX is not set (disabled) */ - cis->s_phy = qos->tx->phy; + /* Use TX PHY if RX is not set (disabled) + * to avoid setting invalid values + */ + cis_param->s_phy = qos->tx->phy; } else { - cis->s_sdu = sys_cpu_to_le16(qos->rx->sdu); - cis->s_phy = qos->rx->phy; - cis->s_rtn = qos->rx->rtn; + cis_param->s_sdu = sys_cpu_to_le16(qos->rx->sdu); + cis_param->s_phy = qos->rx->phy; + cis_param->s_rtn = qos->rx->rtn; } } @@ -982,89 +932,184 @@ static struct net_buf *hci_le_set_cig_params(struct bt_iso_create_param *param) return rsp; } -int bt_conn_bind_iso(struct bt_iso_create_param *param) +static struct bt_iso_cig *get_free_cig(void) { - struct bt_conn *conn; - struct net_buf *rsp; - struct bt_hci_rp_le_set_cig_params *cig_rsp; - int i, err; + /* We can use the index in the `cigs` array as CIG ID */ - /* Check if controller is ISO capable */ + for (int i = 0; i < ARRAY_SIZE(cigs); i++) { + if (!cigs[i].initialized) { + cigs[i].initialized = true; + cigs[i].id = i; + return &cigs[i]; + } + } + + BT_DBG("Could not allocate any more CIGs"); + + return NULL; +} + +static int cig_init_cis(struct bt_iso_cig *cig) +{ + for (int i = 0; i < cig->num_cis; i++) { + struct bt_iso_chan *cis = cig->cis[i]; + + if (cis == NULL) { + BT_DBG("CIS was NULL"); + return -EINVAL; + } + + if (cis->conn) { + BT_DBG("CIS conn was already allocated"); + return -EALREADY; + } + + cis->conn = iso_new(); + if (cis->conn == NULL) { + BT_ERR("Unable to allocate CIS connection"); + return -ENOMEM; + } + + cis->conn->iso.cig_id = cig->id; + cis->conn->iso.is_bis = false; + cis->conn->iso.cis_id = i; + + bt_iso_chan_add(cis->conn, cis); + } + + return 0; +} + +static void cleanup_cig(struct bt_iso_cig *cig) +{ + for (int i = 0; i < cig->num_cis; i++) { + struct bt_iso_chan *cis = cig->cis[i]; + + if (cis != NULL && cis->conn != NULL) { + bt_conn_unref(cis->conn); + cis->conn = NULL; + } + } + + memset(cig, 0, sizeof(*cig)); +} + +int bt_iso_cig_create(const struct bt_iso_cig_create_param *param, + struct bt_iso_cig **out_cig) +{ + int err; + struct net_buf *rsp; + struct bt_iso_cig *cig; + struct bt_hci_rp_le_set_cig_params *cig_rsp; + + CHECKIF(out_cig == NULL) { + BT_DBG("out_cig is NULL"); + return -EINVAL; + } + + *out_cig = NULL; + + /* Check if controller is ISO capable as a central */ if (!BT_FEAT_LE_CIS_MASTER(bt_dev.le.features)) { return -ENOTSUP; } - if (!param->num_conns || param->num_conns > CONFIG_BT_ISO_MAX_CHAN) { + CHECKIF(param->cis_channels == NULL) { + BT_DBG("NULL CIS channels"); return -EINVAL; } - /* Assign ISO connections to each LE connection */ - for (i = 0; i < param->num_conns; i++) { - conn = param->conns[i]; - - if (conn->type != BT_CONN_TYPE_LE) { - err = -EINVAL; - goto failed; - } - - conn = bt_conn_add_iso(conn); - if (!conn) { - err = -ENOMEM; - goto failed; - } - - conn->iso.cig_id = param->id; - conn->iso.cis_id = bt_conn_index(conn); - - param->conns[i] = conn; + CHECKIF(param->num_cis == 0) { + BT_DBG("Invalid number of CIS %u", param->num_cis); + return -EINVAL; } - rsp = hci_le_set_cig_params(param); - if (!rsp) { + cig = get_free_cig(); + + if (!cig) { + return -ENOMEM; + } + + cig->cis = param->cis_channels; + cig->num_cis = param->num_cis; + + err = cig_init_cis(cig); + if (err) { + BT_DBG("Could not init CIS %d", err); + cleanup_cig(cig); + return err; + } + + rsp = hci_le_set_cig_params(cig, param); + if (rsp == NULL) { + BT_WARN("Unexpected response to hci_le_set_cig_params"); err = -EIO; - goto failed; + cleanup_cig(cig); + return err; } cig_rsp = (void *)rsp->data; if (rsp->len < sizeof(cig_rsp) || - cig_rsp->num_handles != param->num_conns) { + cig_rsp->num_handles != param->num_cis) { BT_WARN("Unexpected response to hci_le_set_cig_params"); err = -EIO; net_buf_unref(rsp); - goto failed; + cleanup_cig(cig); + return err; } - for (i = 0; i < cig_rsp->num_handles; i++) { + for (int i = 0; i < cig_rsp->num_handles; i++) { + struct bt_iso_chan *chan; + + chan = param->cis_channels[i]; + /* Assign the connection handle */ - param->conns[i]->handle = cig_rsp->handle[i]; + chan->conn->handle = sys_le16_to_cpu(cig_rsp->handle[i]); + bt_iso_chan_set_state(chan, BT_ISO_BOUND); } net_buf_unref(rsp); - return 0; - -failed: - for (i = 0; i < param->num_conns; i++) { - conn = param->conns[i]; - - if (conn->type == BT_CONN_TYPE_ISO) { - bt_conn_unref(conn); - } - } + *out_cig = cig; return err; } -static int hci_le_create_cis(struct bt_conn **conn, uint8_t num_conns) +int bt_iso_cig_terminate(struct bt_iso_cig *cig) +{ + int err; + + CHECKIF(cig == NULL) { + BT_DBG("cig is NULL"); + return -EINVAL; + } + + for (int i = 0; i < cig->num_cis; i++) { + if (cig->cis[i]->state != BT_ISO_DISCONNECTED) { + BT_DBG("[%d]: Channel is not disconnected", i); + } + } + + err = hci_le_remove_cig(cig->id); + if (err != 0) { + BT_DBG("Failed to terminate CIG: %d", err); + } + + cleanup_cig(cig); + + return 0; +} + +static int hci_le_create_cis(const struct bt_iso_connect_param *param, + size_t count) { struct bt_hci_cis *cis; struct bt_hci_cp_le_create_cis *req; struct net_buf *buf; - int i; buf = bt_hci_cmd_create(BT_HCI_OP_LE_CREATE_CIS, - sizeof(*req) + sizeof(*cis) * num_conns); + sizeof(*req) + sizeof(*cis) * count); if (!buf) { return -ENOBUFS; } @@ -1073,53 +1118,21 @@ static int hci_le_create_cis(struct bt_conn **conn, uint8_t num_conns) memset(req, 0, sizeof(*req)); - req->num_cis = num_conns; + req->num_cis = count; /* Program the cis parameters */ - for (i = 0; i < num_conns; i++) { + for (int i = 0; i < count; i++) { cis = net_buf_add(buf, sizeof(*cis)); memset(cis, 0, sizeof(*cis)); - cis->cis_handle = sys_cpu_to_le16(conn[i]->handle); - cis->acl_handle = sys_cpu_to_le16(conn[i]->iso.acl->handle); + cis->cis_handle = sys_cpu_to_le16(param[i].iso->conn->handle); + cis->acl_handle = sys_cpu_to_le16(param[i].conn->handle); } return bt_hci_cmd_send_sync(BT_HCI_OP_LE_CREATE_CIS, buf, NULL); } -int bt_conn_connect_iso(struct bt_conn **conns, uint8_t num_conns) -{ - int i, err; - - /* Check if controller is ISO capable */ - if (!BT_FEAT_LE_CIS_MASTER(bt_dev.le.features)) { - return -ENOTSUP; - } - - if (num_conns > CONFIG_BT_ISO_MAX_CHAN) { - return -EINVAL; - } - - for (i = 0; i < num_conns; i++) { - if (conns[i]->type != BT_CONN_TYPE_ISO) { - return -EINVAL; - } - } - - err = hci_le_create_cis(conns, num_conns); - if (err) { - return err; - } - - /* Set connection state */ - for (i = 0; i < num_conns; i++) { - bt_conn_set_state(conns[i], BT_CONN_CONNECT); - } - - return 0; -} - int bt_iso_accept(struct bt_conn *conn) { struct bt_iso_chan *chan; @@ -1149,32 +1162,66 @@ int bt_iso_accept(struct bt_conn *conn) return 0; } -int bt_iso_chan_connect(struct bt_iso_chan **chans, uint8_t num_chans) +int bt_iso_chan_connect(const struct bt_iso_connect_param *param, size_t count) { - struct bt_conn *conns[CONFIG_BT_ISO_MAX_CHAN]; - int i, err; + int err; - CHECKIF(!chans || !num_chans) { - BT_DBG("Invalid parameters: chans %p num_chans %u", chans, - num_chans); + CHECKIF(param == NULL || count == 0) { + BT_DBG("param is NULL"); return -EINVAL; } - for (i = 0; i < num_chans; i++) { - if (!chans[i]->conn) { - return -ENOTCONN; - } - - conns[i] = chans[i]->conn; + CHECKIF(count == 0) { + BT_DBG("Invalid count %zu", count); + return -EINVAL; } - err = bt_conn_connect_iso(conns, num_chans); + CHECKIF(count > CONFIG_BT_ISO_MAX_CHAN) { + return -EINVAL; + } + + /* Validate input */ + for (int i = 0; i < count; i++) { + CHECKIF(param[i].iso == NULL) { + BT_DBG("[%d]: Invalid iso (%p)", i, param[i].iso); + return -EINVAL; + } + + CHECKIF(param[i].conn == NULL) { + BT_DBG("[%d]: Invalid conn (%p)", i, param[i].iso); + return -EINVAL; + } + + CHECKIF((param[i].conn->type & BT_CONN_TYPE_LE) == 0) { + BT_DBG("[%d]: Conn type (%u) shall be an LE connection", + i, param[i].conn->type); + return -EINVAL; + } + + if (param[i].iso->conn == NULL) { + BT_DBG("[%d]: ISO has not been initialized in a CIG", + i); + return -EINVAL; + } + + if (param[i].iso->state != BT_ISO_BOUND) { + BT_DBG("[%d]: ISO is not in the BT_ISO_BOUND state: %u", + i, param[i].iso->state); + return -EINVAL; + } + } + + err = hci_le_create_cis(param, count); if (err) { + BT_DBG("Failed to connect CISes: %d", err); return err; } - for (i = 0; i < num_chans; i++) { - bt_iso_chan_set_state(chans[i], BT_ISO_CONNECT); + /* Set connection states */ + for (int i = 0; i < count; i++) { + param[i].iso->conn->iso.acl = bt_conn_ref(param[i].conn); + bt_conn_set_state(param[i].iso->conn, BT_CONN_CONNECT); + bt_iso_chan_set_state(param[i].iso, BT_ISO_CONNECT); } return 0; @@ -1187,13 +1234,14 @@ int bt_iso_chan_disconnect(struct bt_iso_chan *chan) return -EINVAL; } - if (!chan->conn) { - return -ENOTCONN; + CHECKIF(chan->conn == NULL) { + BT_DBG("Channel has not been initialized in a CIG"); + return -EINVAL; } - if (chan->state == BT_ISO_BOUND) { - bt_iso_chan_disconnected(chan, BT_HCI_ERR_LOCALHOST_TERM_CONN); - return 0; + if (chan->conn->iso.acl == NULL) { + BT_DBG("Channel is not connected"); + return -ENOTCONN; } return bt_conn_disconnect(chan->conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); @@ -1232,68 +1280,6 @@ int bt_iso_server_register(struct bt_iso_server *server) return 0; } - -int bt_iso_chan_bind(struct bt_conn **conns, uint8_t num_conns, - struct bt_iso_chan **chans) -{ - struct bt_iso_create_param param; - int i, err; - static uint8_t id; - - CHECKIF(!conns || !num_conns || !chans) { - BT_DBG("Invalid parameters: conns %p num_conns %u chans %p", - conns, num_conns, chans); - return -EINVAL; - } - - memset(¶m, 0, sizeof(param)); - - param.id = id++; - param.num_conns = num_conns; - param.conns = conns; - param.chans = chans; - - err = bt_conn_bind_iso(¶m); - if (err) { - return err; - } - - /* Bind respective connection to channel */ - for (i = 0; i < num_conns; i++) { - bt_iso_chan_add(conns[i], chans[i]); - bt_iso_chan_set_state(chans[i], BT_ISO_BOUND); - } - - return 0; -} - -int bt_iso_chan_unbind(struct bt_iso_chan *chan) -{ - CHECKIF(!chan) { - BT_DBG("Invalid parameter: chan %p", chan); - return -EINVAL; - } - - if (chan->state != BT_ISO_BOUND) { - BT_DBG("Channel is not in BT_ISO_BOUND state"); - return -EINVAL; - } - - if (!chan->conn) { - return -EINVAL; - } - - if (!bt_iso_chan_remove(chan->conn, chan)) { - return -ENOENT; - } - - bt_conn_unref(chan->conn); - chan->conn = NULL; - - bt_iso_chan_set_state(chan, BT_ISO_DISCONNECTED); - - return 0; -} #endif /* CONFIG_BT_ISO_UNICAST */ #if defined(CONFIG_BT_ISO_BROADCAST) @@ -1412,13 +1398,13 @@ static int hci_le_create_big(struct bt_le_ext_adv *padv, struct bt_iso_big *big, req->big_handle = big->handle; req->adv_handle = padv->handle; req->num_bis = big->num_bis; - sys_put_le24(qos->tx->interval, req->sdu_interval); + sys_put_le24(param->interval, req->sdu_interval); req->max_sdu = sys_cpu_to_le16(qos->tx->sdu); - req->max_latency = sys_cpu_to_le16(qos->tx->latency); + req->max_latency = sys_cpu_to_le16(param->latency); req->rtn = qos->tx->rtn; req->phy = qos->tx->phy; - req->packing = qos->packing; - req->framing = qos->framing; + req->packing = param->packing; + req->framing = param->framing; req->encryption = param->encryption; if (req->encryption) { memcpy(req->bcode, param->bcode, sizeof(req->bcode)); diff --git a/subsys/bluetooth/host/iso_internal.h b/subsys/bluetooth/host/iso_internal.h index 685c51ea268..95e4c9e3408 100644 --- a/subsys/bluetooth/host/iso_internal.h +++ b/subsys/bluetooth/host/iso_internal.h @@ -4,6 +4,7 @@ /* * Copyright (c) 2020 Intel Corporation + * Copyright (c) 2021 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ @@ -23,6 +24,19 @@ struct iso_data { uint16_t handle; }; +struct bt_iso_cig { + /** Array of ISO channels to setup as CIS (the CIG). */ + struct bt_iso_chan **cis; + + /** Total number of CISes in the CIG. */ + uint8_t num_cis; + + /** The CIG ID */ + uint8_t id; + + bool initialized; +}; + enum { BT_BIG_INITIALIZED, diff --git a/tests/bluetooth/shell/prj.conf b/tests/bluetooth/shell/prj.conf index bead4b688d2..59f746217c3 100644 --- a/tests/bluetooth/shell/prj.conf +++ b/tests/bluetooth/shell/prj.conf @@ -68,3 +68,6 @@ CONFIG_BT_MICS=y CONFIG_BT_MICS_AICS_INSTANCE_COUNT=1 CONFIG_BT_MICS_CLIENT=y CONFIG_BT_MICS_CLIENT_MAX_AICS_INST=1 + +CONFIG_BT_DEBUG_ISO=y +CONFIG_BT_DEBUG_CONN=y