Bluetooth: Implement deferred sco socket setup
In order to authenticate and configure an incoming SCO connection, the BT_DEFER_SETUP option was added. This option is intended to defer reply to Connect Request on SCO sockets. When a connection is requested, the listening socket is unblocked but the effective connection setup happens only on first recv. Any send between accept and recv fails with -ENOTCONN. Signed-off-by: Frédéric Dalleau <frederic.dalleau@linux.intel.com> Signed-off-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
This commit is contained in:
parent
b96e9c671b
commit
20714bfef8
|
@ -376,7 +376,7 @@ extern int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt);
|
||||||
extern int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb,
|
extern int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb,
|
||||||
u16 flags);
|
u16 flags);
|
||||||
|
|
||||||
extern int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr);
|
extern int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags);
|
||||||
extern void sco_connect_cfm(struct hci_conn *hcon, __u8 status);
|
extern void sco_connect_cfm(struct hci_conn *hcon, __u8 status);
|
||||||
extern void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason);
|
extern void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason);
|
||||||
extern int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb);
|
extern int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb);
|
||||||
|
@ -577,6 +577,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst);
|
||||||
int hci_conn_del(struct hci_conn *conn);
|
int hci_conn_del(struct hci_conn *conn);
|
||||||
void hci_conn_hash_flush(struct hci_dev *hdev);
|
void hci_conn_hash_flush(struct hci_dev *hdev);
|
||||||
void hci_conn_check_pending(struct hci_dev *hdev);
|
void hci_conn_check_pending(struct hci_dev *hdev);
|
||||||
|
void hci_conn_accept(struct hci_conn *conn, int mask);
|
||||||
|
|
||||||
struct hci_chan *hci_chan_create(struct hci_conn *conn);
|
struct hci_chan *hci_chan_create(struct hci_conn *conn);
|
||||||
void hci_chan_del(struct hci_chan *chan);
|
void hci_chan_del(struct hci_chan *chan);
|
||||||
|
@ -779,8 +780,10 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
|
||||||
#define lmp_host_le_br_capable(dev) ((dev)->host_features[0] & LMP_HOST_LE_BREDR)
|
#define lmp_host_le_br_capable(dev) ((dev)->host_features[0] & LMP_HOST_LE_BREDR)
|
||||||
|
|
||||||
/* ----- HCI protocols ----- */
|
/* ----- HCI protocols ----- */
|
||||||
|
#define HCI_PROTO_DEFER 0x01
|
||||||
|
|
||||||
static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||||
__u8 type)
|
__u8 type, __u8 *flags)
|
||||||
{
|
{
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case ACL_LINK:
|
case ACL_LINK:
|
||||||
|
@ -788,7 +791,7 @@ static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||||
|
|
||||||
case SCO_LINK:
|
case SCO_LINK:
|
||||||
case ESCO_LINK:
|
case ESCO_LINK:
|
||||||
return sco_connect_ind(hdev, bdaddr);
|
return sco_connect_ind(hdev, bdaddr, flags);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
BT_ERR("unknown link type %d", type);
|
BT_ERR("unknown link type %d", type);
|
||||||
|
|
|
@ -2047,15 +2047,53 @@ unlock:
|
||||||
hci_conn_check_pending(hdev);
|
hci_conn_check_pending(hdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void hci_conn_accept(struct hci_conn *conn, int mask)
|
||||||
|
{
|
||||||
|
struct hci_dev *hdev = conn->hdev;
|
||||||
|
|
||||||
|
BT_DBG("conn %p", conn);
|
||||||
|
|
||||||
|
conn->state = BT_CONFIG;
|
||||||
|
|
||||||
|
if (!lmp_esco_capable(hdev)) {
|
||||||
|
struct hci_cp_accept_conn_req cp;
|
||||||
|
|
||||||
|
bacpy(&cp.bdaddr, &conn->dst);
|
||||||
|
|
||||||
|
if (lmp_rswitch_capable(hdev) && (mask & HCI_LM_MASTER))
|
||||||
|
cp.role = 0x00; /* Become master */
|
||||||
|
else
|
||||||
|
cp.role = 0x01; /* Remain slave */
|
||||||
|
|
||||||
|
hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp), &cp);
|
||||||
|
} else /* lmp_esco_capable(hdev)) */ {
|
||||||
|
struct hci_cp_accept_sync_conn_req cp;
|
||||||
|
|
||||||
|
bacpy(&cp.bdaddr, &conn->dst);
|
||||||
|
cp.pkt_type = cpu_to_le16(conn->pkt_type);
|
||||||
|
|
||||||
|
cp.tx_bandwidth = __constant_cpu_to_le32(0x00001f40);
|
||||||
|
cp.rx_bandwidth = __constant_cpu_to_le32(0x00001f40);
|
||||||
|
cp.max_latency = __constant_cpu_to_le16(0xffff);
|
||||||
|
cp.content_format = cpu_to_le16(hdev->voice_setting);
|
||||||
|
cp.retrans_effort = 0xff;
|
||||||
|
|
||||||
|
hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ,
|
||||||
|
sizeof(cp), &cp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct hci_ev_conn_request *ev = (void *) skb->data;
|
struct hci_ev_conn_request *ev = (void *) skb->data;
|
||||||
int mask = hdev->link_mode;
|
int mask = hdev->link_mode;
|
||||||
|
__u8 flags = 0;
|
||||||
|
|
||||||
BT_DBG("%s bdaddr %pMR type 0x%x", hdev->name, &ev->bdaddr,
|
BT_DBG("%s bdaddr %pMR type 0x%x", hdev->name, &ev->bdaddr,
|
||||||
ev->link_type);
|
ev->link_type);
|
||||||
|
|
||||||
mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type);
|
mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type,
|
||||||
|
&flags);
|
||||||
|
|
||||||
if ((mask & HCI_LM_ACCEPT) &&
|
if ((mask & HCI_LM_ACCEPT) &&
|
||||||
!hci_blacklist_lookup(hdev, &ev->bdaddr)) {
|
!hci_blacklist_lookup(hdev, &ev->bdaddr)) {
|
||||||
|
@ -2081,12 +2119,13 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(conn->dev_class, ev->dev_class, 3);
|
memcpy(conn->dev_class, ev->dev_class, 3);
|
||||||
conn->state = BT_CONNECT;
|
|
||||||
|
|
||||||
hci_dev_unlock(hdev);
|
hci_dev_unlock(hdev);
|
||||||
|
|
||||||
if (ev->link_type == ACL_LINK || !lmp_esco_capable(hdev)) {
|
if (ev->link_type == ACL_LINK ||
|
||||||
|
(!(flags & HCI_PROTO_DEFER) && !lmp_esco_capable(hdev))) {
|
||||||
struct hci_cp_accept_conn_req cp;
|
struct hci_cp_accept_conn_req cp;
|
||||||
|
conn->state = BT_CONNECT;
|
||||||
|
|
||||||
bacpy(&cp.bdaddr, &ev->bdaddr);
|
bacpy(&cp.bdaddr, &ev->bdaddr);
|
||||||
|
|
||||||
|
@ -2097,8 +2136,9 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
|
|
||||||
hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp),
|
hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp),
|
||||||
&cp);
|
&cp);
|
||||||
} else {
|
} else if (!(flags & HCI_PROTO_DEFER)) {
|
||||||
struct hci_cp_accept_sync_conn_req cp;
|
struct hci_cp_accept_sync_conn_req cp;
|
||||||
|
conn->state = BT_CONNECT;
|
||||||
|
|
||||||
bacpy(&cp.bdaddr, &ev->bdaddr);
|
bacpy(&cp.bdaddr, &ev->bdaddr);
|
||||||
cp.pkt_type = cpu_to_le16(conn->pkt_type);
|
cp.pkt_type = cpu_to_le16(conn->pkt_type);
|
||||||
|
@ -2111,6 +2151,10 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
|
|
||||||
hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ,
|
hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ,
|
||||||
sizeof(cp), &cp);
|
sizeof(cp), &cp);
|
||||||
|
} else {
|
||||||
|
conn->state = BT_CONNECT2;
|
||||||
|
hci_proto_connect_cfm(conn, 0);
|
||||||
|
hci_conn_put(conn);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Connection rejected */
|
/* Connection rejected */
|
||||||
|
|
|
@ -397,6 +397,7 @@ static void sco_sock_init(struct sock *sk, struct sock *parent)
|
||||||
|
|
||||||
if (parent) {
|
if (parent) {
|
||||||
sk->sk_type = parent->sk_type;
|
sk->sk_type = parent->sk_type;
|
||||||
|
bt_sk(sk)->flags = bt_sk(parent)->flags;
|
||||||
security_sk_clone(parent, sk);
|
security_sk_clone(parent, sk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -662,6 +663,28 @@ static int sco_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int sco_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
|
||||||
|
struct msghdr *msg, size_t len, int flags)
|
||||||
|
{
|
||||||
|
struct sock *sk = sock->sk;
|
||||||
|
struct sco_pinfo *pi = sco_pi(sk);
|
||||||
|
|
||||||
|
lock_sock(sk);
|
||||||
|
|
||||||
|
if (sk->sk_state == BT_CONNECT2 &&
|
||||||
|
test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) {
|
||||||
|
hci_conn_accept(pi->conn->hcon, 0);
|
||||||
|
sk->sk_state = BT_CONFIG;
|
||||||
|
|
||||||
|
release_sock(sk);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
release_sock(sk);
|
||||||
|
|
||||||
|
return bt_sock_recvmsg(iocb, sock, msg, len, flags);
|
||||||
|
}
|
||||||
|
|
||||||
static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen)
|
static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen)
|
||||||
{
|
{
|
||||||
struct sock *sk = sock->sk;
|
struct sock *sk = sock->sk;
|
||||||
|
@ -906,6 +929,9 @@ static void sco_conn_ready(struct sco_conn *conn)
|
||||||
hci_conn_hold(conn->hcon);
|
hci_conn_hold(conn->hcon);
|
||||||
__sco_chan_add(conn, sk, parent);
|
__sco_chan_add(conn, sk, parent);
|
||||||
|
|
||||||
|
if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags))
|
||||||
|
sk->sk_state = BT_CONNECT2;
|
||||||
|
else
|
||||||
sk->sk_state = BT_CONNECTED;
|
sk->sk_state = BT_CONNECTED;
|
||||||
|
|
||||||
/* Wake up parent */
|
/* Wake up parent */
|
||||||
|
@ -919,7 +945,7 @@ done:
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----- SCO interface with lower layer (HCI) ----- */
|
/* ----- SCO interface with lower layer (HCI) ----- */
|
||||||
int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
|
||||||
{
|
{
|
||||||
struct sock *sk;
|
struct sock *sk;
|
||||||
struct hlist_node *node;
|
struct hlist_node *node;
|
||||||
|
@ -936,6 +962,9 @@ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
||||||
if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr) ||
|
if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr) ||
|
||||||
!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) {
|
!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) {
|
||||||
lm |= HCI_LM_ACCEPT;
|
lm |= HCI_LM_ACCEPT;
|
||||||
|
|
||||||
|
if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags))
|
||||||
|
*flags |= HCI_PROTO_DEFER;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1024,7 +1053,7 @@ static const struct proto_ops sco_sock_ops = {
|
||||||
.accept = sco_sock_accept,
|
.accept = sco_sock_accept,
|
||||||
.getname = sco_sock_getname,
|
.getname = sco_sock_getname,
|
||||||
.sendmsg = sco_sock_sendmsg,
|
.sendmsg = sco_sock_sendmsg,
|
||||||
.recvmsg = bt_sock_recvmsg,
|
.recvmsg = sco_sock_recvmsg,
|
||||||
.poll = bt_sock_poll,
|
.poll = bt_sock_poll,
|
||||||
.ioctl = bt_sock_ioctl,
|
.ioctl = bt_sock_ioctl,
|
||||||
.mmap = sock_no_mmap,
|
.mmap = sock_no_mmap,
|
||||||
|
|
Loading…
Reference in New Issue