mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	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: Iulia Tanasescu <iulia.tanasescu@nxp.com> Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This commit is contained in:
		
							parent
							
								
									79321b06a0
								
							
						
					
					
						commit
						42ecf19471
					
				
					 5 changed files with 125 additions and 16 deletions
				
			
		| 
						 | 
					@ -29,6 +29,7 @@
 | 
				
			||||||
#define HCI_MAX_ACL_SIZE	1024
 | 
					#define HCI_MAX_ACL_SIZE	1024
 | 
				
			||||||
#define HCI_MAX_SCO_SIZE	255
 | 
					#define HCI_MAX_SCO_SIZE	255
 | 
				
			||||||
#define HCI_MAX_ISO_SIZE	251
 | 
					#define HCI_MAX_ISO_SIZE	251
 | 
				
			||||||
 | 
					#define HCI_MAX_ISO_BIS		31
 | 
				
			||||||
#define HCI_MAX_EVENT_SIZE	260
 | 
					#define HCI_MAX_EVENT_SIZE	260
 | 
				
			||||||
#define HCI_MAX_FRAME_SIZE	(HCI_MAX_ACL_SIZE + 4)
 | 
					#define HCI_MAX_FRAME_SIZE	(HCI_MAX_ACL_SIZE + 4)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -711,6 +711,9 @@ struct hci_conn {
 | 
				
			||||||
	__s8		tx_power;
 | 
						__s8		tx_power;
 | 
				
			||||||
	__s8		max_tx_power;
 | 
						__s8		max_tx_power;
 | 
				
			||||||
	struct bt_iso_qos iso_qos;
 | 
						struct bt_iso_qos iso_qos;
 | 
				
			||||||
 | 
						__u8		num_bis;
 | 
				
			||||||
 | 
						__u8		bis[HCI_MAX_ISO_BIS];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	unsigned long	flags;
 | 
						unsigned long	flags;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	enum conn_reasons conn_reason;
 | 
						enum conn_reasons conn_reason;
 | 
				
			||||||
| 
						 | 
					@ -946,6 +949,7 @@ enum {
 | 
				
			||||||
	HCI_CONN_PER_ADV,
 | 
						HCI_CONN_PER_ADV,
 | 
				
			||||||
	HCI_CONN_BIG_CREATED,
 | 
						HCI_CONN_BIG_CREATED,
 | 
				
			||||||
	HCI_CONN_CREATE_CIS,
 | 
						HCI_CONN_CREATE_CIS,
 | 
				
			||||||
 | 
						HCI_CONN_CREATE_BIG_SYNC,
 | 
				
			||||||
	HCI_CONN_BIG_SYNC,
 | 
						HCI_CONN_BIG_SYNC,
 | 
				
			||||||
	HCI_CONN_BIG_SYNC_FAILED,
 | 
						HCI_CONN_BIG_SYNC_FAILED,
 | 
				
			||||||
	HCI_CONN_CREATE_PA_SYNC,
 | 
						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;
 | 
						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 *
 | 
					static inline struct hci_conn *
 | 
				
			||||||
hci_conn_hash_lookup_big_state(struct hci_dev *hdev, __u8 handle,  __u16 state)
 | 
					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);
 | 
					bool hci_iso_setup_path(struct hci_conn *conn);
 | 
				
			||||||
int hci_le_create_cis_pending(struct hci_dev *hdev);
 | 
					int hci_le_create_cis_pending(struct hci_dev *hdev);
 | 
				
			||||||
int hci_pa_create_sync_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);
 | 
					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,
 | 
					struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2180,34 +2180,93 @@ struct hci_conn *hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst,
 | 
				
			||||||
	return conn;
 | 
						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,
 | 
					int hci_le_big_create_sync(struct hci_dev *hdev, struct hci_conn *hcon,
 | 
				
			||||||
			   struct bt_iso_qos *qos,
 | 
								   struct bt_iso_qos *qos,
 | 
				
			||||||
			   __u16 sync_handle, __u8 num_bis, __u8 bis[])
 | 
								   __u16 sync_handle, __u8 num_bis, __u8 bis[])
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	DEFINE_FLEX(struct hci_cp_le_big_create_sync, pdu, bis, num_bis, 0x11);
 | 
					 | 
				
			||||||
	int err;
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (num_bis < 0x01 || num_bis > pdu->num_bis)
 | 
						if (num_bis < 0x01 || num_bis > ISO_MAX_NUM_BIS)
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = qos_set_big(hdev, qos);
 | 
						err = qos_set_big(hdev, qos);
 | 
				
			||||||
	if (err)
 | 
						if (err)
 | 
				
			||||||
		return err;
 | 
							return err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (hcon)
 | 
						if (hcon) {
 | 
				
			||||||
		hcon->iso_qos.bcast.big = qos->bcast.big;
 | 
							/* Update hcon QoS */
 | 
				
			||||||
 | 
							hcon->iso_qos = *qos;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pdu->handle = qos->bcast.big;
 | 
							hcon->num_bis = num_bis;
 | 
				
			||||||
	pdu->sync_handle = cpu_to_le16(sync_handle);
 | 
							memcpy(hcon->bis, bis, num_bis);
 | 
				
			||||||
	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);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return hci_send_cmd(hdev, HCI_OP_LE_BIG_CREATE_SYNC,
 | 
						return hci_le_big_create_sync_pending(hdev);
 | 
				
			||||||
			    struct_size(pdu, bis, num_bis), pdu);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void create_big_complete(struct hci_dev *hdev, void *data, int err)
 | 
					static void create_big_complete(struct hci_dev *hdev, void *data, int err)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6920,7 +6920,7 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
 | 
				
			||||||
					    struct sk_buff *skb)
 | 
										    struct sk_buff *skb)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct hci_evt_le_big_sync_estabilished *ev = data;
 | 
						struct hci_evt_le_big_sync_estabilished *ev = data;
 | 
				
			||||||
	struct hci_conn *bis;
 | 
						struct hci_conn *bis, *conn;
 | 
				
			||||||
	int i;
 | 
						int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bt_dev_dbg(hdev, "status 0x%2.2x", ev->status);
 | 
						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);
 | 
						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++) {
 | 
						for (i = 0; i < ev->num_bis; i++) {
 | 
				
			||||||
		u16 handle = le16_to_cpu(ev->bis[i]);
 | 
							u16 handle = le16_to_cpu(ev->bis[i]);
 | 
				
			||||||
		__le32 interval;
 | 
							__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);
 | 
								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);
 | 
						hci_dev_unlock(hdev);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1957,6 +1957,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (sk) {
 | 
							if (sk) {
 | 
				
			||||||
			int err;
 | 
								int err;
 | 
				
			||||||
 | 
								struct hci_conn	*hcon = iso_pi(sk)->conn->hcon;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			iso_pi(sk)->qos.bcast.encryption = ev2->encryption;
 | 
								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) &&
 | 
								if (!test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags) &&
 | 
				
			||||||
			    !test_and_set_bit(BT_SK_BIG_SYNC, &iso_pi(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)->qos,
 | 
				
			||||||
							     iso_pi(sk)->sync_handle,
 | 
												     iso_pi(sk)->sync_handle,
 | 
				
			||||||
							     iso_pi(sk)->bc_num_bis,
 | 
												     iso_pi(sk)->bc_num_bis,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue