diff options
Diffstat (limited to 'net/bluetooth/iso.c')
| -rw-r--r-- | net/bluetooth/iso.c | 104 | 
1 files changed, 82 insertions, 22 deletions
diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index 04f6572d35f1..c8793e57f4b5 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -3,7 +3,7 @@   * BlueZ - Bluetooth protocol stack for Linux   *   * Copyright (C) 2022 Intel Corporation - * Copyright 2023 NXP + * Copyright 2023-2024 NXP   */  #include <linux/module.h> @@ -690,11 +690,8 @@ static void iso_sock_cleanup_listen(struct sock *parent)  		iso_sock_kill(sk);  	} -	/* If listening socket stands for a PA sync connection, -	 * properly disconnect the hcon and socket. -	 */ -	if (iso_pi(parent)->conn && iso_pi(parent)->conn->hcon && -	    test_bit(HCI_CONN_PA_SYNC, &iso_pi(parent)->conn->hcon->flags)) { +	/* If listening socket has a hcon, properly disconnect it */ +	if (iso_pi(parent)->conn && iso_pi(parent)->conn->hcon) {  		iso_sock_disconn(parent);  		return;  	} @@ -837,10 +834,10 @@ static struct bt_iso_qos default_qos = {  		.bcode			= {0x00},  		.options		= 0x00,  		.skip			= 0x0000, -		.sync_timeout		= 0x4000, +		.sync_timeout		= BT_ISO_SYNC_TIMEOUT,  		.sync_cte_type		= 0x00,  		.mse			= 0x00, -		.timeout		= 0x4000, +		.timeout		= BT_ISO_SYNC_TIMEOUT,  	},  }; @@ -1076,6 +1073,8 @@ static int iso_listen_bis(struct sock *sk)  {  	struct hci_dev *hdev;  	int err = 0; +	struct iso_conn *conn; +	struct hci_conn *hcon;  	BT_DBG("%pMR -> %pMR (SID 0x%2.2x)", &iso_pi(sk)->src,  	       &iso_pi(sk)->dst, iso_pi(sk)->bc_sid); @@ -1096,18 +1095,40 @@ static int iso_listen_bis(struct sock *sk)  	if (!hdev)  		return -EHOSTUNREACH; +	hci_dev_lock(hdev); +  	/* Fail if user set invalid QoS */  	if (iso_pi(sk)->qos_user_set && !check_bcast_qos(&iso_pi(sk)->qos)) {  		iso_pi(sk)->qos = default_qos; -		return -EINVAL; +		err = -EINVAL; +		goto unlock; +	} + +	hcon = hci_pa_create_sync(hdev, &iso_pi(sk)->dst, +				  le_addr_type(iso_pi(sk)->dst_type), +				  iso_pi(sk)->bc_sid, &iso_pi(sk)->qos); +	if (IS_ERR(hcon)) { +		err = PTR_ERR(hcon); +		goto unlock;  	} -	err = hci_pa_create_sync(hdev, &iso_pi(sk)->dst, -				 le_addr_type(iso_pi(sk)->dst_type), -				 iso_pi(sk)->bc_sid, &iso_pi(sk)->qos); +	conn = iso_conn_add(hcon); +	if (!conn) { +		hci_conn_drop(hcon); +		err = -ENOMEM; +		goto unlock; +	} + +	err = iso_chan_add(conn, sk, NULL); +	if (err) { +		hci_conn_drop(hcon); +		goto unlock; +	}  	hci_dev_put(hdev); +unlock: +	hci_dev_unlock(hdev);  	return err;  } @@ -1889,7 +1910,6 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)  	struct hci_evt_le_big_info_adv_report *ev2;  	struct hci_ev_le_per_adv_report *ev3;  	struct sock *sk; -	int lm = 0;  	bt_dev_dbg(hdev, "bdaddr %pMR", bdaddr); @@ -1933,7 +1953,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)  			if (sk && test_bit(BT_SK_PA_SYNC_TERM,  					   &iso_pi(sk)->flags)) -				return lm; +				return 0;  		}  		if (sk) { @@ -1961,16 +1981,58 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)  	ev3 = hci_recv_event_data(hdev, HCI_EV_LE_PER_ADV_REPORT);  	if (ev3) { -		size_t base_len = ev3->length; +		size_t base_len = 0;  		u8 *base; +		struct hci_conn *hcon;  		sk = iso_get_sock_listen(&hdev->bdaddr, bdaddr,  					 iso_match_sync_handle_pa_report, ev3); -		base = eir_get_service_data(ev3->data, ev3->length, -					    EIR_BAA_SERVICE_UUID, &base_len); -		if (base && sk && base_len <= sizeof(iso_pi(sk)->base)) { +		if (!sk) +			goto done; + +		hcon = iso_pi(sk)->conn->hcon; +		if (!hcon) +			goto done; + +		if (ev3->data_status == LE_PA_DATA_TRUNCATED) { +			/* The controller was unable to retrieve PA data. */ +			memset(hcon->le_per_adv_data, 0, +			       HCI_MAX_PER_AD_TOT_LEN); +			hcon->le_per_adv_data_len = 0; +			hcon->le_per_adv_data_offset = 0; +			goto done; +		} + +		if (hcon->le_per_adv_data_offset + ev3->length > +		    HCI_MAX_PER_AD_TOT_LEN) +			goto done; + +		memcpy(hcon->le_per_adv_data + hcon->le_per_adv_data_offset, +		       ev3->data, ev3->length); +		hcon->le_per_adv_data_offset += ev3->length; + +		if (ev3->data_status == LE_PA_DATA_COMPLETE) { +			/* All PA data has been received. */ +			hcon->le_per_adv_data_len = +				hcon->le_per_adv_data_offset; +			hcon->le_per_adv_data_offset = 0; + +			/* Extract BASE */ +			base = eir_get_service_data(hcon->le_per_adv_data, +						    hcon->le_per_adv_data_len, +						    EIR_BAA_SERVICE_UUID, +						    &base_len); + +			if (!base || base_len > BASE_MAX_LENGTH) +				goto done; +  			memcpy(iso_pi(sk)->base, base, base_len);  			iso_pi(sk)->base_len = base_len; +		} else { +			/* This is a PA data fragment. Keep pa_data_len set to 0 +			 * until all data has been reassembled. +			 */ +			hcon->le_per_adv_data_len = 0;  		}  	} else {  		sk = iso_get_sock_listen(&hdev->bdaddr, BDADDR_ANY, NULL, NULL); @@ -1978,16 +2040,14 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)  done:  	if (!sk) -		return lm; - -	lm |= HCI_LM_ACCEPT; +		return 0;  	if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags))  		*flags |= HCI_PROTO_DEFER;  	sock_put(sk); -	return lm; +	return HCI_LM_ACCEPT;  }  static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)  |