Bluetooth: ISO: Add broadcast support
This adds broadcast support for BTPROTO_ISO by extending the sockaddr_iso with a new struct sockaddr_iso_bc where the socket user can set the broadcast address when receiving, the SID and the BIS indexes it wants to synchronize. When using BTPROTO_ISO for broadcast the roles are: Broadcaster -> uses connect with address set to BDADDR_ANY: > tools/isotest -s 00:00:00:00:00:00 Broadcast Receiver -> uses listen with address set to broadcaster: > tools/isotest -d 00:AA:01:00:00:00 Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This commit is contained in:
parent
eca0ae4aea
commit
f764a6c2c1
@ -220,6 +220,8 @@ struct bt_codecs {
|
|||||||
#define BT_CODEC_TRANSPARENT 0x03
|
#define BT_CODEC_TRANSPARENT 0x03
|
||||||
#define BT_CODEC_MSBC 0x05
|
#define BT_CODEC_MSBC 0x05
|
||||||
|
|
||||||
|
#define BT_ISO_BASE 20
|
||||||
|
|
||||||
__printf(1, 2)
|
__printf(1, 2)
|
||||||
void bt_info(const char *fmt, ...);
|
void bt_info(const char *fmt, ...);
|
||||||
__printf(1, 2)
|
__printf(1, 2)
|
||||||
|
@ -10,12 +10,23 @@
|
|||||||
|
|
||||||
/* ISO defaults */
|
/* ISO defaults */
|
||||||
#define ISO_DEFAULT_MTU 251
|
#define ISO_DEFAULT_MTU 251
|
||||||
|
#define ISO_MAX_NUM_BIS 0x1f
|
||||||
|
|
||||||
|
/* ISO socket broadcast address */
|
||||||
|
struct sockaddr_iso_bc {
|
||||||
|
bdaddr_t bc_bdaddr;
|
||||||
|
__u8 bc_bdaddr_type;
|
||||||
|
__u8 bc_sid;
|
||||||
|
__u8 bc_num_bis;
|
||||||
|
__u8 bc_bis[ISO_MAX_NUM_BIS];
|
||||||
|
};
|
||||||
|
|
||||||
/* ISO socket address */
|
/* ISO socket address */
|
||||||
struct sockaddr_iso {
|
struct sockaddr_iso {
|
||||||
sa_family_t iso_family;
|
sa_family_t iso_family;
|
||||||
bdaddr_t iso_bdaddr;
|
bdaddr_t iso_bdaddr;
|
||||||
__u8 iso_bdaddr_type;
|
__u8 iso_bdaddr_type;
|
||||||
|
struct sockaddr_iso_bc iso_bc[];
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* __ISO_H */
|
#endif /* __ISO_H */
|
||||||
|
@ -50,8 +50,14 @@ struct iso_pinfo {
|
|||||||
__u8 src_type;
|
__u8 src_type;
|
||||||
bdaddr_t dst;
|
bdaddr_t dst;
|
||||||
__u8 dst_type;
|
__u8 dst_type;
|
||||||
|
__u8 bc_sid;
|
||||||
|
__u8 bc_num_bis;
|
||||||
|
__u8 bc_bis[ISO_MAX_NUM_BIS];
|
||||||
|
__u16 sync_handle;
|
||||||
__u32 flags;
|
__u32 flags;
|
||||||
struct bt_iso_qos qos;
|
struct bt_iso_qos qos;
|
||||||
|
__u8 base_len;
|
||||||
|
__u8 base[HCI_MAX_PER_AD_LENGTH];
|
||||||
struct iso_conn *conn;
|
struct iso_conn *conn;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -130,6 +136,7 @@ static struct iso_conn *iso_conn_add(struct hci_conn *hcon)
|
|||||||
static void iso_chan_del(struct sock *sk, int err)
|
static void iso_chan_del(struct sock *sk, int err)
|
||||||
{
|
{
|
||||||
struct iso_conn *conn;
|
struct iso_conn *conn;
|
||||||
|
struct sock *parent;
|
||||||
|
|
||||||
conn = iso_pi(sk)->conn;
|
conn = iso_pi(sk)->conn;
|
||||||
|
|
||||||
@ -147,7 +154,14 @@ static void iso_chan_del(struct sock *sk, int err)
|
|||||||
|
|
||||||
sk->sk_state = BT_CLOSED;
|
sk->sk_state = BT_CLOSED;
|
||||||
sk->sk_err = err;
|
sk->sk_err = err;
|
||||||
sk->sk_state_change(sk);
|
|
||||||
|
parent = bt_sk(sk)->parent;
|
||||||
|
if (parent) {
|
||||||
|
bt_accept_unlink(sk);
|
||||||
|
parent->sk_data_ready(parent);
|
||||||
|
} else {
|
||||||
|
sk->sk_state_change(sk);
|
||||||
|
}
|
||||||
|
|
||||||
sock_set_flag(sk, SOCK_ZAPPED);
|
sock_set_flag(sk, SOCK_ZAPPED);
|
||||||
}
|
}
|
||||||
@ -218,7 +232,70 @@ static int iso_chan_add(struct iso_conn *conn, struct sock *sk,
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int iso_connect(struct sock *sk)
|
static int iso_connect_bis(struct sock *sk)
|
||||||
|
{
|
||||||
|
struct iso_conn *conn;
|
||||||
|
struct hci_conn *hcon;
|
||||||
|
struct hci_dev *hdev;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
BT_DBG("%pMR", &iso_pi(sk)->src);
|
||||||
|
|
||||||
|
hdev = hci_get_route(&iso_pi(sk)->dst, &iso_pi(sk)->src,
|
||||||
|
iso_pi(sk)->src_type);
|
||||||
|
if (!hdev)
|
||||||
|
return -EHOSTUNREACH;
|
||||||
|
|
||||||
|
hci_dev_lock(hdev);
|
||||||
|
|
||||||
|
if (!bis_capable(hdev)) {
|
||||||
|
err = -EOPNOTSUPP;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fail if out PHYs are marked as disabled */
|
||||||
|
if (!iso_pi(sk)->qos.out.phy) {
|
||||||
|
err = -EINVAL;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
hcon = hci_connect_bis(hdev, &iso_pi(sk)->dst, iso_pi(sk)->dst_type,
|
||||||
|
&iso_pi(sk)->qos, iso_pi(sk)->base_len,
|
||||||
|
iso_pi(sk)->base);
|
||||||
|
if (IS_ERR(hcon)) {
|
||||||
|
err = PTR_ERR(hcon);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
conn = iso_conn_add(hcon);
|
||||||
|
if (!conn) {
|
||||||
|
hci_conn_drop(hcon);
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update source addr of the socket */
|
||||||
|
bacpy(&iso_pi(sk)->src, &hcon->src);
|
||||||
|
|
||||||
|
err = iso_chan_add(conn, sk, NULL);
|
||||||
|
if (err)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
if (hcon->state == BT_CONNECTED) {
|
||||||
|
iso_sock_clear_timer(sk);
|
||||||
|
sk->sk_state = BT_CONNECTED;
|
||||||
|
} else {
|
||||||
|
sk->sk_state = BT_CONNECT;
|
||||||
|
iso_sock_set_timer(sk, sk->sk_sndtimeo);
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
hci_dev_unlock(hdev);
|
||||||
|
hci_dev_put(hdev);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int iso_connect_cis(struct sock *sk)
|
||||||
{
|
{
|
||||||
struct iso_conn *conn;
|
struct iso_conn *conn;
|
||||||
struct hci_conn *hcon;
|
struct hci_conn *hcon;
|
||||||
@ -359,10 +436,39 @@ static struct sock *__iso_get_sock_listen_by_addr(bdaddr_t *ba)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Find socket listening on source bdaddr.
|
static struct sock *__iso_get_sock_listen_by_sid(bdaddr_t *ba, bdaddr_t *bc,
|
||||||
|
__u8 sid)
|
||||||
|
{
|
||||||
|
struct sock *sk;
|
||||||
|
|
||||||
|
sk_for_each(sk, &iso_sk_list.head) {
|
||||||
|
if (sk->sk_state != BT_LISTEN)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (bacmp(&iso_pi(sk)->src, ba))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (bacmp(&iso_pi(sk)->dst, bc))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (iso_pi(sk)->bc_sid == sid)
|
||||||
|
return sk;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef bool (*iso_sock_match_t)(struct sock *sk, void *data);
|
||||||
|
|
||||||
|
/* Find socket listening:
|
||||||
|
* source bdaddr (Unicast)
|
||||||
|
* destination bdaddr (Broadcast only)
|
||||||
|
* match func - pass NULL to ignore
|
||||||
|
* match func data - pass -1 to ignore
|
||||||
* Returns closest match.
|
* Returns closest match.
|
||||||
*/
|
*/
|
||||||
static struct sock *iso_get_sock_listen(bdaddr_t *src)
|
static struct sock *iso_get_sock_listen(bdaddr_t *src, bdaddr_t *dst,
|
||||||
|
iso_sock_match_t match, void *data)
|
||||||
{
|
{
|
||||||
struct sock *sk = NULL, *sk1 = NULL;
|
struct sock *sk = NULL, *sk1 = NULL;
|
||||||
|
|
||||||
@ -372,6 +478,14 @@ static struct sock *iso_get_sock_listen(bdaddr_t *src)
|
|||||||
if (sk->sk_state != BT_LISTEN)
|
if (sk->sk_state != BT_LISTEN)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
/* Match Broadcast destination */
|
||||||
|
if (bacmp(dst, BDADDR_ANY) && bacmp(&iso_pi(sk)->dst, dst))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Use Match function if provided */
|
||||||
|
if (match && !match(sk, data))
|
||||||
|
continue;
|
||||||
|
|
||||||
/* Exact match. */
|
/* Exact match. */
|
||||||
if (!bacmp(&iso_pi(sk)->src, src))
|
if (!bacmp(&iso_pi(sk)->src, src))
|
||||||
break;
|
break;
|
||||||
@ -587,6 +701,38 @@ static int iso_sock_create(struct net *net, struct socket *sock, int protocol,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int iso_sock_bind_bc(struct socket *sock, struct sockaddr *addr,
|
||||||
|
int addr_len)
|
||||||
|
{
|
||||||
|
struct sockaddr_iso *sa = (struct sockaddr_iso *)addr;
|
||||||
|
struct sock *sk = sock->sk;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
BT_DBG("sk %p bc_sid %u bc_num_bis %u", sk, sa->iso_bc->bc_sid,
|
||||||
|
sa->iso_bc->bc_num_bis);
|
||||||
|
|
||||||
|
if (addr_len > sizeof(*sa) + sizeof(*sa->iso_bc) ||
|
||||||
|
sa->iso_bc->bc_num_bis < 0x01 || sa->iso_bc->bc_num_bis > 0x1f)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
bacpy(&iso_pi(sk)->dst, &sa->iso_bc->bc_bdaddr);
|
||||||
|
iso_pi(sk)->dst_type = sa->iso_bc->bc_bdaddr_type;
|
||||||
|
iso_pi(sk)->sync_handle = -1;
|
||||||
|
iso_pi(sk)->bc_sid = sa->iso_bc->bc_sid;
|
||||||
|
iso_pi(sk)->bc_num_bis = sa->iso_bc->bc_num_bis;
|
||||||
|
|
||||||
|
for (i = 0; i < iso_pi(sk)->bc_num_bis; i++) {
|
||||||
|
if (sa->iso_bc->bc_bis[i] < 0x01 ||
|
||||||
|
sa->iso_bc->bc_bis[i] > 0x1f)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
memcpy(iso_pi(sk)->bc_bis, sa->iso_bc->bc_bis,
|
||||||
|
iso_pi(sk)->bc_num_bis);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int iso_sock_bind(struct socket *sock, struct sockaddr *addr,
|
static int iso_sock_bind(struct socket *sock, struct sockaddr *addr,
|
||||||
int addr_len)
|
int addr_len)
|
||||||
{
|
{
|
||||||
@ -621,6 +767,13 @@ static int iso_sock_bind(struct socket *sock, struct sockaddr *addr,
|
|||||||
bacpy(&iso_pi(sk)->src, &sa->iso_bdaddr);
|
bacpy(&iso_pi(sk)->src, &sa->iso_bdaddr);
|
||||||
iso_pi(sk)->src_type = sa->iso_bdaddr_type;
|
iso_pi(sk)->src_type = sa->iso_bdaddr_type;
|
||||||
|
|
||||||
|
/* Check for Broadcast address */
|
||||||
|
if (addr_len > sizeof(*sa)) {
|
||||||
|
err = iso_sock_bind_bc(sock, addr, addr_len);
|
||||||
|
if (err)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
sk->sk_state = BT_BOUND;
|
sk->sk_state = BT_BOUND;
|
||||||
|
|
||||||
done:
|
done:
|
||||||
@ -656,7 +809,11 @@ static int iso_sock_connect(struct socket *sock, struct sockaddr *addr,
|
|||||||
bacpy(&iso_pi(sk)->dst, &sa->iso_bdaddr);
|
bacpy(&iso_pi(sk)->dst, &sa->iso_bdaddr);
|
||||||
iso_pi(sk)->dst_type = sa->iso_bdaddr_type;
|
iso_pi(sk)->dst_type = sa->iso_bdaddr_type;
|
||||||
|
|
||||||
err = iso_connect(sk);
|
if (bacmp(&iso_pi(sk)->dst, BDADDR_ANY))
|
||||||
|
err = iso_connect_cis(sk);
|
||||||
|
else
|
||||||
|
err = iso_connect_bis(sk);
|
||||||
|
|
||||||
if (err)
|
if (err)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
@ -670,10 +827,59 @@ static int iso_sock_connect(struct socket *sock, struct sockaddr *addr,
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int iso_listen_bis(struct sock *sk)
|
||||||
|
{
|
||||||
|
struct hci_dev *hdev;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
BT_DBG("%pMR -> %pMR (SID 0x%2.2x)", &iso_pi(sk)->src,
|
||||||
|
&iso_pi(sk)->dst, iso_pi(sk)->bc_sid);
|
||||||
|
|
||||||
|
write_lock(&iso_sk_list.lock);
|
||||||
|
|
||||||
|
if (__iso_get_sock_listen_by_sid(&iso_pi(sk)->src, &iso_pi(sk)->dst,
|
||||||
|
iso_pi(sk)->bc_sid))
|
||||||
|
err = -EADDRINUSE;
|
||||||
|
|
||||||
|
write_unlock(&iso_sk_list.lock);
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
hdev = hci_get_route(&iso_pi(sk)->dst, &iso_pi(sk)->src,
|
||||||
|
iso_pi(sk)->src_type);
|
||||||
|
if (!hdev)
|
||||||
|
return -EHOSTUNREACH;
|
||||||
|
|
||||||
|
hci_dev_lock(hdev);
|
||||||
|
|
||||||
|
err = hci_pa_create_sync(hdev, &iso_pi(sk)->dst, iso_pi(sk)->dst_type,
|
||||||
|
iso_pi(sk)->bc_sid);
|
||||||
|
|
||||||
|
hci_dev_unlock(hdev);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int iso_listen_cis(struct sock *sk)
|
||||||
|
{
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
BT_DBG("%pMR", &iso_pi(sk)->src);
|
||||||
|
|
||||||
|
write_lock(&iso_sk_list.lock);
|
||||||
|
|
||||||
|
if (__iso_get_sock_listen_by_addr(&iso_pi(sk)->src))
|
||||||
|
err = -EADDRINUSE;
|
||||||
|
|
||||||
|
write_unlock(&iso_sk_list.lock);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static int iso_sock_listen(struct socket *sock, int backlog)
|
static int iso_sock_listen(struct socket *sock, int backlog)
|
||||||
{
|
{
|
||||||
struct sock *sk = sock->sk;
|
struct sock *sk = sock->sk;
|
||||||
bdaddr_t *src = &iso_pi(sk)->src;
|
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
BT_DBG("sk %p backlog %d", sk, backlog);
|
BT_DBG("sk %p backlog %d", sk, backlog);
|
||||||
@ -690,21 +896,19 @@ static int iso_sock_listen(struct socket *sock, int backlog)
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
write_lock(&iso_sk_list.lock);
|
if (!bacmp(&iso_pi(sk)->dst, BDADDR_ANY))
|
||||||
|
err = iso_listen_cis(sk);
|
||||||
|
else
|
||||||
|
err = iso_listen_bis(sk);
|
||||||
|
|
||||||
if (__iso_get_sock_listen_by_addr(src)) {
|
if (err)
|
||||||
err = -EADDRINUSE;
|
goto done;
|
||||||
goto unlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
sk->sk_max_ack_backlog = backlog;
|
sk->sk_max_ack_backlog = backlog;
|
||||||
sk->sk_ack_backlog = 0;
|
sk->sk_ack_backlog = 0;
|
||||||
|
|
||||||
sk->sk_state = BT_LISTEN;
|
sk->sk_state = BT_LISTEN;
|
||||||
|
|
||||||
unlock:
|
|
||||||
write_unlock(&iso_sk_list.lock);
|
|
||||||
|
|
||||||
done:
|
done:
|
||||||
release_sock(sk);
|
release_sock(sk);
|
||||||
return err;
|
return err;
|
||||||
@ -886,7 +1090,7 @@ static int iso_sock_recvmsg(struct socket *sock, struct msghdr *msg,
|
|||||||
release_sock(sk);
|
release_sock(sk);
|
||||||
return 0;
|
return 0;
|
||||||
case BT_CONNECT:
|
case BT_CONNECT:
|
||||||
err = iso_connect(sk);
|
err = iso_connect_cis(sk);
|
||||||
release_sock(sk);
|
release_sock(sk);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@ -917,10 +1121,6 @@ static bool check_io_qos(struct bt_iso_io_qos *qos)
|
|||||||
|
|
||||||
static bool check_qos(struct bt_iso_qos *qos)
|
static bool check_qos(struct bt_iso_qos *qos)
|
||||||
{
|
{
|
||||||
/* CIS shall not be set */
|
|
||||||
if (qos->cis != BT_ISO_QOS_CIS_UNSET)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (qos->sca > 0x07)
|
if (qos->sca > 0x07)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -996,6 +1196,29 @@ static int iso_sock_setsockopt(struct socket *sock, int level, int optname,
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case BT_ISO_BASE:
|
||||||
|
if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND &&
|
||||||
|
sk->sk_state != BT_CONNECT2) {
|
||||||
|
err = -EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optlen > sizeof(iso_pi(sk)->base)) {
|
||||||
|
err = -EOVERFLOW;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = min_t(unsigned int, sizeof(iso_pi(sk)->base), optlen);
|
||||||
|
|
||||||
|
if (copy_from_sockptr(iso_pi(sk)->base, optval, len)) {
|
||||||
|
err = -EFAULT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
iso_pi(sk)->base_len = len;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
err = -ENOPROTOOPT;
|
err = -ENOPROTOOPT;
|
||||||
break;
|
break;
|
||||||
@ -1011,6 +1234,8 @@ static int iso_sock_getsockopt(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;
|
||||||
|
u8 base_len;
|
||||||
|
u8 *base;
|
||||||
|
|
||||||
BT_DBG("sk %p", sk);
|
BT_DBG("sk %p", sk);
|
||||||
|
|
||||||
@ -1044,6 +1269,21 @@ static int iso_sock_getsockopt(struct socket *sock, int level, int optname,
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case BT_ISO_BASE:
|
||||||
|
if (sk->sk_state == BT_CONNECTED) {
|
||||||
|
base_len = iso_pi(sk)->conn->hcon->le_per_adv_data_len;
|
||||||
|
base = iso_pi(sk)->conn->hcon->le_per_adv_data;
|
||||||
|
} else {
|
||||||
|
base_len = iso_pi(sk)->base_len;
|
||||||
|
base = iso_pi(sk)->base;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = min_t(unsigned int, len, base_len);
|
||||||
|
if (copy_to_user(optval, base, len))
|
||||||
|
err = -EFAULT;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
err = -ENOPROTOOPT;
|
err = -ENOPROTOOPT;
|
||||||
break;
|
break;
|
||||||
@ -1126,10 +1366,18 @@ struct iso_list_data {
|
|||||||
int count;
|
int count;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
static void iso_conn_ready(struct iso_conn *conn)
|
static void iso_conn_ready(struct iso_conn *conn)
|
||||||
{
|
{
|
||||||
struct sock *parent;
|
struct sock *parent;
|
||||||
struct sock *sk = conn->sk;
|
struct sock *sk = conn->sk;
|
||||||
|
struct hci_ev_le_big_sync_estabilished *ev;
|
||||||
|
|
||||||
BT_DBG("conn %p", conn);
|
BT_DBG("conn %p", conn);
|
||||||
|
|
||||||
@ -1143,7 +1391,16 @@ static void iso_conn_ready(struct iso_conn *conn)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
parent = iso_get_sock_listen(&conn->hcon->src);
|
ev = hci_recv_event_data(conn->hcon->hdev,
|
||||||
|
HCI_EVT_LE_BIG_SYNC_ESTABILISHED);
|
||||||
|
if (ev)
|
||||||
|
parent = iso_get_sock_listen(&conn->hcon->src,
|
||||||
|
&conn->hcon->dst,
|
||||||
|
iso_match_big, ev);
|
||||||
|
else
|
||||||
|
parent = iso_get_sock_listen(&conn->hcon->src,
|
||||||
|
BDADDR_ANY, NULL, NULL);
|
||||||
|
|
||||||
if (!parent) {
|
if (!parent) {
|
||||||
iso_conn_unlock(conn);
|
iso_conn_unlock(conn);
|
||||||
return;
|
return;
|
||||||
@ -1163,6 +1420,17 @@ static void iso_conn_ready(struct iso_conn *conn)
|
|||||||
|
|
||||||
bacpy(&iso_pi(sk)->src, &conn->hcon->src);
|
bacpy(&iso_pi(sk)->src, &conn->hcon->src);
|
||||||
iso_pi(sk)->src_type = conn->hcon->src_type;
|
iso_pi(sk)->src_type = conn->hcon->src_type;
|
||||||
|
|
||||||
|
/* If hcon has no destination address (BDADDR_ANY) it means it
|
||||||
|
* was created by HCI_EV_LE_BIG_SYNC_ESTABILISHED so we need to
|
||||||
|
* initialize using the parent socket destination address.
|
||||||
|
*/
|
||||||
|
if (!bacmp(&conn->hcon->dst, BDADDR_ANY)) {
|
||||||
|
bacpy(&conn->hcon->dst, &iso_pi(parent)->dst);
|
||||||
|
conn->hcon->dst_type = iso_pi(parent)->dst_type;
|
||||||
|
conn->hcon->sync_handle = iso_pi(parent)->sync_handle;
|
||||||
|
}
|
||||||
|
|
||||||
bacpy(&iso_pi(sk)->dst, &conn->hcon->dst);
|
bacpy(&iso_pi(sk)->dst, &conn->hcon->dst);
|
||||||
iso_pi(sk)->dst_type = conn->hcon->dst_type;
|
iso_pi(sk)->dst_type = conn->hcon->dst_type;
|
||||||
|
|
||||||
@ -1183,30 +1451,85 @@ static void iso_conn_ready(struct iso_conn *conn)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool iso_match_sid(struct sock *sk, void *data)
|
||||||
|
{
|
||||||
|
struct hci_ev_le_pa_sync_established *ev = data;
|
||||||
|
|
||||||
|
return ev->sid == iso_pi(sk)->bc_sid;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool iso_match_sync_handle(struct sock *sk, void *data)
|
||||||
|
{
|
||||||
|
struct hci_evt_le_big_info_adv_report *ev = data;
|
||||||
|
|
||||||
|
return le16_to_cpu(ev->sync_handle) == iso_pi(sk)->sync_handle;
|
||||||
|
}
|
||||||
|
|
||||||
/* ----- ISO interface with lower layer (HCI) ----- */
|
/* ----- ISO interface with lower layer (HCI) ----- */
|
||||||
|
|
||||||
int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
|
int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
|
||||||
{
|
{
|
||||||
|
struct hci_ev_le_pa_sync_established *ev1;
|
||||||
|
struct hci_evt_le_big_info_adv_report *ev2;
|
||||||
struct sock *sk;
|
struct sock *sk;
|
||||||
int lm = 0;
|
int lm = 0;
|
||||||
|
|
||||||
BT_DBG("hdev %s, bdaddr %pMR", hdev->name, bdaddr);
|
bt_dev_dbg(hdev, "bdaddr %pMR", bdaddr);
|
||||||
|
|
||||||
/* Find listening sockets */
|
/* Broadcast receiver requires handling of some events before it can
|
||||||
read_lock(&iso_sk_list.lock);
|
* proceed to establishing a BIG sync:
|
||||||
sk_for_each(sk, &iso_sk_list.head) {
|
*
|
||||||
if (sk->sk_state != BT_LISTEN)
|
* 1. HCI_EV_LE_PA_SYNC_ESTABLISHED: The socket may specify a specific
|
||||||
continue;
|
* SID to listen to and once sync is estabilished its handle needs to
|
||||||
|
* be stored in iso_pi(sk)->sync_handle so it can be matched once
|
||||||
|
* receiving the BIG Info.
|
||||||
|
* 2. HCI_EVT_LE_BIG_INFO_ADV_REPORT: When connect_ind is triggered by a
|
||||||
|
* a BIG Info it attempts to check if there any listening socket with
|
||||||
|
* the same sync_handle and if it does then attempt to create a sync.
|
||||||
|
*/
|
||||||
|
ev1 = hci_recv_event_data(hdev, HCI_EV_LE_PA_SYNC_ESTABLISHED);
|
||||||
|
if (ev1) {
|
||||||
|
sk = iso_get_sock_listen(&hdev->bdaddr, bdaddr, iso_match_sid,
|
||||||
|
ev1);
|
||||||
|
if (sk)
|
||||||
|
iso_pi(sk)->sync_handle = le16_to_cpu(ev1->handle);
|
||||||
|
|
||||||
if (!bacmp(&iso_pi(sk)->src, &hdev->bdaddr) ||
|
goto done;
|
||||||
!bacmp(&iso_pi(sk)->src, BDADDR_ANY)) {
|
|
||||||
lm |= HCI_LM_ACCEPT;
|
|
||||||
|
|
||||||
if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags))
|
|
||||||
*flags |= HCI_PROTO_DEFER;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
read_unlock(&iso_sk_list.lock);
|
|
||||||
|
ev2 = hci_recv_event_data(hdev, HCI_EVT_LE_BIG_INFO_ADV_REPORT);
|
||||||
|
if (ev2) {
|
||||||
|
sk = iso_get_sock_listen(&hdev->bdaddr, bdaddr,
|
||||||
|
iso_match_sync_handle, ev2);
|
||||||
|
if (sk) {
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (ev2->num_bis < iso_pi(sk)->bc_num_bis)
|
||||||
|
iso_pi(sk)->bc_num_bis = ev2->num_bis;
|
||||||
|
|
||||||
|
err = hci_le_big_create_sync(hdev,
|
||||||
|
&iso_pi(sk)->qos,
|
||||||
|
iso_pi(sk)->sync_handle,
|
||||||
|
iso_pi(sk)->bc_num_bis,
|
||||||
|
iso_pi(sk)->bc_bis);
|
||||||
|
if (err) {
|
||||||
|
bt_dev_err(hdev, "hci_le_big_create_sync: %d",
|
||||||
|
err);
|
||||||
|
sk = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sk = iso_get_sock_listen(&hdev->bdaddr, BDADDR_ANY, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
if (!sk)
|
||||||
|
return lm;
|
||||||
|
|
||||||
|
lm |= HCI_LM_ACCEPT;
|
||||||
|
|
||||||
|
if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags))
|
||||||
|
*flags |= HCI_PROTO_DEFER;
|
||||||
|
|
||||||
return lm;
|
return lm;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user