Commit 42ecf194 authored by Iulia Tanasescu's avatar Iulia Tanasescu Committed by Luiz Augusto von Dentz
Browse files

Bluetooth: ISO: Do not emit LE BIG Create Sync if previous is pending



The Bluetooth Core spec does not allow a LE BIG Create sync command to be
sent to Controller if another one is pending (Vol 4, Part E, page 2586).

In order to avoid this issue, the HCI_CONN_CREATE_BIG_SYNC was added
to mark that the LE BIG Create Sync command has been sent for a hcon.
Once the BIG Sync Established event is received, the hcon flag is
erased and the next pending hcon is handled.

Signed-off-by: default avatarIulia Tanasescu <iulia.tanasescu@nxp.com>
Signed-off-by: default avatarLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
parent 79321b06
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@
#define HCI_MAX_ACL_SIZE	1024
#define HCI_MAX_SCO_SIZE	255
#define HCI_MAX_ISO_SIZE	251
#define HCI_MAX_ISO_BIS		31
#define HCI_MAX_EVENT_SIZE	260
#define HCI_MAX_FRAME_SIZE	(HCI_MAX_ACL_SIZE + 4)

+29 −0
Original line number Diff line number Diff line
@@ -711,6 +711,9 @@ struct hci_conn {
	__s8		tx_power;
	__s8		max_tx_power;
	struct bt_iso_qos iso_qos;
	__u8		num_bis;
	__u8		bis[HCI_MAX_ISO_BIS];

	unsigned long	flags;

	enum conn_reasons conn_reason;
@@ -946,6 +949,7 @@ enum {
	HCI_CONN_PER_ADV,
	HCI_CONN_BIG_CREATED,
	HCI_CONN_CREATE_CIS,
	HCI_CONN_CREATE_BIG_SYNC,
	HCI_CONN_BIG_SYNC,
	HCI_CONN_BIG_SYNC_FAILED,
	HCI_CONN_CREATE_PA_SYNC,
@@ -1295,6 +1299,30 @@ static inline struct hci_conn *hci_conn_hash_lookup_big(struct hci_dev *hdev,
	return NULL;
}

static inline struct hci_conn *
hci_conn_hash_lookup_big_sync_pend(struct hci_dev *hdev,
				   __u8 handle, __u8 num_bis)
{
	struct hci_conn_hash *h = &hdev->conn_hash;
	struct hci_conn  *c;

	rcu_read_lock();

	list_for_each_entry_rcu(c, &h->list, list) {
		if (c->type != ISO_LINK)
			continue;

		if (handle == c->iso_qos.bcast.big && num_bis == c->num_bis) {
			rcu_read_unlock();
			return c;
		}
	}

	rcu_read_unlock();

	return NULL;
}

static inline struct hci_conn *
hci_conn_hash_lookup_big_state(struct hci_dev *hdev, __u8 handle,  __u16 state)
{
@@ -1479,6 +1507,7 @@ void hci_sco_setup(struct hci_conn *conn, __u8 status);
bool hci_iso_setup_path(struct hci_conn *conn);
int hci_le_create_cis_pending(struct hci_dev *hdev);
int hci_pa_create_sync_pending(struct hci_dev *hdev);
int hci_le_big_create_sync_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,
+73 −14
Original line number Diff line number Diff line
@@ -2180,34 +2180,93 @@ struct hci_conn *hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst,
	return conn;
}

static bool hci_conn_check_create_big_sync(struct hci_conn *conn)
{
	if (!conn->num_bis)
		return false;

	return true;
}

int hci_le_big_create_sync_pending(struct hci_dev *hdev)
{
	DEFINE_FLEX(struct hci_cp_le_big_create_sync, pdu, bis, num_bis, 0x11);
	struct hci_conn *conn;

	rcu_read_lock();

	pdu->num_bis = 0;

	/* The spec allows only one pending LE BIG Create Sync command at
	 * a time. If the command is pending now, don't do anything. We
	 * check for pending connections after each BIG Sync Established
	 * event.
	 *
	 * BLUETOOTH CORE SPECIFICATION Version 5.3 | Vol 4, Part E
	 * page 2586:
	 *
	 * If the Host sends this command when the Controller is in the
	 * process of synchronizing to any BIG, i.e. the HCI_LE_BIG_Sync_
	 * Established event has not been generated, the Controller shall
	 * return the error code Command Disallowed (0x0C).
	 */
	list_for_each_entry_rcu(conn, &hdev->conn_hash.list, list) {
		if (test_bit(HCI_CONN_CREATE_BIG_SYNC, &conn->flags))
			goto unlock;
	}

	list_for_each_entry_rcu(conn, &hdev->conn_hash.list, list) {
		if (hci_conn_check_create_big_sync(conn)) {
			struct bt_iso_qos *qos = &conn->iso_qos;

			set_bit(HCI_CONN_CREATE_BIG_SYNC, &conn->flags);

			pdu->handle = qos->bcast.big;
			pdu->sync_handle = cpu_to_le16(conn->sync_handle);
			pdu->encryption = qos->bcast.encryption;
			memcpy(pdu->bcode, qos->bcast.bcode,
			       sizeof(pdu->bcode));
			pdu->mse = qos->bcast.mse;
			pdu->timeout = cpu_to_le16(qos->bcast.timeout);
			pdu->num_bis = conn->num_bis;
			memcpy(pdu->bis, conn->bis, conn->num_bis);

			break;
		}
	}

unlock:
	rcu_read_unlock();

	if (!pdu->num_bis)
		return 0;

	return hci_send_cmd(hdev, HCI_OP_LE_BIG_CREATE_SYNC,
			    struct_size(pdu, bis, pdu->num_bis), pdu);
}

int hci_le_big_create_sync(struct hci_dev *hdev, struct hci_conn *hcon,
			   struct bt_iso_qos *qos,
			   __u16 sync_handle, __u8 num_bis, __u8 bis[])
{
	DEFINE_FLEX(struct hci_cp_le_big_create_sync, pdu, bis, num_bis, 0x11);
	int err;

	if (num_bis < 0x01 || num_bis > pdu->num_bis)
	if (num_bis < 0x01 || num_bis > ISO_MAX_NUM_BIS)
		return -EINVAL;

	err = qos_set_big(hdev, qos);
	if (err)
		return err;

	if (hcon)
		hcon->iso_qos.bcast.big = qos->bcast.big;
	if (hcon) {
		/* Update hcon QoS */
		hcon->iso_qos = *qos;

	pdu->handle = qos->bcast.big;
	pdu->sync_handle = cpu_to_le16(sync_handle);
	pdu->encryption = qos->bcast.encryption;
	memcpy(pdu->bcode, qos->bcast.bcode, sizeof(pdu->bcode));
	pdu->mse = qos->bcast.mse;
	pdu->timeout = cpu_to_le16(qos->bcast.timeout);
	pdu->num_bis = num_bis;
	memcpy(pdu->bis, bis, num_bis);
		hcon->num_bis = num_bis;
		memcpy(hcon->bis, bis, num_bis);
	}

	return hci_send_cmd(hdev, HCI_OP_LE_BIG_CREATE_SYNC,
			    struct_size(pdu, bis, num_bis), pdu);
	return hci_le_big_create_sync_pending(hdev);
}

static void create_big_complete(struct hci_dev *hdev, void *data, int err)
+19 −1
Original line number Diff line number Diff line
@@ -6920,7 +6920,7 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
					    struct sk_buff *skb)
{
	struct hci_evt_le_big_sync_estabilished *ev = data;
	struct hci_conn *bis;
	struct hci_conn *bis, *conn;
	int i;

	bt_dev_dbg(hdev, "status 0x%2.2x", ev->status);
@@ -6931,6 +6931,20 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,

	hci_dev_lock(hdev);

	conn = hci_conn_hash_lookup_big_sync_pend(hdev, ev->handle,
						  ev->num_bis);
	if (!conn) {
		bt_dev_err(hdev,
			   "Unable to find connection for big 0x%2.2x",
			   ev->handle);
		goto unlock;
	}

	clear_bit(HCI_CONN_CREATE_BIG_SYNC, &conn->flags);

	conn->num_bis = 0;
	memset(conn->bis, 0, sizeof(conn->num_bis));

	for (i = 0; i < ev->num_bis; i++) {
		u16 handle = le16_to_cpu(ev->bis[i]);
		__le32 interval;
@@ -6980,6 +6994,10 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
			hci_connect_cfm(bis, ev->status);
		}

unlock:
	/* Handle any other pending BIG sync command */
	hci_le_big_create_sync_pending(hdev);

	hci_dev_unlock(hdev);
}

+3 −1
Original line number Diff line number Diff line
@@ -1957,6 +1957,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)

		if (sk) {
			int err;
			struct hci_conn	*hcon = iso_pi(sk)->conn->hcon;

			iso_pi(sk)->qos.bcast.encryption = ev2->encryption;

@@ -1965,7 +1966,8 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)

			if (!test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags) &&
			    !test_and_set_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags)) {
				err = hci_le_big_create_sync(hdev, NULL,
				err = hci_le_big_create_sync(hdev,
							     hcon,
							     &iso_pi(sk)->qos,
							     iso_pi(sk)->sync_handle,
							     iso_pi(sk)->bc_num_bis,