Skip to content

Commit

Permalink
Bluetooth: ISO: do not emit new LE Create CIS if previous is pending
Browse files Browse the repository at this point in the history
[ Upstream commit 7f74563 ]

LE Create CIS command shall not be sent before all CIS Established
events from its previous invocation have been processed. Currently it is
sent via hci_sync but that only waits for the first event, but there can
be multiple.

Make it wait for all events, and simplify the CIS creation as follows:

Add new flag HCI_CONN_CREATE_CIS, which is set if Create CIS has been
sent for the connection but it is not yet completed.

Make BT_CONNECT state to mean the connection wants Create CIS.

On events after which new Create CIS may need to be sent, send it if
possible and some connections need it. These events are:
hci_connect_cis, iso_connect_cfm, hci_cs_le_create_cis,
hci_le_cis_estabilished_evt.

The Create CIS status/completion events shall queue new Create CIS only
if at least one of the connections transitions away from BT_CONNECT, so
that we don't loop if controller is sending bogus events.

This fixes sending multiple CIS Create for the same CIS in the
"ISO AC 6(i) - Success" BlueZ test case:

< HCI Command: LE Create Co.. (0x08|0x0064) plen 9  torvalds#129 [hci0]
        Number of CIS: 2
        CIS Handle: 257
        ACL Handle: 42
        CIS Handle: 258
        ACL Handle: 42
> HCI Event: Command Status (0x0f) plen 4           torvalds#130 [hci0]
      LE Create Connected Isochronous Stream (0x08|0x0064) ncmd 1
        Status: Success (0x00)
> HCI Event: LE Meta Event (0x3e) plen 29           torvalds#131 [hci0]
      LE Connected Isochronous Stream Established (0x19)
        Status: Success (0x00)
        Connection Handle: 257
        ...
< HCI Command: LE Setup Is.. (0x08|0x006e) plen 13  torvalds#132 [hci0]
        ...
> HCI Event: Command Complete (0x0e) plen 6         torvalds#133 [hci0]
      LE Setup Isochronous Data Path (0x08|0x006e) ncmd 1
        ...
< HCI Command: LE Create Co.. (0x08|0x0064) plen 5  torvalds#134 [hci0]
        Number of CIS: 1
        CIS Handle: 258
        ACL Handle: 42
> HCI Event: Command Status (0x0f) plen 4           torvalds#135 [hci0]
      LE Create Connected Isochronous Stream (0x08|0x0064) ncmd 1
        Status: ACL Connection Already Exists (0x0b)
> HCI Event: LE Meta Event (0x3e) plen 29           torvalds#136 [hci0]
      LE Connected Isochronous Stream Established (0x19)
        Status: Success (0x00)
        Connection Handle: 258
        ...

Fixes: c09b80b ("Bluetooth: hci_conn: Fix not waiting for HCI_EVT_LE_CIS_ESTABLISHED")
Signed-off-by: Pauli Virtanen <pav@iki.fi>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
  • Loading branch information
pv authored and gregkh committed Sep 11, 2023
1 parent 260c431 commit e5931e6
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 78 deletions.
4 changes: 3 additions & 1 deletion include/net/bluetooth/hci_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -975,6 +975,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)
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion include/net/bluetooth/hci_sync.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
74 changes: 30 additions & 44 deletions net/bluetooth/hci_conn.c
Original file line number Diff line number Diff line change
Expand Up @@ -1990,59 +1990,47 @@ bool hci_iso_setup_path(struct hci_conn *conn)
return true;
}

static int hci_create_cis_sync(struct hci_dev *hdev, void *data)
int hci_conn_check_create_cis(struct hci_conn *conn)
{
return hci_le_create_cis_sync(hdev, data);
}
if (conn->type != ISO_LINK || !bacmp(&conn->dst, BDADDR_ANY))
return -EINVAL;

int hci_le_create_cis(struct hci_conn *conn)
{
struct hci_conn *cis;
struct hci_link *link, *t;
struct hci_dev *hdev = conn->hdev;
int err;
if (!conn->parent || conn->parent->state != BT_CONNECTED ||
conn->state != BT_CONNECT || conn->handle == HCI_CONN_HANDLE_UNSET)
return 1;

bt_dev_dbg(hdev, "hcon %p", conn);
return 0;
}

switch (conn->type) {
case LE_LINK:
if (conn->state != BT_CONNECTED || list_empty(&conn->link_list))
return -EINVAL;
static int hci_create_cis_sync(struct hci_dev *hdev, void *data)
{
return hci_le_create_cis_sync(hdev);
}

cis = NULL;
int hci_le_create_cis_pending(struct hci_dev *hdev)
{
struct hci_conn *conn;
bool pending = false;

/* 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;
rcu_read_lock();

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,
Expand Down Expand Up @@ -2317,11 +2305,9 @@ struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst,
return ERR_PTR(-ENOLINK);
}

/* 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;
}
Expand Down
25 changes: 21 additions & 4 deletions net/bluetooth/hci_event.c
Original file line number Diff line number Diff line change
Expand Up @@ -3810,6 +3810,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);
Expand Down Expand Up @@ -3852,13 +3853,15 @@ 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)
hci_le_create_cis(conn);

if (conn->state == BT_CONNECT)
pending = true;
}

unlock:
if (pending)
hci_le_create_cis_pending(hdev);

hci_dev_unlock(hdev);

return rp->status;
Expand Down Expand Up @@ -4224,6 +4227,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);
Expand All @@ -4246,12 +4250,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);
}

Expand Down Expand Up @@ -6790,6 +6800,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;
struct bt_iso_qos *qos;
bool pending = false;
u16 handle = __le16_to_cpu(ev->handle);

bt_dev_dbg(hdev, "status 0x%2.2x", ev->status);
Expand All @@ -6813,6 +6824,8 @@ static void hci_le_cis_estabilished_evt(struct hci_dev *hdev, void *data,

qos = &conn->iso_qos;

pending = test_and_clear_bit(HCI_CONN_CREATE_CIS, &conn->flags);

/* Convert ISO Interval (1.25 ms slots) to SDU Interval (us) */
qos->ucast.in.interval = le16_to_cpu(ev->interval) * 1250;
qos->ucast.out.interval = qos->ucast.in.interval;
Expand Down Expand Up @@ -6854,10 +6867,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);
}

Expand Down
90 changes: 63 additions & 27 deletions net/bluetooth/hci_sync.c
Original file line number Diff line number Diff line change
Expand Up @@ -6260,56 +6260,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) {
struct hci_cis *cis = &cmd.cis[cmd.cp.num_cis];
if (test_bit(HCI_CONN_CREATE_CIS, &conn->flags))
goto done;
}

if (conn == hcon || conn->type != ISO_LINK ||
conn->state == BT_CONNECTED ||
conn->iso_qos.ucast.cig != cig)
/* 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;

/* 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;
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;
}
}

/* 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).
*/
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 (hci_conn_check_create_cis(conn) ||
conn->iso_qos.ucast.cig != cig)
continue;

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);
Expand Down
2 changes: 1 addition & 1 deletion net/bluetooth/iso.c
Original file line number Diff line number Diff line change
Expand Up @@ -1690,7 +1690,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;
}

Expand Down

0 comments on commit e5931e6

Please sign in to comment.