Commit 0fe8c8d0 authored by Iulia Tanasescu's avatar Iulia Tanasescu Committed by Luiz Augusto von Dentz

Bluetooth: Split bt_iso_qos into dedicated structures

Split bt_iso_qos into dedicated unicast and broadcast
structures and add additional broadcast parameters.

Fixes: eca0ae4a ("Bluetooth: Add initial implementation of BIS connections")
Signed-off-by: default avatarIulia Tanasescu <iulia.tanasescu@nxp.com>
Signed-off-by: default avatarLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
parent af395330
/*
BlueZ - Bluetooth protocol stack for Linux
Copyright (C) 2000-2001 Qualcomm Incorporated
Copyright 2023 NXP
Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
......@@ -171,23 +172,39 @@ struct bt_iso_io_qos {
__u8 rtn;
};
struct bt_iso_qos {
union {
struct bt_iso_ucast_qos {
__u8 cig;
__u8 big;
};
union {
__u8 cis;
__u8 bis;
};
union {
__u8 sca;
__u8 packing;
__u8 framing;
struct bt_iso_io_qos in;
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
......
/*
BlueZ - Bluetooth protocol stack for Linux
Copyright (c) 2000-2001, 2010, Code Aurora Forum. All rights reserved.
Copyright 2023 NXP
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)
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();
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)
continue;
if (handle == c->iso_qos.cig) {
if (handle == c->iso_qos.ucast.cig) {
rcu_read_unlock();
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)
continue;
if (handle == c->iso_qos.big) {
if (handle == c->iso_qos.bcast.big) {
rcu_read_unlock();
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 data_len, __u8 *data);
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,
__u16 sync_handle, __u8 num_bis, __u8 bis[]);
int hci_conn_check_link_mode(struct hci_conn *conn);
......
This diff is collapsed.
/*
BlueZ - Bluetooth protocol stack for Linux
Copyright (c) 2000-2001, 2010, Code Aurora Forum. All rights reserved.
Copyright 2023 NXP
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();
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)
continue;
......@@ -3890,7 +3891,7 @@ static u8 hci_cc_le_setup_iso_path(struct hci_dev *hdev, void *data,
/* Input (Host to Controller) */
case 0x00:
/* 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);
break;
/* 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));
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));
conn->iso_qos.out.interval = le32_to_cpu(interval);
conn->iso_qos.in.latency = le16_to_cpu(ev->interval);
conn->iso_qos.out.latency = le16_to_cpu(ev->interval);
conn->iso_qos.in.sdu = le16_to_cpu(ev->c_mtu);
conn->iso_qos.out.sdu = le16_to_cpu(ev->p_mtu);
conn->iso_qos.in.phy = ev->c_phy;
conn->iso_qos.out.phy = ev->p_phy;
conn->iso_qos.ucast.out.interval = le32_to_cpu(interval);
conn->iso_qos.ucast.in.latency = le16_to_cpu(ev->interval);
conn->iso_qos.ucast.out.latency = le16_to_cpu(ev->interval);
conn->iso_qos.ucast.in.sdu = le16_to_cpu(ev->c_mtu);
conn->iso_qos.ucast.out.sdu = le16_to_cpu(ev->p_mtu);
conn->iso_qos.ucast.in.phy = ev->c_phy;
conn->iso_qos.ucast.out.phy = ev->p_phy;
}
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->iso_qos.cig = ev->cig_id;
cis->iso_qos.cis = ev->cis_id;
cis->iso_qos.ucast.cig = ev->cig_id;
cis->iso_qos.ucast.cis = ev->cis_id;
if (!(flags & HCI_PROTO_DEFER)) {
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->iso_qos.big = ev->handle;
bis->iso_qos.bcast.big = ev->handle;
memset(&interval, 0, sizeof(interval));
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) */
bis->iso_qos.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.latency = le16_to_cpu(ev->interval) * 125 / 100;
bis->iso_qos.bcast.in.sdu = le16_to_cpu(ev->max_pdu);
hci_iso_setup_path(bis);
}
......
......@@ -3,6 +3,7 @@
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2022 Intel Corporation
* Copyright 2023 NXP
*/
#include <linux/module.h>
......@@ -59,11 +60,17 @@ struct iso_pinfo {
__u16 sync_handle;
__u32 flags;
struct bt_iso_qos qos;
bool qos_user_set;
__u8 base_len;
__u8 base[BASE_MAX_LENGTH];
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 ---- */
#define ISO_CONN_TIMEOUT (HZ * 40)
#define ISO_DISCONN_TIMEOUT (HZ * 2)
......@@ -264,8 +271,15 @@ static int iso_connect_bis(struct sock *sk)
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 */
if (!iso_pi(sk)->qos.out.phy) {
if (!iso_pi(sk)->qos.bcast.out.phy) {
err = -EINVAL;
goto unlock;
}
......@@ -336,8 +350,15 @@ static int iso_connect_cis(struct sock *sk)
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 */
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;
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);
if (skb->len > qos->out.sdu)
if (skb->len > qos->ucast.out.sdu)
return -EMSGSIZE;
len = skb->len;
......@@ -680,13 +701,23 @@ static struct proto iso_proto = {
}
static struct bt_iso_qos default_qos = {
.cig = BT_ISO_QOS_CIG_UNSET,
.cis = BT_ISO_QOS_CIS_UNSET,
.sca = 0x00,
.bcast = {
.big = BT_ISO_QOS_BIG_UNSET,
.bis = BT_ISO_QOS_BIS_UNSET,
.sync_interval = 0x00,
.packing = 0x00,
.framing = 0x00,
.in = 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,
......@@ -893,9 +924,15 @@ static int iso_listen_bis(struct sock *sk)
if (!hdev)
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,
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);
......@@ -1154,21 +1191,62 @@ static bool check_io_qos(struct bt_iso_io_qos *qos)
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;
if (qos->packing > 0x01)
if (qos->ucast.packing > 0x01)
return false;
if (qos->framing > 0x01)
if (qos->ucast.framing > 0x01)
return false;
if (!check_io_qos(&qos->in))
if (!check_io_qos(&qos->ucast.in))
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 true;
......@@ -1179,7 +1257,7 @@ static int iso_sock_setsockopt(struct socket *sock, int level, int optname,
{
struct sock *sk = sock->sk;
int len, err = 0;
struct bt_iso_qos qos;
struct bt_iso_qos qos = default_qos;
u32 opt;
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);
if (len != sizeof(qos)) {
err = -EINVAL;
break;
}
memset(&qos, 0, sizeof(qos));
if (copy_from_sockptr(&qos, optval, len)) {
err = -EFAULT;
break;
}
if (!check_qos(&qos)) {
if (len == sizeof(qos.ucast) && !check_ucast_qos(&qos)) {
err = -EINVAL;
break;
}
iso_pi(sk)->qos = qos;
iso_pi(sk)->qos_user_set = true;
break;
......@@ -1419,7 +1492,7 @@ static bool iso_match_big(struct sock *sk, void *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)
......
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