@@ -976,6 +976,7 @@ enum {
HCI_CONN_AUTH_FAILURE,
HCI_CONN_PER_ADV,
HCI_CONN_BIG_CREATED,
+ HCI_CONN_CREATE_CIS,
};
static inline bool hci_conn_ssp_enabled(struct hci_conn *conn)
@@ -1351,7 +1352,8 @@ int hci_disconnect(struct hci_conn *conn, __u8 reason);
bool hci_setup_sync(struct hci_conn *conn, __u16 handle);
void hci_sco_setup(struct hci_conn *conn, __u8 status);
bool hci_iso_setup_path(struct hci_conn *conn);
-int hci_le_create_cis(struct hci_conn *conn);
+int hci_le_create_cis_pending(struct hci_dev *hdev);
+int hci_conn_check_create_cis(struct hci_conn *conn);
struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
u8 role);
@@ -124,7 +124,7 @@ int hci_abort_conn_sync(struct hci_dev *hdev, struct hci_conn *conn, u8 reason);
int hci_le_create_conn_sync(struct hci_dev *hdev, struct hci_conn *conn);
-int hci_le_create_cis_sync(struct hci_dev *hdev, struct hci_conn *conn);
+int hci_le_create_cis_sync(struct hci_dev *hdev);
int hci_le_remove_cig_sync(struct hci_dev *hdev, u8 handle);
@@ -1992,59 +1992,47 @@ bool hci_iso_setup_path(struct hci_conn *conn)
return true;
}
+int hci_conn_check_create_cis(struct hci_conn *conn)
+{
+ if (conn->type != ISO_LINK || !bacmp(&conn->dst, BDADDR_ANY))
+ return -EINVAL;
+
+ if (!conn->parent || conn->parent->state != BT_CONNECTED ||
+ conn->state != BT_CONNECT || conn->handle == HCI_CONN_HANDLE_UNSET)
+ return 1;
+
+ return 0;
+}
+
static int hci_create_cis_sync(struct hci_dev *hdev, void *data)
{
- return hci_le_create_cis_sync(hdev, data);
+ return hci_le_create_cis_sync(hdev);
}
-int hci_le_create_cis(struct hci_conn *conn)
+int hci_le_create_cis_pending(struct hci_dev *hdev)
{
- struct hci_conn *cis;
- struct hci_link *link, *t;
- struct hci_dev *hdev = conn->hdev;
- int err;
+ struct hci_conn *conn;
+ bool pending = false;
- bt_dev_dbg(hdev, "hcon %p", conn);
+ rcu_read_lock();
- switch (conn->type) {
- case LE_LINK:
- if (conn->state != BT_CONNECTED || list_empty(&conn->link_list))
- return -EINVAL;
-
- cis = NULL;
-
- /* hci_conn_link uses list_add_tail_rcu so the list is in
- * the same order as the connections are requested.
- */
- list_for_each_entry_safe(link, t, &conn->link_list, list) {
- if (link->conn->state == BT_BOUND) {
- err = hci_le_create_cis(link->conn);
- if (err)
- return err;
-
- cis = link->conn;
- }
+ list_for_each_entry_rcu(conn, &hdev->conn_hash.list, list) {
+ if (test_bit(HCI_CONN_CREATE_CIS, &conn->flags)) {
+ rcu_read_unlock();
+ return -EBUSY;
}
- return cis ? 0 : -EINVAL;
- case ISO_LINK:
- cis = conn;
- break;
- default:
- return -EINVAL;
+ if (!hci_conn_check_create_cis(conn))
+ pending = true;
}
- if (cis->state == BT_CONNECT)
+ rcu_read_unlock();
+
+ if (!pending)
return 0;
/* Queue Create CIS */
- err = hci_cmd_sync_queue(hdev, hci_create_cis_sync, cis, NULL);
- if (err)
- return err;
-
- cis->state = BT_CONNECT;
-
- return 0;
+ return hci_cmd_sync_queue(hdev, hci_create_cis_sync, NULL, NULL);
}
static void hci_iso_qos_setup(struct hci_dev *hdev, struct hci_conn *conn,
@@ -2319,11 +2307,9 @@ struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst,
return NULL;
}
- /* If LE is already connected and CIS handle is already set proceed to
- * Create CIS immediately.
- */
- if (le->state == BT_CONNECTED && cis->handle != HCI_CONN_HANDLE_UNSET)
- hci_le_create_cis(cis);
+ cis->state = BT_CONNECT;
+
+ hci_le_create_cis_pending(hdev);
return cis;
}
@@ -3807,6 +3807,7 @@ static u8 hci_cc_le_set_cig_params(struct hci_dev *hdev, void *data,
struct hci_cp_le_set_cig_params *cp;
struct hci_conn *conn;
u8 status = rp->status;
+ bool pending = false;
int i;
bt_dev_dbg(hdev, "status 0x%2.2x", rp->status);
@@ -3855,13 +3856,8 @@ static u8 hci_cc_le_set_cig_params(struct hci_dev *hdev, void *data,
bt_dev_dbg(hdev, "%p handle 0x%4.4x parent %p",
conn, conn->handle, conn->parent);
- /* Create CIS if LE is already connected */
- if (conn->parent &&
- conn->parent->state == BT_CONNECTED) {
- rcu_read_unlock();
- hci_le_create_cis(conn);
- rcu_read_lock();
- }
+ if (conn->state == BT_CONNECT)
+ pending = true;
break;
}
@@ -3870,6 +3866,9 @@ static u8 hci_cc_le_set_cig_params(struct hci_dev *hdev, void *data,
rcu_read_unlock();
unlock:
+ if (pending)
+ hci_le_create_cis_pending(hdev);
+
hci_dev_unlock(hdev);
return rp->status;
@@ -4235,6 +4234,7 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, void *data,
static void hci_cs_le_create_cis(struct hci_dev *hdev, u8 status)
{
struct hci_cp_le_create_cis *cp;
+ bool pending = false;
int i;
bt_dev_dbg(hdev, "status 0x%2.2x", status);
@@ -4257,12 +4257,18 @@ static void hci_cs_le_create_cis(struct hci_dev *hdev, u8 status)
conn = hci_conn_hash_lookup_handle(hdev, handle);
if (conn) {
+ if (test_and_clear_bit(HCI_CONN_CREATE_CIS,
+ &conn->flags))
+ pending = true;
conn->state = BT_CLOSED;
hci_connect_cfm(conn, status);
hci_conn_del(conn);
}
}
+ if (pending)
+ hci_le_create_cis_pending(hdev);
+
hci_dev_unlock(hdev);
}
@@ -6805,6 +6811,7 @@ static void hci_le_cis_estabilished_evt(struct hci_dev *hdev, void *data,
{
struct hci_evt_le_cis_established *ev = data;
struct hci_conn *conn;
+ bool pending = false;
u16 handle = __le16_to_cpu(ev->handle);
bt_dev_dbg(hdev, "status 0x%2.2x", ev->status);
@@ -6826,6 +6833,8 @@ static void hci_le_cis_estabilished_evt(struct hci_dev *hdev, void *data,
goto unlock;
}
+ pending = test_and_clear_bit(HCI_CONN_CREATE_CIS, &conn->flags);
+
if (conn->role == HCI_ROLE_SLAVE) {
__le32 interval;
@@ -6851,10 +6860,14 @@ static void hci_le_cis_estabilished_evt(struct hci_dev *hdev, void *data,
goto unlock;
}
+ conn->state = BT_CLOSED;
hci_connect_cfm(conn, ev->status);
hci_conn_del(conn);
unlock:
+ if (pending)
+ hci_le_create_cis_pending(hdev);
+
hci_dev_unlock(hdev);
}
@@ -6170,56 +6170,92 @@ int hci_le_create_conn_sync(struct hci_dev *hdev, struct hci_conn *conn)
return err;
}
-int hci_le_create_cis_sync(struct hci_dev *hdev, struct hci_conn *conn)
+int hci_le_create_cis_sync(struct hci_dev *hdev)
{
struct {
struct hci_cp_le_create_cis cp;
struct hci_cis cis[0x1f];
} cmd;
- u8 cig;
- struct hci_conn *hcon = conn;
+ struct hci_conn *conn;
+ u8 cig = BT_ISO_QOS_CIG_UNSET;
+
+ /* The spec allows only one pending LE Create CIS command at a time. If
+ * the command is pending now, don't do anything. We check for pending
+ * connections after each CIS Established event.
+ *
+ * BLUETOOTH CORE SPECIFICATION Version 5.3 | Vol 4, Part E
+ * page 2566:
+ *
+ * If the Host issues this command before all the
+ * HCI_LE_CIS_Established events from the previous use of the
+ * command have been generated, the Controller shall return the
+ * error code Command Disallowed (0x0C).
+ *
+ * BLUETOOTH CORE SPECIFICATION Version 5.3 | Vol 4, Part E
+ * page 2567:
+ *
+ * When the Controller receives the HCI_LE_Create_CIS command, the
+ * Controller sends the HCI_Command_Status event to the Host. An
+ * HCI_LE_CIS_Established event will be generated for each CIS when it
+ * is established or if it is disconnected or considered lost before
+ * being established; until all the events are generated, the command
+ * remains pending.
+ */
memset(&cmd, 0, sizeof(cmd));
- cmd.cis[0].acl_handle = cpu_to_le16(conn->parent->handle);
- cmd.cis[0].cis_handle = cpu_to_le16(conn->handle);
- cmd.cp.num_cis++;
- cig = conn->iso_qos.ucast.cig;
hci_dev_lock(hdev);
rcu_read_lock();
+ /* Wait until previous Create CIS has completed */
+ list_for_each_entry_rcu(conn, &hdev->conn_hash.list, list) {
+ if (test_bit(HCI_CONN_CREATE_CIS, &conn->flags))
+ goto done;
+ }
+
+ /* Find CIG with all CIS ready */
+ list_for_each_entry_rcu(conn, &hdev->conn_hash.list, list) {
+ struct hci_conn *link;
+
+ if (hci_conn_check_create_cis(conn))
+ continue;
+
+ cig = conn->iso_qos.ucast.cig;
+
+ list_for_each_entry_rcu(link, &hdev->conn_hash.list, list) {
+ if (hci_conn_check_create_cis(link) > 0 &&
+ link->iso_qos.ucast.cig == cig &&
+ link->state != BT_CONNECTED) {
+ cig = BT_ISO_QOS_CIG_UNSET;
+ break;
+ }
+ }
+
+ if (cig != BT_ISO_QOS_CIG_UNSET)
+ break;
+ }
+
+ if (cig == BT_ISO_QOS_CIG_UNSET)
+ goto done;
+
list_for_each_entry_rcu(conn, &hdev->conn_hash.list, list) {
struct hci_cis *cis = &cmd.cis[cmd.cp.num_cis];
- if (conn == hcon || conn->type != ISO_LINK ||
- conn->state == BT_CONNECTED ||
+ if (hci_conn_check_create_cis(conn) ||
conn->iso_qos.ucast.cig != cig)
continue;
- /* Check if all CIS(s) belonging to a CIG are ready */
- if (!conn->parent || conn->parent->state != BT_CONNECTED ||
- conn->state != BT_CONNECT) {
- cmd.cp.num_cis = 0;
- break;
- }
-
- /* Group all CIS with state BT_CONNECT since the spec don't
- * allow to send them individually:
- *
- * BLUETOOTH CORE SPECIFICATION Version 5.3 | Vol 4, Part E
- * page 2566:
- *
- * If the Host issues this command before all the
- * HCI_LE_CIS_Established events from the previous use of the
- * command have been generated, the Controller shall return the
- * error code Command Disallowed (0x0C).
- */
+ set_bit(HCI_CONN_CREATE_CIS, &conn->flags);
cis->acl_handle = cpu_to_le16(conn->parent->handle);
cis->cis_handle = cpu_to_le16(conn->handle);
cmd.cp.num_cis++;
+
+ if (cmd.cp.num_cis >= ARRAY_SIZE(cmd.cis))
+ break;
}
+done:
rcu_read_unlock();
hci_dev_unlock(hdev);
@@ -1681,7 +1681,7 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
}
/* Create CIS if pending */
- hci_le_create_cis(hcon);
+ hci_le_create_cis_pending(hcon->hdev);
return;
}