Bluetooth: Split bt_iso_qos into dedicated structures
Split bt_iso_qos into dedicated unicast and broadcast
structures and add additional broadcast parameters.
Fixes: eca0ae4aea
("Bluetooth: Add initial implementation of BIS connections")
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
af395330ab
commit
0fe8c8d071
5 changed files with 237 additions and 135 deletions
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
BlueZ - Bluetooth protocol stack for Linux
|
BlueZ - Bluetooth protocol stack for Linux
|
||||||
Copyright (C) 2000-2001 Qualcomm Incorporated
|
Copyright (C) 2000-2001 Qualcomm Incorporated
|
||||||
|
Copyright 2023 NXP
|
||||||
|
|
||||||
Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
|
Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
|
||||||
|
|
||||||
|
@ -171,25 +172,41 @@ struct bt_iso_io_qos {
|
||||||
__u8 rtn;
|
__u8 rtn;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct bt_iso_qos {
|
struct bt_iso_ucast_qos {
|
||||||
union {
|
|
||||||
__u8 cig;
|
__u8 cig;
|
||||||
__u8 big;
|
|
||||||
};
|
|
||||||
union {
|
|
||||||
__u8 cis;
|
__u8 cis;
|
||||||
__u8 bis;
|
|
||||||
};
|
|
||||||
union {
|
|
||||||
__u8 sca;
|
__u8 sca;
|
||||||
__u8 sync_interval;
|
|
||||||
};
|
|
||||||
__u8 packing;
|
__u8 packing;
|
||||||
__u8 framing;
|
__u8 framing;
|
||||||
struct bt_iso_io_qos in;
|
struct bt_iso_io_qos in;
|
||||||
struct bt_iso_io_qos out;
|
struct bt_iso_io_qos out;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct bt_iso_bcast_qos {
|
||||||
|
__u8 big;
|
||||||
|
__u8 bis;
|
||||||
|
__u8 sync_interval;
|
||||||
|
__u8 packing;
|
||||||
|
__u8 framing;
|
||||||
|
struct bt_iso_io_qos in;
|
||||||
|
struct bt_iso_io_qos out;
|
||||||
|
__u8 encryption;
|
||||||
|
__u8 bcode[16];
|
||||||
|
__u8 options;
|
||||||
|
__u16 skip;
|
||||||
|
__u16 sync_timeout;
|
||||||
|
__u8 sync_cte_type;
|
||||||
|
__u8 mse;
|
||||||
|
__u16 timeout;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bt_iso_qos {
|
||||||
|
union {
|
||||||
|
struct bt_iso_ucast_qos ucast;
|
||||||
|
struct bt_iso_bcast_qos bcast;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
#define BT_ISO_PHY_1M 0x01
|
#define BT_ISO_PHY_1M 0x01
|
||||||
#define BT_ISO_PHY_2M 0x02
|
#define BT_ISO_PHY_2M 0x02
|
||||||
#define BT_ISO_PHY_CODED 0x04
|
#define BT_ISO_PHY_CODED 0x04
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
BlueZ - Bluetooth protocol stack for Linux
|
BlueZ - Bluetooth protocol stack for Linux
|
||||||
Copyright (c) 2000-2001, 2010, Code Aurora Forum. All rights reserved.
|
Copyright (c) 2000-2001, 2010, Code Aurora Forum. All rights reserved.
|
||||||
|
Copyright 2023 NXP
|
||||||
|
|
||||||
Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
|
Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
|
||||||
|
|
||||||
|
@ -1096,7 +1097,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_bis(struct hci_dev *hdev,
|
||||||
if (bacmp(&c->dst, ba) || c->type != ISO_LINK)
|
if (bacmp(&c->dst, ba) || c->type != ISO_LINK)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (c->iso_qos.big == big && c->iso_qos.bis == bis) {
|
if (c->iso_qos.bcast.big == big && c->iso_qos.bcast.bis == bis) {
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
@ -1205,7 +1206,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_cig(struct hci_dev *hdev,
|
||||||
if (c->type != ISO_LINK)
|
if (c->type != ISO_LINK)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (handle == c->iso_qos.cig) {
|
if (handle == c->iso_qos.ucast.cig) {
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
@ -1228,7 +1229,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_big(struct hci_dev *hdev,
|
||||||
if (bacmp(&c->dst, BDADDR_ANY) || c->type != ISO_LINK)
|
if (bacmp(&c->dst, BDADDR_ANY) || c->type != ISO_LINK)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (handle == c->iso_qos.big) {
|
if (handle == c->iso_qos.bcast.big) {
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
@ -1337,7 +1338,7 @@ struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst,
|
||||||
__u8 dst_type, struct bt_iso_qos *qos,
|
__u8 dst_type, struct bt_iso_qos *qos,
|
||||||
__u8 data_len, __u8 *data);
|
__u8 data_len, __u8 *data);
|
||||||
int hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst, __u8 dst_type,
|
int hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst, __u8 dst_type,
|
||||||
__u8 sid);
|
__u8 sid, struct bt_iso_qos *qos);
|
||||||
int hci_le_big_create_sync(struct hci_dev *hdev, struct bt_iso_qos *qos,
|
int hci_le_big_create_sync(struct hci_dev *hdev, struct bt_iso_qos *qos,
|
||||||
__u16 sync_handle, __u8 num_bis, __u8 bis[]);
|
__u16 sync_handle, __u8 num_bis, __u8 bis[]);
|
||||||
int hci_conn_check_link_mode(struct hci_conn *conn);
|
int hci_conn_check_link_mode(struct hci_conn *conn);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
BlueZ - Bluetooth protocol stack for Linux
|
BlueZ - Bluetooth protocol stack for Linux
|
||||||
Copyright (c) 2000-2001, 2010, Code Aurora Forum. All rights reserved.
|
Copyright (c) 2000-2001, 2010, Code Aurora Forum. All rights reserved.
|
||||||
|
Copyright 2023 NXP
|
||||||
|
|
||||||
Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
|
Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
|
||||||
|
|
||||||
|
@ -795,8 +796,8 @@ static void bis_list(struct hci_conn *conn, void *data)
|
||||||
if (bacmp(&conn->dst, BDADDR_ANY))
|
if (bacmp(&conn->dst, BDADDR_ANY))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (d->big != conn->iso_qos.big || d->bis == BT_ISO_QOS_BIS_UNSET ||
|
if (d->big != conn->iso_qos.bcast.big || d->bis == BT_ISO_QOS_BIS_UNSET ||
|
||||||
d->bis != conn->iso_qos.bis)
|
d->bis != conn->iso_qos.bcast.bis)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
d->count++;
|
d->count++;
|
||||||
|
@ -916,10 +917,10 @@ static void bis_cleanup(struct hci_conn *conn)
|
||||||
if (!test_and_clear_bit(HCI_CONN_PER_ADV, &conn->flags))
|
if (!test_and_clear_bit(HCI_CONN_PER_ADV, &conn->flags))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
hci_le_terminate_big(hdev, conn->iso_qos.big,
|
hci_le_terminate_big(hdev, conn->iso_qos.bcast.big,
|
||||||
conn->iso_qos.bis);
|
conn->iso_qos.bcast.bis);
|
||||||
} else {
|
} else {
|
||||||
hci_le_big_terminate(hdev, conn->iso_qos.big,
|
hci_le_big_terminate(hdev, conn->iso_qos.bcast.big,
|
||||||
conn->sync_handle);
|
conn->sync_handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -959,7 +960,7 @@ static void cis_cleanup(struct hci_conn *conn)
|
||||||
struct iso_list_data d;
|
struct iso_list_data d;
|
||||||
|
|
||||||
memset(&d, 0, sizeof(d));
|
memset(&d, 0, sizeof(d));
|
||||||
d.cig = conn->iso_qos.cig;
|
d.cig = conn->iso_qos.ucast.cig;
|
||||||
|
|
||||||
/* Check if ISO connection is a CIS and remove CIG if there are
|
/* Check if ISO connection is a CIS and remove CIG if there are
|
||||||
* no other connections using it.
|
* no other connections using it.
|
||||||
|
@ -968,7 +969,7 @@ static void cis_cleanup(struct hci_conn *conn)
|
||||||
if (d.count)
|
if (d.count)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
hci_le_remove_cig(hdev, conn->iso_qos.cig);
|
hci_le_remove_cig(hdev, conn->iso_qos.ucast.cig);
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
|
@ -1411,7 +1412,7 @@ static int qos_set_big(struct hci_dev *hdev, struct bt_iso_qos *qos)
|
||||||
struct iso_list_data data;
|
struct iso_list_data data;
|
||||||
|
|
||||||
/* Allocate a BIG if not set */
|
/* Allocate a BIG if not set */
|
||||||
if (qos->big == BT_ISO_QOS_BIG_UNSET) {
|
if (qos->bcast.big == BT_ISO_QOS_BIG_UNSET) {
|
||||||
for (data.big = 0x00; data.big < 0xef; data.big++) {
|
for (data.big = 0x00; data.big < 0xef; data.big++) {
|
||||||
data.count = 0;
|
data.count = 0;
|
||||||
data.bis = 0xff;
|
data.bis = 0xff;
|
||||||
|
@ -1426,7 +1427,7 @@ static int qos_set_big(struct hci_dev *hdev, struct bt_iso_qos *qos)
|
||||||
return -EADDRNOTAVAIL;
|
return -EADDRNOTAVAIL;
|
||||||
|
|
||||||
/* Update BIG */
|
/* Update BIG */
|
||||||
qos->big = data.big;
|
qos->bcast.big = data.big;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1437,7 +1438,7 @@ static int qos_set_bis(struct hci_dev *hdev, struct bt_iso_qos *qos)
|
||||||
struct iso_list_data data;
|
struct iso_list_data data;
|
||||||
|
|
||||||
/* Allocate BIS if not set */
|
/* Allocate BIS if not set */
|
||||||
if (qos->bis == BT_ISO_QOS_BIS_UNSET) {
|
if (qos->bcast.bis == BT_ISO_QOS_BIS_UNSET) {
|
||||||
/* Find an unused adv set to advertise BIS, skip instance 0x00
|
/* Find an unused adv set to advertise BIS, skip instance 0x00
|
||||||
* since it is reserved as general purpose set.
|
* since it is reserved as general purpose set.
|
||||||
*/
|
*/
|
||||||
|
@ -1455,7 +1456,7 @@ static int qos_set_bis(struct hci_dev *hdev, struct bt_iso_qos *qos)
|
||||||
return -EADDRNOTAVAIL;
|
return -EADDRNOTAVAIL;
|
||||||
|
|
||||||
/* Update BIS */
|
/* Update BIS */
|
||||||
qos->bis = data.bis;
|
qos->bcast.bis = data.bis;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1484,8 +1485,8 @@ static struct hci_conn *hci_add_bis(struct hci_dev *hdev, bdaddr_t *dst,
|
||||||
if (err)
|
if (err)
|
||||||
return ERR_PTR(err);
|
return ERR_PTR(err);
|
||||||
|
|
||||||
data.big = qos->big;
|
data.big = qos->bcast.big;
|
||||||
data.bis = qos->bis;
|
data.bis = qos->bcast.bis;
|
||||||
data.count = 0;
|
data.count = 0;
|
||||||
|
|
||||||
/* Check if there is already a matching BIG/BIS */
|
/* Check if there is already a matching BIG/BIS */
|
||||||
|
@ -1493,7 +1494,7 @@ static struct hci_conn *hci_add_bis(struct hci_dev *hdev, bdaddr_t *dst,
|
||||||
if (data.count)
|
if (data.count)
|
||||||
return ERR_PTR(-EADDRINUSE);
|
return ERR_PTR(-EADDRINUSE);
|
||||||
|
|
||||||
conn = hci_conn_hash_lookup_bis(hdev, dst, qos->big, qos->bis);
|
conn = hci_conn_hash_lookup_bis(hdev, dst, qos->bcast.big, qos->bcast.bis);
|
||||||
if (conn)
|
if (conn)
|
||||||
return ERR_PTR(-EADDRINUSE);
|
return ERR_PTR(-EADDRINUSE);
|
||||||
|
|
||||||
|
@ -1648,13 +1649,13 @@ 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];
|
struct hci_cis_params *cis = &d->pdu.cis[d->pdu.cp.num_cis];
|
||||||
|
|
||||||
cis->cis_id = qos->cis;
|
cis->cis_id = qos->ucast.cis;
|
||||||
cis->c_sdu = cpu_to_le16(qos->out.sdu);
|
cis->c_sdu = cpu_to_le16(qos->ucast.out.sdu);
|
||||||
cis->p_sdu = cpu_to_le16(qos->in.sdu);
|
cis->p_sdu = cpu_to_le16(qos->ucast.in.sdu);
|
||||||
cis->c_phy = qos->out.phy ? qos->out.phy : qos->in.phy;
|
cis->c_phy = qos->ucast.out.phy ? qos->ucast.out.phy : qos->ucast.in.phy;
|
||||||
cis->p_phy = qos->in.phy ? qos->in.phy : qos->out.phy;
|
cis->p_phy = qos->ucast.in.phy ? qos->ucast.in.phy : qos->ucast.out.phy;
|
||||||
cis->c_rtn = qos->out.rtn;
|
cis->c_rtn = qos->ucast.out.rtn;
|
||||||
cis->p_rtn = qos->in.rtn;
|
cis->p_rtn = qos->ucast.in.rtn;
|
||||||
|
|
||||||
d->pdu.cp.num_cis++;
|
d->pdu.cp.num_cis++;
|
||||||
}
|
}
|
||||||
|
@ -1667,8 +1668,8 @@ static void cis_list(struct hci_conn *conn, void *data)
|
||||||
if (!bacmp(&conn->dst, BDADDR_ANY))
|
if (!bacmp(&conn->dst, BDADDR_ANY))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (d->cig != conn->iso_qos.cig || d->cis == BT_ISO_QOS_CIS_UNSET ||
|
if (d->cig != conn->iso_qos.ucast.cig || d->cis == BT_ISO_QOS_CIS_UNSET ||
|
||||||
d->cis != conn->iso_qos.cis)
|
d->cis != conn->iso_qos.ucast.cis)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
d->count++;
|
d->count++;
|
||||||
|
@ -1687,17 +1688,18 @@ static int hci_le_create_big(struct hci_conn *conn, struct bt_iso_qos *qos)
|
||||||
|
|
||||||
memset(&cp, 0, sizeof(cp));
|
memset(&cp, 0, sizeof(cp));
|
||||||
|
|
||||||
cp.handle = qos->big;
|
cp.handle = qos->bcast.big;
|
||||||
cp.adv_handle = qos->bis;
|
cp.adv_handle = qos->bcast.bis;
|
||||||
cp.num_bis = 0x01;
|
cp.num_bis = 0x01;
|
||||||
hci_cpu_to_le24(qos->out.interval, cp.bis.sdu_interval);
|
hci_cpu_to_le24(qos->bcast.out.interval, cp.bis.sdu_interval);
|
||||||
cp.bis.sdu = cpu_to_le16(qos->out.sdu);
|
cp.bis.sdu = cpu_to_le16(qos->bcast.out.sdu);
|
||||||
cp.bis.latency = cpu_to_le16(qos->out.latency);
|
cp.bis.latency = cpu_to_le16(qos->bcast.out.latency);
|
||||||
cp.bis.rtn = qos->out.rtn;
|
cp.bis.rtn = qos->bcast.out.rtn;
|
||||||
cp.bis.phy = qos->out.phy;
|
cp.bis.phy = qos->bcast.out.phy;
|
||||||
cp.bis.packing = qos->packing;
|
cp.bis.packing = qos->bcast.packing;
|
||||||
cp.bis.framing = qos->framing;
|
cp.bis.framing = qos->bcast.framing;
|
||||||
cp.bis.encryption = 0x00;
|
cp.bis.encryption = qos->bcast.encryption;
|
||||||
|
memcpy(cp.bis.bcode, qos->bcast.bcode, sizeof(cp.bis.bcode));
|
||||||
memset(&cp.bis.bcode, 0, sizeof(cp.bis.bcode));
|
memset(&cp.bis.bcode, 0, sizeof(cp.bis.bcode));
|
||||||
|
|
||||||
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);
|
||||||
|
@ -1711,7 +1713,7 @@ static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos)
|
||||||
memset(&data, 0, sizeof(data));
|
memset(&data, 0, sizeof(data));
|
||||||
|
|
||||||
/* Allocate a CIG if not set */
|
/* Allocate a CIG if not set */
|
||||||
if (qos->cig == BT_ISO_QOS_CIG_UNSET) {
|
if (qos->ucast.cig == BT_ISO_QOS_CIG_UNSET) {
|
||||||
for (data.cig = 0x00; data.cig < 0xff; data.cig++) {
|
for (data.cig = 0x00; data.cig < 0xff; data.cig++) {
|
||||||
data.count = 0;
|
data.count = 0;
|
||||||
data.cis = 0xff;
|
data.cis = 0xff;
|
||||||
|
@ -1731,22 +1733,22 @@ static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* Update CIG */
|
/* Update CIG */
|
||||||
qos->cig = data.cig;
|
qos->ucast.cig = data.cig;
|
||||||
}
|
}
|
||||||
|
|
||||||
data.pdu.cp.cig_id = qos->cig;
|
data.pdu.cp.cig_id = qos->ucast.cig;
|
||||||
hci_cpu_to_le24(qos->out.interval, data.pdu.cp.c_interval);
|
hci_cpu_to_le24(qos->ucast.out.interval, data.pdu.cp.c_interval);
|
||||||
hci_cpu_to_le24(qos->in.interval, data.pdu.cp.p_interval);
|
hci_cpu_to_le24(qos->ucast.in.interval, data.pdu.cp.p_interval);
|
||||||
data.pdu.cp.sca = qos->sca;
|
data.pdu.cp.sca = qos->ucast.sca;
|
||||||
data.pdu.cp.packing = qos->packing;
|
data.pdu.cp.packing = qos->ucast.packing;
|
||||||
data.pdu.cp.framing = qos->framing;
|
data.pdu.cp.framing = qos->ucast.framing;
|
||||||
data.pdu.cp.c_latency = cpu_to_le16(qos->out.latency);
|
data.pdu.cp.c_latency = cpu_to_le16(qos->ucast.out.latency);
|
||||||
data.pdu.cp.p_latency = cpu_to_le16(qos->in.latency);
|
data.pdu.cp.p_latency = cpu_to_le16(qos->ucast.in.latency);
|
||||||
|
|
||||||
if (qos->cis != BT_ISO_QOS_CIS_UNSET) {
|
if (qos->ucast.cis != BT_ISO_QOS_CIS_UNSET) {
|
||||||
data.count = 0;
|
data.count = 0;
|
||||||
data.cig = qos->cig;
|
data.cig = qos->ucast.cig;
|
||||||
data.cis = qos->cis;
|
data.cis = qos->ucast.cis;
|
||||||
|
|
||||||
hci_conn_hash_list_state(hdev, cis_list, ISO_LINK, BT_BOUND,
|
hci_conn_hash_list_state(hdev, cis_list, ISO_LINK, BT_BOUND,
|
||||||
&data);
|
&data);
|
||||||
|
@ -1757,7 +1759,7 @@ static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reprogram all CIS(s) with the same CIG */
|
/* Reprogram all CIS(s) with the same CIG */
|
||||||
for (data.cig = qos->cig, data.cis = 0x00; data.cis < 0x11;
|
for (data.cig = qos->ucast.cig, data.cis = 0x00; data.cis < 0x11;
|
||||||
data.cis++) {
|
data.cis++) {
|
||||||
data.count = 0;
|
data.count = 0;
|
||||||
|
|
||||||
|
@ -1767,14 +1769,14 @@ static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Allocate a CIS if not set */
|
/* Allocate a CIS if not set */
|
||||||
if (qos->cis == BT_ISO_QOS_CIS_UNSET) {
|
if (qos->ucast.cis == BT_ISO_QOS_CIS_UNSET) {
|
||||||
/* Update CIS */
|
/* Update CIS */
|
||||||
qos->cis = data.cis;
|
qos->ucast.cis = data.cis;
|
||||||
cis_add(&data, qos);
|
cis_add(&data, qos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (qos->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,
|
if (hci_send_cmd(hdev, HCI_OP_LE_SET_CIG_PARAMS,
|
||||||
|
@ -1809,32 +1811,32 @@ struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst,
|
||||||
return cis;
|
return cis;
|
||||||
|
|
||||||
/* Update LINK PHYs according to QoS preference */
|
/* Update LINK PHYs according to QoS preference */
|
||||||
cis->le_tx_phy = qos->out.phy;
|
cis->le_tx_phy = qos->ucast.out.phy;
|
||||||
cis->le_rx_phy = qos->in.phy;
|
cis->le_rx_phy = qos->ucast.in.phy;
|
||||||
|
|
||||||
/* If output interval is not set use the input interval as it cannot be
|
/* If output interval is not set use the input interval as it cannot be
|
||||||
* 0x000000.
|
* 0x000000.
|
||||||
*/
|
*/
|
||||||
if (!qos->out.interval)
|
if (!qos->ucast.out.interval)
|
||||||
qos->out.interval = qos->in.interval;
|
qos->ucast.out.interval = qos->ucast.in.interval;
|
||||||
|
|
||||||
/* If input interval is not set use the output interval as it cannot be
|
/* If input interval is not set use the output interval as it cannot be
|
||||||
* 0x000000.
|
* 0x000000.
|
||||||
*/
|
*/
|
||||||
if (!qos->in.interval)
|
if (!qos->ucast.in.interval)
|
||||||
qos->in.interval = qos->out.interval;
|
qos->ucast.in.interval = qos->ucast.out.interval;
|
||||||
|
|
||||||
/* If output latency is not set use the input latency as it cannot be
|
/* If output latency is not set use the input latency as it cannot be
|
||||||
* 0x0000.
|
* 0x0000.
|
||||||
*/
|
*/
|
||||||
if (!qos->out.latency)
|
if (!qos->ucast.out.latency)
|
||||||
qos->out.latency = qos->in.latency;
|
qos->ucast.out.latency = qos->ucast.in.latency;
|
||||||
|
|
||||||
/* If input latency is not set use the output latency as it cannot be
|
/* If input latency is not set use the output latency as it cannot be
|
||||||
* 0x0000.
|
* 0x0000.
|
||||||
*/
|
*/
|
||||||
if (!qos->in.latency)
|
if (!qos->ucast.in.latency)
|
||||||
qos->in.latency = qos->out.latency;
|
qos->ucast.in.latency = qos->ucast.out.latency;
|
||||||
|
|
||||||
if (!hci_le_set_cig_params(cis, qos)) {
|
if (!hci_le_set_cig_params(cis, qos)) {
|
||||||
hci_conn_drop(cis);
|
hci_conn_drop(cis);
|
||||||
|
@ -1854,7 +1856,7 @@ bool hci_iso_setup_path(struct hci_conn *conn)
|
||||||
|
|
||||||
memset(&cmd, 0, sizeof(cmd));
|
memset(&cmd, 0, sizeof(cmd));
|
||||||
|
|
||||||
if (conn->iso_qos.out.sdu) {
|
if (conn->iso_qos.ucast.out.sdu) {
|
||||||
cmd.handle = cpu_to_le16(conn->handle);
|
cmd.handle = cpu_to_le16(conn->handle);
|
||||||
cmd.direction = 0x00; /* Input (Host to Controller) */
|
cmd.direction = 0x00; /* Input (Host to Controller) */
|
||||||
cmd.path = 0x00; /* HCI path if enabled */
|
cmd.path = 0x00; /* HCI path if enabled */
|
||||||
|
@ -1865,7 +1867,7 @@ bool hci_iso_setup_path(struct hci_conn *conn)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conn->iso_qos.in.sdu) {
|
if (conn->iso_qos.ucast.in.sdu) {
|
||||||
cmd.handle = cpu_to_le16(conn->handle);
|
cmd.handle = cpu_to_le16(conn->handle);
|
||||||
cmd.direction = 0x01; /* Output (Controller to Host) */
|
cmd.direction = 0x01; /* Output (Controller to Host) */
|
||||||
cmd.path = 0x00; /* HCI path if enabled */
|
cmd.path = 0x00; /* HCI path if enabled */
|
||||||
|
@ -1892,7 +1894,7 @@ static int hci_create_cis_sync(struct hci_dev *hdev, void *data)
|
||||||
cmd.cis[0].acl_handle = cpu_to_le16(conn->link->handle);
|
cmd.cis[0].acl_handle = cpu_to_le16(conn->link->handle);
|
||||||
cmd.cis[0].cis_handle = cpu_to_le16(conn->handle);
|
cmd.cis[0].cis_handle = cpu_to_le16(conn->handle);
|
||||||
cmd.cp.num_cis++;
|
cmd.cp.num_cis++;
|
||||||
cig = conn->iso_qos.cig;
|
cig = conn->iso_qos.ucast.cig;
|
||||||
|
|
||||||
hci_dev_lock(hdev);
|
hci_dev_lock(hdev);
|
||||||
|
|
||||||
|
@ -1902,7 +1904,7 @@ static int hci_create_cis_sync(struct hci_dev *hdev, void *data)
|
||||||
struct hci_cis *cis = &cmd.cis[cmd.cp.num_cis];
|
struct hci_cis *cis = &cmd.cis[cmd.cp.num_cis];
|
||||||
|
|
||||||
if (conn == data || conn->type != ISO_LINK ||
|
if (conn == data || conn->type != ISO_LINK ||
|
||||||
conn->state == BT_CONNECTED || conn->iso_qos.cig != cig)
|
conn->state == BT_CONNECTED || conn->iso_qos.ucast.cig != cig)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Check if all CIS(s) belonging to a CIG are ready */
|
/* Check if all CIS(s) belonging to a CIG are ready */
|
||||||
|
@ -2002,8 +2004,8 @@ static void hci_bind_bis(struct hci_conn *conn,
|
||||||
struct bt_iso_qos *qos)
|
struct bt_iso_qos *qos)
|
||||||
{
|
{
|
||||||
/* Update LINK PHYs according to QoS preference */
|
/* Update LINK PHYs according to QoS preference */
|
||||||
conn->le_tx_phy = qos->out.phy;
|
conn->le_tx_phy = qos->bcast.out.phy;
|
||||||
conn->le_tx_phy = qos->out.phy;
|
conn->le_tx_phy = qos->bcast.out.phy;
|
||||||
conn->iso_qos = *qos;
|
conn->iso_qos = *qos;
|
||||||
conn->state = BT_BOUND;
|
conn->state = BT_BOUND;
|
||||||
}
|
}
|
||||||
|
@ -2016,16 +2018,16 @@ static int create_big_sync(struct hci_dev *hdev, void *data)
|
||||||
u32 flags = 0;
|
u32 flags = 0;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (qos->out.phy == 0x02)
|
if (qos->bcast.out.phy == 0x02)
|
||||||
flags |= MGMT_ADV_FLAG_SEC_2M;
|
flags |= MGMT_ADV_FLAG_SEC_2M;
|
||||||
|
|
||||||
/* Align intervals */
|
/* Align intervals */
|
||||||
interval = qos->out.interval / 1250;
|
interval = qos->bcast.out.interval / 1250;
|
||||||
|
|
||||||
if (qos->bis)
|
if (qos->bcast.bis)
|
||||||
sync_interval = qos->sync_interval * 1600;
|
sync_interval = qos->bcast.sync_interval * 1600;
|
||||||
|
|
||||||
err = hci_start_per_adv_sync(hdev, qos->bis, conn->le_per_adv_data_len,
|
err = hci_start_per_adv_sync(hdev, qos->bcast.bis, conn->le_per_adv_data_len,
|
||||||
conn->le_per_adv_data, flags, interval,
|
conn->le_per_adv_data, flags, interval,
|
||||||
interval, sync_interval);
|
interval, sync_interval);
|
||||||
if (err)
|
if (err)
|
||||||
|
@ -2062,7 +2064,7 @@ static int create_pa_sync(struct hci_dev *hdev, void *data)
|
||||||
}
|
}
|
||||||
|
|
||||||
int hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst, __u8 dst_type,
|
int hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst, __u8 dst_type,
|
||||||
__u8 sid)
|
__u8 sid, struct bt_iso_qos *qos)
|
||||||
{
|
{
|
||||||
struct hci_cp_le_pa_create_sync *cp;
|
struct hci_cp_le_pa_create_sync *cp;
|
||||||
|
|
||||||
|
@ -2075,9 +2077,13 @@ int hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst, __u8 dst_type,
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cp->options = qos->bcast.options;
|
||||||
cp->sid = sid;
|
cp->sid = sid;
|
||||||
cp->addr_type = dst_type;
|
cp->addr_type = dst_type;
|
||||||
bacpy(&cp->addr, dst);
|
bacpy(&cp->addr, dst);
|
||||||
|
cp->skip = cpu_to_le16(qos->bcast.skip);
|
||||||
|
cp->sync_timeout = cpu_to_le16(qos->bcast.sync_timeout);
|
||||||
|
cp->sync_cte_type = qos->bcast.sync_cte_type;
|
||||||
|
|
||||||
/* Queue start pa_create_sync and scan */
|
/* Queue start pa_create_sync and scan */
|
||||||
return hci_cmd_sync_queue(hdev, create_pa_sync, cp, create_pa_complete);
|
return hci_cmd_sync_queue(hdev, create_pa_sync, cp, create_pa_complete);
|
||||||
|
@ -2100,8 +2106,12 @@ int hci_le_big_create_sync(struct hci_dev *hdev, struct bt_iso_qos *qos,
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
memset(&pdu, 0, sizeof(pdu));
|
memset(&pdu, 0, sizeof(pdu));
|
||||||
pdu.cp.handle = qos->big;
|
pdu.cp.handle = qos->bcast.big;
|
||||||
pdu.cp.sync_handle = cpu_to_le16(sync_handle);
|
pdu.cp.sync_handle = cpu_to_le16(sync_handle);
|
||||||
|
pdu.cp.encryption = qos->bcast.encryption;
|
||||||
|
memcpy(pdu.cp.bcode, qos->bcast.bcode, sizeof(pdu.cp.bcode));
|
||||||
|
pdu.cp.mse = qos->bcast.mse;
|
||||||
|
pdu.cp.timeout = cpu_to_le16(qos->bcast.timeout);
|
||||||
pdu.cp.num_bis = num_bis;
|
pdu.cp.num_bis = num_bis;
|
||||||
memcpy(pdu.bis, bis, num_bis);
|
memcpy(pdu.bis, bis, num_bis);
|
||||||
|
|
||||||
|
@ -2151,7 +2161,7 @@ struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst,
|
||||||
return ERR_PTR(err);
|
return ERR_PTR(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
hci_iso_qos_setup(hdev, conn, &qos->out,
|
hci_iso_qos_setup(hdev, conn, &qos->bcast.out,
|
||||||
conn->le_tx_phy ? conn->le_tx_phy :
|
conn->le_tx_phy ? conn->le_tx_phy :
|
||||||
hdev->le_tx_def_phys);
|
hdev->le_tx_def_phys);
|
||||||
|
|
||||||
|
@ -2177,9 +2187,9 @@ struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst,
|
||||||
if (IS_ERR(le))
|
if (IS_ERR(le))
|
||||||
return le;
|
return le;
|
||||||
|
|
||||||
hci_iso_qos_setup(hdev, le, &qos->out,
|
hci_iso_qos_setup(hdev, le, &qos->ucast.out,
|
||||||
le->le_tx_phy ? le->le_tx_phy : hdev->le_tx_def_phys);
|
le->le_tx_phy ? le->le_tx_phy : hdev->le_tx_def_phys);
|
||||||
hci_iso_qos_setup(hdev, le, &qos->in,
|
hci_iso_qos_setup(hdev, le, &qos->ucast.in,
|
||||||
le->le_rx_phy ? le->le_rx_phy : hdev->le_rx_def_phys);
|
le->le_rx_phy ? le->le_rx_phy : hdev->le_rx_def_phys);
|
||||||
|
|
||||||
cis = hci_bind_cis(hdev, dst, dst_type, qos);
|
cis = hci_bind_cis(hdev, dst, dst_type, qos);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
BlueZ - Bluetooth protocol stack for Linux
|
BlueZ - Bluetooth protocol stack for Linux
|
||||||
Copyright (c) 2000-2001, 2010, Code Aurora Forum. All rights reserved.
|
Copyright (c) 2000-2001, 2010, Code Aurora Forum. All rights reserved.
|
||||||
|
Copyright 2023 NXP
|
||||||
|
|
||||||
Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
|
Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
|
||||||
|
|
||||||
|
@ -3833,7 +3834,7 @@ static u8 hci_cc_le_set_cig_params(struct hci_dev *hdev, void *data,
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
|
|
||||||
list_for_each_entry_rcu(conn, &hdev->conn_hash.list, list) {
|
list_for_each_entry_rcu(conn, &hdev->conn_hash.list, list) {
|
||||||
if (conn->type != ISO_LINK || conn->iso_qos.cig != rp->cig_id ||
|
if (conn->type != ISO_LINK || conn->iso_qos.ucast.cig != rp->cig_id ||
|
||||||
conn->state == BT_CONNECTED)
|
conn->state == BT_CONNECTED)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -3890,7 +3891,7 @@ static u8 hci_cc_le_setup_iso_path(struct hci_dev *hdev, void *data,
|
||||||
/* Input (Host to Controller) */
|
/* Input (Host to Controller) */
|
||||||
case 0x00:
|
case 0x00:
|
||||||
/* Only confirm connection if output only */
|
/* Only confirm connection if output only */
|
||||||
if (conn->iso_qos.out.sdu && !conn->iso_qos.in.sdu)
|
if (conn->iso_qos.ucast.out.sdu && !conn->iso_qos.ucast.in.sdu)
|
||||||
hci_connect_cfm(conn, rp->status);
|
hci_connect_cfm(conn, rp->status);
|
||||||
break;
|
break;
|
||||||
/* Output (Controller to Host) */
|
/* Output (Controller to Host) */
|
||||||
|
@ -6818,15 +6819,15 @@ static void hci_le_cis_estabilished_evt(struct hci_dev *hdev, void *data,
|
||||||
memset(&interval, 0, sizeof(interval));
|
memset(&interval, 0, sizeof(interval));
|
||||||
|
|
||||||
memcpy(&interval, ev->c_latency, sizeof(ev->c_latency));
|
memcpy(&interval, ev->c_latency, sizeof(ev->c_latency));
|
||||||
conn->iso_qos.in.interval = le32_to_cpu(interval);
|
conn->iso_qos.ucast.in.interval = le32_to_cpu(interval);
|
||||||
memcpy(&interval, ev->p_latency, sizeof(ev->p_latency));
|
memcpy(&interval, ev->p_latency, sizeof(ev->p_latency));
|
||||||
conn->iso_qos.out.interval = le32_to_cpu(interval);
|
conn->iso_qos.ucast.out.interval = le32_to_cpu(interval);
|
||||||
conn->iso_qos.in.latency = le16_to_cpu(ev->interval);
|
conn->iso_qos.ucast.in.latency = le16_to_cpu(ev->interval);
|
||||||
conn->iso_qos.out.latency = le16_to_cpu(ev->interval);
|
conn->iso_qos.ucast.out.latency = le16_to_cpu(ev->interval);
|
||||||
conn->iso_qos.in.sdu = le16_to_cpu(ev->c_mtu);
|
conn->iso_qos.ucast.in.sdu = le16_to_cpu(ev->c_mtu);
|
||||||
conn->iso_qos.out.sdu = le16_to_cpu(ev->p_mtu);
|
conn->iso_qos.ucast.out.sdu = le16_to_cpu(ev->p_mtu);
|
||||||
conn->iso_qos.in.phy = ev->c_phy;
|
conn->iso_qos.ucast.in.phy = ev->c_phy;
|
||||||
conn->iso_qos.out.phy = ev->p_phy;
|
conn->iso_qos.ucast.out.phy = ev->p_phy;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ev->status) {
|
if (!ev->status) {
|
||||||
|
@ -6900,8 +6901,8 @@ static void hci_le_cis_req_evt(struct hci_dev *hdev, void *data,
|
||||||
cis->handle = cis_handle;
|
cis->handle = cis_handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
cis->iso_qos.cig = ev->cig_id;
|
cis->iso_qos.ucast.cig = ev->cig_id;
|
||||||
cis->iso_qos.cis = ev->cis_id;
|
cis->iso_qos.ucast.cis = ev->cis_id;
|
||||||
|
|
||||||
if (!(flags & HCI_PROTO_DEFER)) {
|
if (!(flags & HCI_PROTO_DEFER)) {
|
||||||
hci_le_accept_cis(hdev, ev->cis_handle);
|
hci_le_accept_cis(hdev, ev->cis_handle);
|
||||||
|
@ -6988,13 +6989,13 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
|
||||||
bis->handle = handle;
|
bis->handle = handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
bis->iso_qos.big = ev->handle;
|
bis->iso_qos.bcast.big = ev->handle;
|
||||||
memset(&interval, 0, sizeof(interval));
|
memset(&interval, 0, sizeof(interval));
|
||||||
memcpy(&interval, ev->latency, sizeof(ev->latency));
|
memcpy(&interval, ev->latency, sizeof(ev->latency));
|
||||||
bis->iso_qos.in.interval = le32_to_cpu(interval);
|
bis->iso_qos.bcast.in.interval = le32_to_cpu(interval);
|
||||||
/* Convert ISO Interval (1.25 ms slots) to latency (ms) */
|
/* Convert ISO Interval (1.25 ms slots) to latency (ms) */
|
||||||
bis->iso_qos.in.latency = le16_to_cpu(ev->interval) * 125 / 100;
|
bis->iso_qos.bcast.in.latency = le16_to_cpu(ev->interval) * 125 / 100;
|
||||||
bis->iso_qos.in.sdu = le16_to_cpu(ev->max_pdu);
|
bis->iso_qos.bcast.in.sdu = le16_to_cpu(ev->max_pdu);
|
||||||
|
|
||||||
hci_iso_setup_path(bis);
|
hci_iso_setup_path(bis);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
* BlueZ - Bluetooth protocol stack for Linux
|
* BlueZ - Bluetooth protocol stack for Linux
|
||||||
*
|
*
|
||||||
* Copyright (C) 2022 Intel Corporation
|
* Copyright (C) 2022 Intel Corporation
|
||||||
|
* Copyright 2023 NXP
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
@ -59,11 +60,17 @@ struct iso_pinfo {
|
||||||
__u16 sync_handle;
|
__u16 sync_handle;
|
||||||
__u32 flags;
|
__u32 flags;
|
||||||
struct bt_iso_qos qos;
|
struct bt_iso_qos qos;
|
||||||
|
bool qos_user_set;
|
||||||
__u8 base_len;
|
__u8 base_len;
|
||||||
__u8 base[BASE_MAX_LENGTH];
|
__u8 base[BASE_MAX_LENGTH];
|
||||||
struct iso_conn *conn;
|
struct iso_conn *conn;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct bt_iso_qos default_qos;
|
||||||
|
|
||||||
|
static bool check_ucast_qos(struct bt_iso_qos *qos);
|
||||||
|
static bool check_bcast_qos(struct bt_iso_qos *qos);
|
||||||
|
|
||||||
/* ---- ISO timers ---- */
|
/* ---- ISO timers ---- */
|
||||||
#define ISO_CONN_TIMEOUT (HZ * 40)
|
#define ISO_CONN_TIMEOUT (HZ * 40)
|
||||||
#define ISO_DISCONN_TIMEOUT (HZ * 2)
|
#define ISO_DISCONN_TIMEOUT (HZ * 2)
|
||||||
|
@ -264,8 +271,15 @@ static int iso_connect_bis(struct sock *sk)
|
||||||
goto unlock;
|
goto unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
err = -EINVAL;
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
/* Fail if out PHYs are marked as disabled */
|
/* Fail if out PHYs are marked as disabled */
|
||||||
if (!iso_pi(sk)->qos.out.phy) {
|
if (!iso_pi(sk)->qos.bcast.out.phy) {
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
goto unlock;
|
goto unlock;
|
||||||
}
|
}
|
||||||
|
@ -336,8 +350,15 @@ static int iso_connect_cis(struct sock *sk)
|
||||||
goto unlock;
|
goto unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Fail if user set invalid QoS */
|
||||||
|
if (iso_pi(sk)->qos_user_set && !check_ucast_qos(&iso_pi(sk)->qos)) {
|
||||||
|
iso_pi(sk)->qos = default_qos;
|
||||||
|
err = -EINVAL;
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
/* Fail if either PHYs are marked as disabled */
|
/* Fail if either PHYs are marked as disabled */
|
||||||
if (!iso_pi(sk)->qos.in.phy && !iso_pi(sk)->qos.out.phy) {
|
if (!iso_pi(sk)->qos.ucast.in.phy && !iso_pi(sk)->qos.ucast.out.phy) {
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
goto unlock;
|
goto unlock;
|
||||||
}
|
}
|
||||||
|
@ -417,7 +438,7 @@ static int iso_send_frame(struct sock *sk, struct sk_buff *skb)
|
||||||
|
|
||||||
BT_DBG("sk %p len %d", sk, skb->len);
|
BT_DBG("sk %p len %d", sk, skb->len);
|
||||||
|
|
||||||
if (skb->len > qos->out.sdu)
|
if (skb->len > qos->ucast.out.sdu)
|
||||||
return -EMSGSIZE;
|
return -EMSGSIZE;
|
||||||
|
|
||||||
len = skb->len;
|
len = skb->len;
|
||||||
|
@ -680,13 +701,23 @@ static struct proto iso_proto = {
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct bt_iso_qos default_qos = {
|
static struct bt_iso_qos default_qos = {
|
||||||
.cig = BT_ISO_QOS_CIG_UNSET,
|
.bcast = {
|
||||||
.cis = BT_ISO_QOS_CIS_UNSET,
|
.big = BT_ISO_QOS_BIG_UNSET,
|
||||||
.sca = 0x00,
|
.bis = BT_ISO_QOS_BIS_UNSET,
|
||||||
|
.sync_interval = 0x00,
|
||||||
.packing = 0x00,
|
.packing = 0x00,
|
||||||
.framing = 0x00,
|
.framing = 0x00,
|
||||||
.in = DEFAULT_IO_QOS,
|
.in = DEFAULT_IO_QOS,
|
||||||
.out = DEFAULT_IO_QOS,
|
.out = DEFAULT_IO_QOS,
|
||||||
|
.encryption = 0x00,
|
||||||
|
.bcode = {0x00},
|
||||||
|
.options = 0x00,
|
||||||
|
.skip = 0x0000,
|
||||||
|
.sync_timeout = 0x4000,
|
||||||
|
.sync_cte_type = 0x00,
|
||||||
|
.mse = 0x00,
|
||||||
|
.timeout = 0x4000,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct sock *iso_sock_alloc(struct net *net, struct socket *sock,
|
static struct sock *iso_sock_alloc(struct net *net, struct socket *sock,
|
||||||
|
@ -893,9 +924,15 @@ static int iso_listen_bis(struct sock *sk)
|
||||||
if (!hdev)
|
if (!hdev)
|
||||||
return -EHOSTUNREACH;
|
return -EHOSTUNREACH;
|
||||||
|
|
||||||
|
/* 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 = hci_pa_create_sync(hdev, &iso_pi(sk)->dst,
|
err = hci_pa_create_sync(hdev, &iso_pi(sk)->dst,
|
||||||
le_addr_type(iso_pi(sk)->dst_type),
|
le_addr_type(iso_pi(sk)->dst_type),
|
||||||
iso_pi(sk)->bc_sid);
|
iso_pi(sk)->bc_sid, &iso_pi(sk)->qos);
|
||||||
|
|
||||||
hci_dev_put(hdev);
|
hci_dev_put(hdev);
|
||||||
|
|
||||||
|
@ -1154,21 +1191,62 @@ static bool check_io_qos(struct bt_iso_io_qos *qos)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool check_qos(struct bt_iso_qos *qos)
|
static bool check_ucast_qos(struct bt_iso_qos *qos)
|
||||||
{
|
{
|
||||||
if (qos->sca > 0x07)
|
if (qos->ucast.sca > 0x07)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (qos->packing > 0x01)
|
if (qos->ucast.packing > 0x01)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (qos->framing > 0x01)
|
if (qos->ucast.framing > 0x01)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!check_io_qos(&qos->in))
|
if (!check_io_qos(&qos->ucast.in))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!check_io_qos(&qos->out))
|
if (!check_io_qos(&qos->ucast.out))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool check_bcast_qos(struct bt_iso_qos *qos)
|
||||||
|
{
|
||||||
|
if (qos->bcast.sync_interval > 0x07)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (qos->bcast.packing > 0x01)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (qos->bcast.framing > 0x01)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!check_io_qos(&qos->bcast.in))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!check_io_qos(&qos->bcast.out))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (qos->bcast.encryption > 0x01)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (qos->bcast.options > 0x07)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (qos->bcast.skip > 0x01f3)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (qos->bcast.sync_timeout < 0x000a || qos->bcast.sync_timeout > 0x4000)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (qos->bcast.sync_cte_type > 0x1f)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (qos->bcast.mse > 0x1f)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (qos->bcast.timeout < 0x000a || qos->bcast.timeout > 0x4000)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -1179,7 +1257,7 @@ static int iso_sock_setsockopt(struct socket *sock, int level, int optname,
|
||||||
{
|
{
|
||||||
struct sock *sk = sock->sk;
|
struct sock *sk = sock->sk;
|
||||||
int len, err = 0;
|
int len, err = 0;
|
||||||
struct bt_iso_qos qos;
|
struct bt_iso_qos qos = default_qos;
|
||||||
u32 opt;
|
u32 opt;
|
||||||
|
|
||||||
BT_DBG("sk %p", sk);
|
BT_DBG("sk %p", sk);
|
||||||
|
@ -1212,24 +1290,19 @@ static int iso_sock_setsockopt(struct socket *sock, int level, int optname,
|
||||||
}
|
}
|
||||||
|
|
||||||
len = min_t(unsigned int, sizeof(qos), optlen);
|
len = min_t(unsigned int, sizeof(qos), optlen);
|
||||||
if (len != sizeof(qos)) {
|
|
||||||
err = -EINVAL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(&qos, 0, sizeof(qos));
|
|
||||||
|
|
||||||
if (copy_from_sockptr(&qos, optval, len)) {
|
if (copy_from_sockptr(&qos, optval, len)) {
|
||||||
err = -EFAULT;
|
err = -EFAULT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!check_qos(&qos)) {
|
if (len == sizeof(qos.ucast) && !check_ucast_qos(&qos)) {
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
iso_pi(sk)->qos = qos;
|
iso_pi(sk)->qos = qos;
|
||||||
|
iso_pi(sk)->qos_user_set = true;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1419,7 +1492,7 @@ static bool iso_match_big(struct sock *sk, void *data)
|
||||||
{
|
{
|
||||||
struct hci_evt_le_big_sync_estabilished *ev = data;
|
struct hci_evt_le_big_sync_estabilished *ev = data;
|
||||||
|
|
||||||
return ev->handle == iso_pi(sk)->qos.big;
|
return ev->handle == iso_pi(sk)->qos.bcast.big;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void iso_conn_ready(struct iso_conn *conn)
|
static void iso_conn_ready(struct iso_conn *conn)
|
||||||
|
|
Loading…
Add table
Reference in a new issue