aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuiz Augusto von Dentz <luiz.von.dentz@intel.com>2023-08-04 14:54:09 -0700
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>2023-08-11 11:55:28 -0700
commita091289218202bc09d9b9caa8afcde1018584aec (patch)
tree3b34c23efbaebfe78f1eec0f29a01829f2abdb34
parentf88670161eb205f842989df555d0dd2f9fe2d4b5 (diff)
downloadnf-next-a091289218202bc09d9b9caa8afcde1018584aec.tar.gz
Bluetooth: hci_conn: Fix hci_le_set_cig_params
When running with concurrent task only one CIS was being assigned so this attempts to rework the way the PDU is constructed so it is handled later at the callback instead of in place. Fixes: 26afbd826ee3 ("Bluetooth: Add initial implementation of CIS connections") Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
-rw-r--r--net/bluetooth/hci_conn.c157
1 files changed, 63 insertions, 94 deletions
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 71e5a4c2e5230b..ae206eb551f76a 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -733,7 +733,6 @@ struct iso_list_data {
u16 sync_handle;
};
int count;
- struct iso_cig_params pdu;
bool big_term;
bool big_sync_term;
};
@@ -1703,42 +1702,6 @@ struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
return sco;
}
-static void cis_add(struct iso_list_data *d, struct bt_iso_qos *qos)
-{
- struct hci_cis_params *cis = &d->pdu.cis[d->pdu.cp.num_cis];
-
- cis->cis_id = qos->ucast.cis;
- cis->c_sdu = cpu_to_le16(qos->ucast.out.sdu);
- cis->p_sdu = cpu_to_le16(qos->ucast.in.sdu);
- cis->c_phy = qos->ucast.out.phy ? qos->ucast.out.phy : qos->ucast.in.phy;
- cis->p_phy = qos->ucast.in.phy ? qos->ucast.in.phy : qos->ucast.out.phy;
- cis->c_rtn = qos->ucast.out.rtn;
- cis->p_rtn = qos->ucast.in.rtn;
-
- d->pdu.cp.num_cis++;
-}
-
-static void cis_list(struct hci_conn *conn, void *data)
-{
- struct iso_list_data *d = data;
-
- /* Skip if broadcast/ANY address */
- if (!bacmp(&conn->dst, BDADDR_ANY))
- return;
-
- if (d->cig != conn->iso_qos.ucast.cig || d->cis == BT_ISO_QOS_CIS_UNSET ||
- d->cis != conn->iso_qos.ucast.cis)
- return;
-
- d->count++;
-
- if (d->pdu.cp.cig_id == BT_ISO_QOS_CIG_UNSET ||
- d->count >= ARRAY_SIZE(d->pdu.cis))
- return;
-
- cis_add(d, &conn->iso_qos);
-}
-
static int hci_le_create_big(struct hci_conn *conn, struct bt_iso_qos *qos)
{
struct hci_dev *hdev = conn->hdev;
@@ -1771,25 +1734,62 @@ 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);
}
-static void set_cig_params_complete(struct hci_dev *hdev, void *data, int err)
+static int set_cig_params_sync(struct hci_dev *hdev, void *data)
{
- struct iso_cig_params *pdu = data;
+ u8 cig_id = PTR_ERR(data);
+ struct hci_conn *conn;
+ struct bt_iso_qos *qos;
+ struct iso_cig_params pdu;
+ u8 cis_id;
- bt_dev_dbg(hdev, "");
+ conn = hci_conn_hash_lookup_cig(hdev, cig_id);
+ if (!conn)
+ return 0;
- if (err)
- bt_dev_err(hdev, "Unable to set CIG parameters: %d", err);
+ memset(&pdu, 0, sizeof(pdu));
- kfree(pdu);
-}
+ qos = &conn->iso_qos;
+ pdu.cp.cig_id = cig_id;
+ hci_cpu_to_le24(qos->ucast.out.interval, pdu.cp.c_interval);
+ hci_cpu_to_le24(qos->ucast.in.interval, pdu.cp.p_interval);
+ pdu.cp.sca = qos->ucast.sca;
+ pdu.cp.packing = qos->ucast.packing;
+ pdu.cp.framing = qos->ucast.framing;
+ pdu.cp.c_latency = cpu_to_le16(qos->ucast.out.latency);
+ pdu.cp.p_latency = cpu_to_le16(qos->ucast.in.latency);
-static int set_cig_params_sync(struct hci_dev *hdev, void *data)
-{
- struct iso_cig_params *pdu = data;
- u32 plen;
+ /* Reprogram all CIS(s) with the same CIG, valid range are:
+ * num_cis: 0x00 to 0x1F
+ * cis_id: 0x00 to 0xEF
+ */
+ for (cis_id = 0x00; cis_id < 0xf0 &&
+ pdu.cp.num_cis < ARRAY_SIZE(pdu.cis); cis_id++) {
+ struct hci_cis_params *cis;
+
+ conn = hci_conn_hash_lookup_cis(hdev, NULL, 0, cig_id, cis_id);
+ if (!conn)
+ continue;
- 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,
+ qos = &conn->iso_qos;
+
+ cis = &pdu.cis[pdu.cp.num_cis++];
+ cis->cis_id = cis_id;
+ cis->c_sdu = cpu_to_le16(conn->iso_qos.ucast.out.sdu);
+ cis->p_sdu = cpu_to_le16(conn->iso_qos.ucast.in.sdu);
+ cis->c_phy = qos->ucast.out.phy ? qos->ucast.out.phy :
+ qos->ucast.in.phy;
+ cis->p_phy = qos->ucast.in.phy ? qos->ucast.in.phy :
+ qos->ucast.out.phy;
+ cis->c_rtn = qos->ucast.out.rtn;
+ cis->p_rtn = qos->ucast.in.rtn;
+ }
+
+ if (!pdu.cp.num_cis)
+ return 0;
+
+ return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_CIG_PARAMS,
+ sizeof(pdu.cp) +
+ pdu.cp.num_cis * sizeof(pdu.cis[0]), &pdu,
HCI_CMD_TIMEOUT);
}
@@ -1797,7 +1797,6 @@ static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos)
{
struct hci_dev *hdev = conn->hdev;
struct iso_list_data data;
- struct iso_cig_params *pdu;
memset(&data, 0, sizeof(data));
@@ -1824,61 +1823,31 @@ static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos)
qos->ucast.cig = data.cig;
}
- data.pdu.cp.cig_id = qos->ucast.cig;
- hci_cpu_to_le24(qos->ucast.out.interval, data.pdu.cp.c_interval);
- hci_cpu_to_le24(qos->ucast.in.interval, data.pdu.cp.p_interval);
- data.pdu.cp.sca = qos->ucast.sca;
- data.pdu.cp.packing = qos->ucast.packing;
- data.pdu.cp.framing = qos->ucast.framing;
- data.pdu.cp.c_latency = cpu_to_le16(qos->ucast.out.latency);
- data.pdu.cp.p_latency = cpu_to_le16(qos->ucast.in.latency);
-
if (qos->ucast.cis != BT_ISO_QOS_CIS_UNSET) {
- data.count = 0;
- data.cig = qos->ucast.cig;
- data.cis = qos->ucast.cis;
-
- hci_conn_hash_list_state(hdev, cis_list, ISO_LINK, BT_BOUND,
- &data);
- if (data.count)
+ if (hci_conn_hash_lookup_cis(hdev, NULL, 0, qos->ucast.cig,
+ qos->ucast.cis))
return false;
-
- cis_add(&data, qos);
+ goto done;
}
- /* Reprogram all CIS(s) with the same CIG, valid range are:
- * num_cis: 0x00 to 0x1F
- * cis_id: 0x00 to 0xEF
- */
- for (data.cig = qos->ucast.cig, data.cis = 0x00; data.cis < 0xf0 &&
- data.pdu.cp.num_cis < ARRAY_SIZE(data.pdu.cis); data.cis++) {
- data.count = 0;
-
- hci_conn_hash_list_state(hdev, cis_list, ISO_LINK, BT_BOUND,
- &data);
- if (data.count)
- continue;
-
- /* Allocate a CIS if not set */
- if (qos->ucast.cis == BT_ISO_QOS_CIS_UNSET) {
+ /* Allocate first available CIS if not set */
+ for (data.cig = qos->ucast.cig, data.cis = 0x00; data.cis < 0xf0;
+ data.cis++) {
+ if (!hci_conn_hash_lookup_cis(hdev, NULL, 0, data.cig,
+ data.cis)) {
/* Update CIS */
qos->ucast.cis = data.cis;
- cis_add(&data, qos);
+ break;
}
}
- if (qos->ucast.cis == BT_ISO_QOS_CIS_UNSET || !data.pdu.cp.num_cis)
+ if (qos->ucast.cis == BT_ISO_QOS_CIS_UNSET)
return false;
- pdu = kmemdup(&data.pdu, sizeof(*pdu), GFP_KERNEL);
- if (!pdu)
- return false;
-
- if (hci_cmd_sync_queue(hdev, set_cig_params_sync, pdu,
- set_cig_params_complete) < 0) {
- kfree(pdu);
+done:
+ if (hci_cmd_sync_queue(hdev, set_cig_params_sync,
+ ERR_PTR(qos->ucast.cig), NULL) < 0)
return false;
- }
return true;
}