Commit 6b9545dc authored by Pauli Virtanen's avatar Pauli Virtanen Committed by Jakub Kicinski

Bluetooth: ISO: use hci_sync for setting CIG parameters

When reconfiguring CIG after disconnection of the last CIS, LE Remove
CIG shall be sent before LE Set CIG Parameters.  Otherwise, it fails
because CIG is in the inactive state and not configurable (Core v5.3
Vol 6 Part B Sec. 4.5.14.3). This ordering is currently wrong under
suitable timing conditions, because LE Remove CIG is sent via the
hci_sync queue and may be delayed, but Set CIG Parameters is via
hci_send_cmd.

Make the ordering well-defined by sending also Set CIG Parameters via
hci_sync.

Fixes: 26afbd82 ("Bluetooth: Add initial implementation of CIS connections")
Signed-off-by: default avatarPauli Virtanen <pav@iki.fi>
Signed-off-by: default avatarLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 56b7f325
...@@ -775,6 +775,11 @@ static void le_conn_timeout(struct work_struct *work) ...@@ -775,6 +775,11 @@ static void le_conn_timeout(struct work_struct *work)
hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM); hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM);
} }
struct iso_cig_params {
struct hci_cp_le_set_cig_params cp;
struct hci_cis_params cis[0x1f];
};
struct iso_list_data { struct iso_list_data {
union { union {
u8 cig; u8 cig;
...@@ -786,10 +791,7 @@ struct iso_list_data { ...@@ -786,10 +791,7 @@ struct iso_list_data {
u16 sync_handle; u16 sync_handle;
}; };
int count; int count;
struct { struct iso_cig_params pdu;
struct hci_cp_le_set_cig_params cp;
struct hci_cis_params cis[0x11];
} pdu;
}; };
static void bis_list(struct hci_conn *conn, void *data) static void bis_list(struct hci_conn *conn, void *data)
...@@ -1764,10 +1766,33 @@ static int hci_le_create_big(struct hci_conn *conn, struct bt_iso_qos *qos) ...@@ -1764,10 +1766,33 @@ static int hci_le_create_big(struct hci_conn *conn, struct bt_iso_qos *qos)
return hci_send_cmd(hdev, HCI_OP_LE_CREATE_BIG, sizeof(cp), &cp); return hci_send_cmd(hdev, HCI_OP_LE_CREATE_BIG, sizeof(cp), &cp);
} }
static void set_cig_params_complete(struct hci_dev *hdev, void *data, int err)
{
struct iso_cig_params *pdu = data;
bt_dev_dbg(hdev, "");
if (err)
bt_dev_err(hdev, "Unable to set CIG parameters: %d", err);
kfree(pdu);
}
static int set_cig_params_sync(struct hci_dev *hdev, void *data)
{
struct iso_cig_params *pdu = data;
u32 plen;
plen = sizeof(pdu->cp) + pdu->cp.num_cis * sizeof(pdu->cis[0]);
return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_CIG_PARAMS, plen, pdu,
HCI_CMD_TIMEOUT);
}
static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos) static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos)
{ {
struct hci_dev *hdev = conn->hdev; struct hci_dev *hdev = conn->hdev;
struct iso_list_data data; struct iso_list_data data;
struct iso_cig_params *pdu;
memset(&data, 0, sizeof(data)); memset(&data, 0, sizeof(data));
...@@ -1837,12 +1862,18 @@ static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos) ...@@ -1837,12 +1862,18 @@ static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos)
if (qos->ucast.cis == BT_ISO_QOS_CIS_UNSET || !data.pdu.cp.num_cis) if (qos->ucast.cis == BT_ISO_QOS_CIS_UNSET || !data.pdu.cp.num_cis)
return false; return false;
if (hci_send_cmd(hdev, HCI_OP_LE_SET_CIG_PARAMS, pdu = kzalloc(sizeof(*pdu), GFP_KERNEL);
sizeof(data.pdu.cp) + if (!pdu)
(data.pdu.cp.num_cis * sizeof(*data.pdu.cis)),
&data.pdu) < 0)
return false; return false;
memcpy(pdu, &data.pdu, sizeof(*pdu));
if (hci_cmd_sync_queue(hdev, set_cig_params_sync, pdu,
set_cig_params_complete) < 0) {
kfree(pdu);
return false;
}
return true; return true;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment