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 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,23 +172,39 @@ struct bt_iso_io_qos { ...@@ -171,23 +172,39 @@ struct bt_iso_io_qos {
__u8 rtn; __u8 rtn;
}; };
struct bt_iso_qos { struct bt_iso_ucast_qos {
union { __u8 cig;
__u8 cig; __u8 cis;
__u8 big; __u8 sca;
}; __u8 packing;
union { __u8 framing;
__u8 cis; struct bt_iso_io_qos in;
__u8 bis; struct bt_iso_io_qos out;
}; };
union {
__u8 sca; struct bt_iso_bcast_qos {
__u8 sync_interval; __u8 big;
}; __u8 bis;
__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;
__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
......
/* /*
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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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);
......
This diff is collapsed.
/* /*
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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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 @@ ...@@ -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 { ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 = { ...@@ -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,
.packing = 0x00, .sync_interval = 0x00,
.framing = 0x00, .packing = 0x00,
.in = DEFAULT_IO_QOS, .framing = 0x00,
.out = DEFAULT_IO_QOS, .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, static struct sock *iso_sock_alloc(struct net *net, struct socket *sock,
...@@ -893,9 +924,15 @@ static int iso_listen_bis(struct sock *sk) ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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)
......
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