mirror of
https://github.com/torvalds/linux.git
synced 2024-11-21 19:41:42 +00: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
@ -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)
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user