Commit 23205562 authored by Pauli Virtanen's avatar Pauli Virtanen Committed by Luiz Augusto von Dentz
Browse files

Bluetooth: separate CIS_LINK and BIS_LINK link types



Use separate link type id for unicast and broadcast ISO connections.
These connection types are handled with separate HCI commands, socket
API is different, and hci_conn has union fields that are different in
the two cases, so they shall not be mixed up.

Currently in most places it is attempted to distinguish ucast by
bacmp(&c->dst, BDADDR_ANY) but it is wrong as dst is set for bcast sink
hci_conn in iso_conn_ready(). Additionally checking sync_handle might be
OK, but depends on details of bcast conn configuration flow.

To avoid complicating it, use separate link types.

Fixes: f764a6c2 ("Bluetooth: ISO: Add broadcast support")
Signed-off-by: default avatarPauli Virtanen <pav@iki.fi>
Signed-off-by: default avatarLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
parent 5bd5c716
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -558,7 +558,8 @@ enum {
#define ESCO_LINK	0x02
/* Low Energy links do not have defined link type. Use invented one */
#define LE_LINK		0x80
#define ISO_LINK	0x82
#define CIS_LINK	0x82
#define BIS_LINK	0x83
#define INVALID_LINK	0xff

/* LMP features */
+21 −27
Original line number Diff line number Diff line
@@ -999,7 +999,8 @@ static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c)
	case ESCO_LINK:
		h->sco_num++;
		break;
	case ISO_LINK:
	case CIS_LINK:
	case BIS_LINK:
		h->iso_num++;
		break;
	}
@@ -1025,7 +1026,8 @@ static inline void hci_conn_hash_del(struct hci_dev *hdev, struct hci_conn *c)
	case ESCO_LINK:
		h->sco_num--;
		break;
	case ISO_LINK:
	case CIS_LINK:
	case BIS_LINK:
		h->iso_num--;
		break;
	}
@@ -1042,7 +1044,8 @@ static inline unsigned int hci_conn_num(struct hci_dev *hdev, __u8 type)
	case SCO_LINK:
	case ESCO_LINK:
		return h->sco_num;
	case ISO_LINK:
	case CIS_LINK:
	case BIS_LINK:
		return h->iso_num;
	default:
		return 0;
@@ -1103,7 +1106,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_bis(struct hci_dev *hdev,
	rcu_read_lock();

	list_for_each_entry_rcu(c, &h->list, list) {
		if (bacmp(&c->dst, ba) || c->type != ISO_LINK)
		if (bacmp(&c->dst, ba) || c->type != BIS_LINK)
			continue;

		if (c->iso_qos.bcast.bis == bis) {
@@ -1125,7 +1128,7 @@ hci_conn_hash_lookup_create_pa_sync(struct hci_dev *hdev)
	rcu_read_lock();

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

		if (!test_bit(HCI_CONN_CREATE_PA_SYNC, &c->flags))
@@ -1151,7 +1154,7 @@ hci_conn_hash_lookup_per_adv_bis(struct hci_dev *hdev,
	rcu_read_lock();

	list_for_each_entry_rcu(c, &h->list, list) {
		if (bacmp(&c->dst, ba) || c->type != ISO_LINK ||
		if (bacmp(&c->dst, ba) || c->type != BIS_LINK ||
		    !test_bit(HCI_CONN_PER_ADV, &c->flags))
			continue;

@@ -1241,7 +1244,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_cis(struct hci_dev *hdev,
	rcu_read_lock();

	list_for_each_entry_rcu(c, &h->list, list) {
		if (c->type != ISO_LINK || !bacmp(&c->dst, BDADDR_ANY))
		if (c->type != CIS_LINK)
			continue;

		/* Match CIG ID if set */
@@ -1273,7 +1276,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_cig(struct hci_dev *hdev,
	rcu_read_lock();

	list_for_each_entry_rcu(c, &h->list, list) {
		if (c->type != ISO_LINK || !bacmp(&c->dst, BDADDR_ANY))
		if (c->type != CIS_LINK)
			continue;

		if (handle == c->iso_qos.ucast.cig) {
@@ -1296,17 +1299,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_big(struct hci_dev *hdev,
	rcu_read_lock();

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

		/* An ISO_LINK hcon with BDADDR_ANY as destination
		 * address is a Broadcast connection. A Broadcast
		 * slave connection is associated with a PA train,
		 * so the sync_handle can be used to differentiate
		 * from unicast.
		 */
		if (bacmp(&c->dst, BDADDR_ANY) &&
		    c->sync_handle == HCI_SYNC_HANDLE_INVALID)
		if (c->type != BIS_LINK)
			continue;

		if (handle == c->iso_qos.bcast.big) {
@@ -1330,7 +1323,7 @@ hci_conn_hash_lookup_big_sync_pend(struct hci_dev *hdev,
	rcu_read_lock();

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

		if (handle == c->iso_qos.bcast.big && num_bis == c->num_bis) {
@@ -1353,7 +1346,7 @@ hci_conn_hash_lookup_big_state(struct hci_dev *hdev, __u8 handle, __u16 state)
	rcu_read_lock();

	list_for_each_entry_rcu(c, &h->list, list) {
		if (bacmp(&c->dst, BDADDR_ANY) || c->type != ISO_LINK ||
		if (c->type != BIS_LINK || bacmp(&c->dst, BDADDR_ANY) ||
		    c->state != state)
			continue;

@@ -1377,7 +1370,7 @@ hci_conn_hash_lookup_pa_sync_big_handle(struct hci_dev *hdev, __u8 big)
	rcu_read_lock();

	list_for_each_entry_rcu(c, &h->list, list) {
		if (c->type != ISO_LINK ||
		if (c->type != BIS_LINK ||
		    !test_bit(HCI_CONN_PA_SYNC, &c->flags))
			continue;

@@ -1400,7 +1393,7 @@ hci_conn_hash_lookup_pa_sync_handle(struct hci_dev *hdev, __u16 sync_handle)
	rcu_read_lock();

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

		/* Ignore the listen hcon, we are looking
@@ -2015,7 +2008,8 @@ static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr,
	case ESCO_LINK:
		return sco_connect_ind(hdev, bdaddr, flags);

	case ISO_LINK:
	case CIS_LINK:
	case BIS_LINK:
		return iso_connect_ind(hdev, bdaddr, flags);

	default:
+26 −18
Original line number Diff line number Diff line
@@ -785,7 +785,7 @@ static int hci_le_big_terminate(struct hci_dev *hdev, u8 big, struct hci_conn *c
	d->sync_handle = conn->sync_handle;

	if (test_and_clear_bit(HCI_CONN_PA_SYNC, &conn->flags)) {
		hci_conn_hash_list_flag(hdev, find_bis, ISO_LINK,
		hci_conn_hash_list_flag(hdev, find_bis, BIS_LINK,
					HCI_CONN_PA_SYNC, d);

		if (!d->count)
@@ -795,7 +795,7 @@ static int hci_le_big_terminate(struct hci_dev *hdev, u8 big, struct hci_conn *c
	}

	if (test_and_clear_bit(HCI_CONN_BIG_SYNC, &conn->flags)) {
		hci_conn_hash_list_flag(hdev, find_bis, ISO_LINK,
		hci_conn_hash_list_flag(hdev, find_bis, BIS_LINK,
					HCI_CONN_BIG_SYNC, d);

		if (!d->count)
@@ -885,9 +885,11 @@ static void cis_cleanup(struct hci_conn *conn)
	/* Check if ISO connection is a CIS and remove CIG if there are
	 * no other connections using it.
	 */
	hci_conn_hash_list_state(hdev, find_cis, ISO_LINK, BT_BOUND, &d);
	hci_conn_hash_list_state(hdev, find_cis, ISO_LINK, BT_CONNECT, &d);
	hci_conn_hash_list_state(hdev, find_cis, ISO_LINK, BT_CONNECTED, &d);
	hci_conn_hash_list_state(hdev, find_cis, CIS_LINK, BT_BOUND, &d);
	hci_conn_hash_list_state(hdev, find_cis, CIS_LINK, BT_CONNECT,
				 &d);
	hci_conn_hash_list_state(hdev, find_cis, CIS_LINK, BT_CONNECTED,
				 &d);
	if (d.count)
		return;

@@ -910,7 +912,8 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t
		if (!hdev->acl_mtu)
			return ERR_PTR(-ECONNREFUSED);
		break;
	case ISO_LINK:
	case CIS_LINK:
	case BIS_LINK:
		if (hdev->iso_mtu)
			/* Dedicated ISO Buffer exists */
			break;
@@ -974,7 +977,8 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t
		hci_copy_identity_address(hdev, &conn->src, &conn->src_type);
		conn->mtu = hdev->le_mtu ? hdev->le_mtu : hdev->acl_mtu;
		break;
	case ISO_LINK:
	case CIS_LINK:
	case BIS_LINK:
		/* conn->src should reflect the local identity address */
		hci_copy_identity_address(hdev, &conn->src, &conn->src_type);

@@ -1071,7 +1075,8 @@ static void hci_conn_cleanup_child(struct hci_conn *conn, u8 reason)
		if (HCI_CONN_HANDLE_UNSET(conn->handle))
			hci_conn_failed(conn, reason);
		break;
	case ISO_LINK:
	case CIS_LINK:
	case BIS_LINK:
		if ((conn->state != BT_CONNECTED &&
		    !test_bit(HCI_CONN_CREATE_CIS, &conn->flags)) ||
		    test_bit(HCI_CONN_BIG_CREATED, &conn->flags))
@@ -1146,7 +1151,8 @@ void hci_conn_del(struct hci_conn *conn)
			hdev->acl_cnt += conn->sent;
	} else {
		/* Unacked ISO frames */
		if (conn->type == ISO_LINK) {
		if (conn->type == CIS_LINK ||
		    conn->type == BIS_LINK) {
			if (hdev->iso_pkts)
				hdev->iso_cnt += conn->sent;
			else if (hdev->le_pkts)
@@ -1532,7 +1538,7 @@ static struct hci_conn *hci_add_bis(struct hci_dev *hdev, bdaddr_t *dst,
		     memcmp(conn->le_per_adv_data, base, base_len)))
		return ERR_PTR(-EADDRINUSE);

	conn = hci_conn_add_unset(hdev, ISO_LINK, dst, HCI_ROLE_MASTER);
	conn = hci_conn_add_unset(hdev, BIS_LINK, dst, HCI_ROLE_MASTER);
	if (IS_ERR(conn))
		return conn;

@@ -1740,7 +1746,7 @@ static int hci_le_create_big(struct hci_conn *conn, struct bt_iso_qos *qos)
	data.count = 0;

	/* Create a BIS for each bound connection */
	hci_conn_hash_list_state(hdev, bis_list, ISO_LINK,
	hci_conn_hash_list_state(hdev, bis_list, BIS_LINK,
				 BT_BOUND, &data);

	cp.handle = qos->bcast.big;
@@ -1829,12 +1835,12 @@ static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos)
		for (data.cig = 0x00; data.cig < 0xf0; data.cig++) {
			data.count = 0;

			hci_conn_hash_list_state(hdev, find_cis, ISO_LINK,
			hci_conn_hash_list_state(hdev, find_cis, CIS_LINK,
						 BT_CONNECT, &data);
			if (data.count)
				continue;

			hci_conn_hash_list_state(hdev, find_cis, ISO_LINK,
			hci_conn_hash_list_state(hdev, find_cis, CIS_LINK,
						 BT_CONNECTED, &data);
			if (!data.count)
				break;
@@ -1884,7 +1890,8 @@ struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst,
	cis = hci_conn_hash_lookup_cis(hdev, dst, dst_type, qos->ucast.cig,
				       qos->ucast.cis);
	if (!cis) {
		cis = hci_conn_add_unset(hdev, ISO_LINK, dst, HCI_ROLE_MASTER);
		cis = hci_conn_add_unset(hdev, CIS_LINK, dst,
					 HCI_ROLE_MASTER);
		if (IS_ERR(cis))
			return cis;
		cis->cleanup = cis_cleanup;
@@ -1976,7 +1983,7 @@ bool hci_iso_setup_path(struct hci_conn *conn)

int hci_conn_check_create_cis(struct hci_conn *conn)
{
	if (conn->type != ISO_LINK || !bacmp(&conn->dst, BDADDR_ANY))
	if (conn->type != CIS_LINK)
		return -EINVAL;

	if (!conn->parent || conn->parent->state != BT_CONNECTED ||
@@ -2072,7 +2079,7 @@ struct hci_conn *hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst,

	bt_dev_dbg(hdev, "dst %pMR type %d sid %d", dst, dst_type, sid);

	conn = hci_conn_add_unset(hdev, ISO_LINK, dst, HCI_ROLE_SLAVE);
	conn = hci_conn_add_unset(hdev, BIS_LINK, dst, HCI_ROLE_SLAVE);
	if (IS_ERR(conn))
		return conn;

@@ -2221,7 +2228,7 @@ struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst,
	 * the start periodic advertising and create BIG commands have
	 * been queued
	 */
	hci_conn_hash_list_state(hdev, bis_mark_per_adv, ISO_LINK,
	hci_conn_hash_list_state(hdev, bis_mark_per_adv, BIS_LINK,
				 BT_BOUND, &data);

	/* Queue start periodic advertising and create BIG */
@@ -2953,7 +2960,8 @@ void hci_conn_tx_queue(struct hci_conn *conn, struct sk_buff *skb)
	 * TODO: SCO support without flowctl (needs to be done in drivers)
	 */
	switch (conn->type) {
	case ISO_LINK:
	case CIS_LINK:
	case BIS_LINK:
	case ACL_LINK:
	case LE_LINK:
		break;
+13 −8
Original line number Diff line number Diff line
@@ -2898,12 +2898,13 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
		break;
	case HCI_ACLDATA_PKT:
		/* Detect if ISO packet has been sent as ACL */
		if (hci_conn_num(hdev, ISO_LINK)) {
		if (hci_conn_num(hdev, CIS_LINK) ||
		    hci_conn_num(hdev, BIS_LINK)) {
			__u16 handle = __le16_to_cpu(hci_acl_hdr(skb)->handle);
			__u8 type;

			type = hci_conn_lookup_type(hdev, hci_handle(handle));
			if (type == ISO_LINK)
			if (type == CIS_LINK || type == BIS_LINK)
				hci_skb_pkt_type(skb) = HCI_ISODATA_PKT;
		}
		break;
@@ -3356,7 +3357,8 @@ static inline void hci_quote_sent(struct hci_conn *conn, int num, int *quote)
	case LE_LINK:
		cnt = hdev->le_mtu ? hdev->le_cnt : hdev->acl_cnt;
		break;
	case ISO_LINK:
	case CIS_LINK:
	case BIS_LINK:
		cnt = hdev->iso_mtu ? hdev->iso_cnt :
			hdev->le_mtu ? hdev->le_cnt : hdev->acl_cnt;
		break;
@@ -3370,7 +3372,7 @@ static inline void hci_quote_sent(struct hci_conn *conn, int num, int *quote)
}

static struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type,
				     int *quote)
				     __u8 type2, int *quote)
{
	struct hci_conn_hash *h = &hdev->conn_hash;
	struct hci_conn *conn = NULL, *c;
@@ -3382,7 +3384,8 @@ static struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type,
	rcu_read_lock();

	list_for_each_entry_rcu(c, &h->list, list) {
		if (c->type != type || skb_queue_empty(&c->data_q))
		if ((c->type != type && c->type != type2) ||
		    skb_queue_empty(&c->data_q))
			continue;

		if (c->state != BT_CONNECTED && c->state != BT_CONFIG)
@@ -3590,7 +3593,7 @@ static void hci_sched_sco(struct hci_dev *hdev, __u8 type)
	else
		cnt = &hdev->sco_cnt;

	while (*cnt && (conn = hci_low_sent(hdev, type, &quote))) {
	while (*cnt && (conn = hci_low_sent(hdev, type, type, &quote))) {
		while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
			BT_DBG("skb %p len %d", skb, skb->len);
			hci_send_conn_frame(hdev, conn, skb);
@@ -3718,12 +3721,14 @@ static void hci_sched_iso(struct hci_dev *hdev)

	BT_DBG("%s", hdev->name);

	if (!hci_conn_num(hdev, ISO_LINK))
	if (!hci_conn_num(hdev, CIS_LINK) &&
	    !hci_conn_num(hdev, BIS_LINK))
		return;

	cnt = hdev->iso_pkts ? &hdev->iso_cnt :
		hdev->le_pkts ? &hdev->le_cnt : &hdev->acl_cnt;
	while (*cnt && (conn = hci_low_sent(hdev, ISO_LINK, &quote))) {
	while (*cnt && (conn = hci_low_sent(hdev, CIS_LINK, BIS_LINK,
					    &quote))) {
		while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
			BT_DBG("skb %p len %d", skb, skb->len);
			hci_send_conn_frame(hdev, conn, skb);
+13 −11
Original line number Diff line number Diff line
@@ -3804,7 +3804,7 @@ static void hci_unbound_cis_failed(struct hci_dev *hdev, u8 cig, u8 status)
	lockdep_assert_held(&hdev->lock);

	list_for_each_entry_safe(conn, tmp, &hdev->conn_hash.list, list) {
		if (conn->type != ISO_LINK || !bacmp(&conn->dst, BDADDR_ANY) ||
		if (conn->type != CIS_LINK ||
		    conn->state == BT_OPEN || conn->iso_qos.ucast.cig != cig)
			continue;

@@ -4467,7 +4467,8 @@ static void hci_num_comp_pkts_evt(struct hci_dev *hdev, void *data,

			break;

		case ISO_LINK:
		case CIS_LINK:
		case BIS_LINK:
			if (hdev->iso_pkts) {
				hdev->iso_cnt += count;
				if (hdev->iso_cnt > hdev->iso_pkts)
@@ -6413,7 +6414,8 @@ static void hci_le_pa_sync_estabilished_evt(struct hci_dev *hdev, void *data,
	conn->sync_handle = le16_to_cpu(ev->handle);
	conn->sid = HCI_SID_INVALID;

	mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ISO_LINK, &flags);
	mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, BIS_LINK,
				      &flags);
	if (!(mask & HCI_LM_ACCEPT)) {
		hci_le_pa_term_sync(hdev, ev->handle);
		goto unlock;
@@ -6423,7 +6425,7 @@ static void hci_le_pa_sync_estabilished_evt(struct hci_dev *hdev, void *data,
		goto unlock;

	/* Add connection to indicate PA sync event */
	pa_sync = hci_conn_add_unset(hdev, ISO_LINK, BDADDR_ANY,
	pa_sync = hci_conn_add_unset(hdev, BIS_LINK, BDADDR_ANY,
				     HCI_ROLE_SLAVE);

	if (IS_ERR(pa_sync))
@@ -6454,7 +6456,7 @@ static void hci_le_per_adv_report_evt(struct hci_dev *hdev, void *data,

	hci_dev_lock(hdev);

	mask |= hci_proto_connect_ind(hdev, BDADDR_ANY, ISO_LINK, &flags);
	mask |= hci_proto_connect_ind(hdev, BDADDR_ANY, BIS_LINK, &flags);
	if (!(mask & HCI_LM_ACCEPT))
		goto unlock;

@@ -6738,7 +6740,7 @@ static void hci_le_cis_estabilished_evt(struct hci_dev *hdev, void *data,
		goto unlock;
	}

	if (conn->type != ISO_LINK) {
	if (conn->type != CIS_LINK) {
		bt_dev_err(hdev,
			   "Invalid connection link type handle 0x%4.4x",
			   handle);
@@ -6856,7 +6858,7 @@ static void hci_le_cis_req_evt(struct hci_dev *hdev, void *data,
	if (!acl)
		goto unlock;

	mask = hci_proto_connect_ind(hdev, &acl->dst, ISO_LINK, &flags);
	mask = hci_proto_connect_ind(hdev, &acl->dst, CIS_LINK, &flags);
	if (!(mask & HCI_LM_ACCEPT)) {
		hci_le_reject_cis(hdev, ev->cis_handle);
		goto unlock;
@@ -6864,8 +6866,8 @@ static void hci_le_cis_req_evt(struct hci_dev *hdev, void *data,

	cis = hci_conn_hash_lookup_handle(hdev, cis_handle);
	if (!cis) {
		cis = hci_conn_add(hdev, ISO_LINK, &acl->dst, HCI_ROLE_SLAVE,
				   cis_handle);
		cis = hci_conn_add(hdev, CIS_LINK, &acl->dst,
				   HCI_ROLE_SLAVE, cis_handle);
		if (IS_ERR(cis)) {
			hci_le_reject_cis(hdev, ev->cis_handle);
			goto unlock;
@@ -6980,7 +6982,7 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
				bt_dev_dbg(hdev, "ignore too large handle %u", handle);
				continue;
			}
			bis = hci_conn_add(hdev, ISO_LINK, BDADDR_ANY,
			bis = hci_conn_add(hdev, BIS_LINK, BDADDR_ANY,
					   HCI_ROLE_SLAVE, handle);
			if (IS_ERR(bis))
				continue;
@@ -7036,7 +7038,7 @@ static void hci_le_big_info_adv_report_evt(struct hci_dev *hdev, void *data,

	hci_dev_lock(hdev);

	mask |= hci_proto_connect_ind(hdev, BDADDR_ANY, ISO_LINK, &flags);
	mask |= hci_proto_connect_ind(hdev, BDADDR_ANY, BIS_LINK, &flags);
	if (!(mask & HCI_LM_ACCEPT))
		goto unlock;

Loading