Bluetooth: Implement deferred sco socket setup
authorFrédéric Dalleau <frederic.dalleau@linux.intel.com>
Wed, 21 Nov 2012 09:51:12 +0000 (10:51 +0100)
committerGustavo Padovan <gustavo.padovan@collabora.co.uk>
Mon, 3 Dec 2012 17:59:58 +0000 (15:59 -0200)
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>
include/net/bluetooth/hci_core.h
net/bluetooth/hci_event.c
net/bluetooth/sco.c

index ef5b85dac3f77720c13ba69a6482b58a3043c018..76891a914e7532df5637f5e1c01520d59b34b17e 100644 (file)
@@ -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,
                              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_disconn_cfm(struct hci_conn *hcon, __u8 reason);
 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);
 void hci_conn_hash_flush(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);
 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)
 
 /* ----- HCI protocols ----- */
+#define HCI_PROTO_DEFER             0x01
+
 static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                                                               __u8 type)
+                                       __u8 type, __u8 *flags)
 {
        switch (type) {
        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 ESCO_LINK:
-               return sco_connect_ind(hdev, bdaddr);
+               return sco_connect_ind(hdev, bdaddr, flags);
 
        default:
                BT_ERR("unknown link type %d", type);
index 9f5c5f244502ef3cad289c8477fb05ad3990f37e..3843f1897c86a4bc9e6b625b7dc38d7c8b3b2fa8 100644 (file)
@@ -2047,15 +2047,53 @@ unlock:
        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)
 {
        struct hci_ev_conn_request *ev = (void *) skb->data;
        int mask = hdev->link_mode;
+       __u8 flags = 0;
 
        BT_DBG("%s bdaddr %pMR type 0x%x", hdev->name, &ev->bdaddr,
               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) &&
            !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);
-               conn->state = BT_CONNECT;
 
                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;
+                       conn->state = BT_CONNECT;
 
                        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),
                                     &cp);
-               } else {
+               } else if (!(flags & HCI_PROTO_DEFER)) {
                        struct hci_cp_accept_sync_conn_req cp;
+                       conn->state = BT_CONNECT;
 
                        bacpy(&cp.bdaddr, &ev->bdaddr);
                        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,
                                     sizeof(cp), &cp);
+               } else {
+                       conn->state = BT_CONNECT2;
+                       hci_proto_connect_cfm(conn, 0);
+                       hci_conn_put(conn);
                }
        } else {
                /* Connection rejected */
index c6678f2bffc958e0f10e511ee9522b5dd52bbd34..eea17cdcaf7f30aa87c9a4a5a07859aac2891fab 100644 (file)
@@ -397,6 +397,7 @@ static void sco_sock_init(struct sock *sk, struct sock *parent)
 
        if (parent) {
                sk->sk_type = parent->sk_type;
+               bt_sk(sk)->flags = bt_sk(parent)->flags;
                security_sk_clone(parent, sk);
        }
 }
@@ -662,6 +663,28 @@ static int sco_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
        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)
 {
        struct sock *sk = sock->sk;
@@ -906,7 +929,10 @@ static void sco_conn_ready(struct sco_conn *conn)
                hci_conn_hold(conn->hcon);
                __sco_chan_add(conn, sk, parent);
 
-               sk->sk_state = BT_CONNECTED;
+               if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags))
+                       sk->sk_state = BT_CONNECT2;
+               else
+                       sk->sk_state = BT_CONNECTED;
 
                /* Wake up parent */
                parent->sk_data_ready(parent, 1);
@@ -919,7 +945,7 @@ done:
 }
 
 /* ----- 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 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) ||
                    !bacmp(&bt_sk(sk)->src, BDADDR_ANY)) {
                        lm |= HCI_LM_ACCEPT;
+
+                       if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags))
+                               *flags |= HCI_PROTO_DEFER;
                        break;
                }
        }
@@ -1024,7 +1053,7 @@ static const struct proto_ops sco_sock_ops = {
        .accept         = sco_sock_accept,
        .getname        = sco_sock_getname,
        .sendmsg        = sco_sock_sendmsg,
-       .recvmsg        = bt_sock_recvmsg,
+       .recvmsg        = sco_sock_recvmsg,
        .poll           = bt_sock_poll,
        .ioctl          = bt_sock_ioctl,
        .mmap           = sock_no_mmap,