]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
[Bluetooth] Use ACL config stage to retrieve remote features
authorMarcel Holtmann <marcel@holtmann.org>
Mon, 14 Jul 2008 18:13:49 +0000 (20:13 +0200)
committerMarcel Holtmann <marcel@holtmann.org>
Mon, 14 Jul 2008 18:13:49 +0000 (20:13 +0200)
The Bluetooth technology introduces new features on a regular basis
and for some of them it is important that the hardware on both sides
support them. For features like Simple Pairing it is important that
the host stacks on both sides have switched this feature on. To make
valid decisions, a config stage during ACL link establishment has been
introduced that retrieves remote features and if needed also the remote
extended features (known as remote host features) before signalling
this link as connected.

This change introduces full reference counting of incoming and outgoing
ACL links and the Bluetooth core will disconnect both if no owner of it
is present. To better handle interoperability during the pairing phase
the disconnect timeout for incoming connections has been increased to
10 seconds. This is five times more than for outgoing connections.

Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
net/bluetooth/hci_conn.c
net/bluetooth/hci_core.c
net/bluetooth/hci_event.c

index 5ac0a18db63c4abdb593369bc9b5384b614a777c..55576e8488276365eeb78a87bf863eff30218fda 100644 (file)
@@ -180,6 +180,8 @@ enum {
 
 #define LMP_SNIFF_SUBR 0x02
 
+#define LMP_SIMPLE_PAIR        0x08
+
 /* Connection modes */
 #define HCI_CM_ACTIVE  0x0000
 #define HCI_CM_HOLD    0x0001
index f73cc294570069f2a4549d8ef05aca65a8d0150b..28fbd0caa5341dccb92a355e583f14a144f80507 100644 (file)
@@ -348,7 +348,7 @@ static inline void hci_conn_put(struct hci_conn *conn)
                        if (conn->state == BT_CONNECTED) {
                                timeo = msecs_to_jiffies(HCI_DISCONN_TIMEOUT);
                                if (!conn->out)
-                                       timeo *= 2;
+                                       timeo *= 5;
                        } else
                                timeo = msecs_to_jiffies(10);
                } else
@@ -463,6 +463,7 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
 #define lmp_sniff_capable(dev)     ((dev)->features[0] & LMP_SNIFF)
 #define lmp_sniffsubr_capable(dev) ((dev)->features[5] & LMP_SNIFF_SUBR)
 #define lmp_esco_capable(dev)      ((dev)->features[3] & LMP_ESCO)
+#define lmp_ssp_capable(dev)       ((dev)->features[6] & LMP_SIMPLE_PAIR)
 
 /* ----- HCI protocols ----- */
 struct hci_proto {
index 41351ba692e90174ec5dcfc4519cb4b393858762..6f22533e7656fac87a5578275954e5a23301131e 100644 (file)
@@ -170,11 +170,13 @@ static void hci_conn_timeout(unsigned long arg)
 
        switch (conn->state) {
        case BT_CONNECT:
+       case BT_CONNECT2:
                if (conn->type == ACL_LINK)
                        hci_acl_connect_cancel(conn);
                else
                        hci_acl_disconn(conn, 0x13);
                break;
+       case BT_CONFIG:
        case BT_CONNECTED:
                hci_acl_disconn(conn, 0x13);
                break;
index 69b2c1aac08a5caa82274b57cb774932cbf65d74..f5b21cb936996b87ec7138d8b8b81c3e1fa6f006 100644 (file)
@@ -1283,9 +1283,12 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int
                struct hci_conn *c;
                c = list_entry(p, struct hci_conn, list);
 
-               if (c->type != type || c->state != BT_CONNECTED
-                               || skb_queue_empty(&c->data_q))
+               if (c->type != type || skb_queue_empty(&c->data_q))
                        continue;
+
+               if (c->state != BT_CONNECTED && c->state != BT_CONFIG)
+                       continue;
+
                num++;
 
                if (c->sent < min) {
index c8fda7dc298693f1761b8cf4fd88f854591690ef..e3e360c3c536e8d4a55521ea31ce54bcf1934726 100644 (file)
@@ -624,6 +624,62 @@ static void hci_cs_remote_name_req(struct hci_dev *hdev, __u8 status)
        BT_DBG("%s status 0x%x", hdev->name, status);
 }
 
+static void hci_cs_read_remote_features(struct hci_dev *hdev, __u8 status)
+{
+       struct hci_cp_read_remote_features *cp;
+       struct hci_conn *conn;
+
+       BT_DBG("%s status 0x%x", hdev->name, status);
+
+       if (!status)
+               return;
+
+       cp = hci_sent_cmd_data(hdev, HCI_OP_READ_REMOTE_FEATURES);
+       if (!cp)
+               return;
+
+       hci_dev_lock(hdev);
+
+       conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
+       if (conn) {
+               if (conn->state == BT_CONFIG) {
+                       conn->state = BT_CONNECTED;
+                       hci_proto_connect_cfm(conn, status);
+                       hci_conn_put(conn);
+               }
+       }
+
+       hci_dev_unlock(hdev);
+}
+
+static void hci_cs_read_remote_ext_features(struct hci_dev *hdev, __u8 status)
+{
+       struct hci_cp_read_remote_ext_features *cp;
+       struct hci_conn *conn;
+
+       BT_DBG("%s status 0x%x", hdev->name, status);
+
+       if (!status)
+               return;
+
+       cp = hci_sent_cmd_data(hdev, HCI_OP_READ_REMOTE_EXT_FEATURES);
+       if (!cp)
+               return;
+
+       hci_dev_lock(hdev);
+
+       conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
+       if (conn) {
+               if (conn->state == BT_CONFIG) {
+                       conn->state = BT_CONNECTED;
+                       hci_proto_connect_cfm(conn, status);
+                       hci_conn_put(conn);
+               }
+       }
+
+       hci_dev_unlock(hdev);
+}
+
 static void hci_cs_setup_sync_conn(struct hci_dev *hdev, __u8 status)
 {
        struct hci_cp_setup_sync_conn *cp;
@@ -759,7 +815,12 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
 
        if (!ev->status) {
                conn->handle = __le16_to_cpu(ev->handle);
-               conn->state  = BT_CONNECTED;
+
+               if (conn->type == ACL_LINK) {
+                       conn->state = BT_CONFIG;
+                       hci_conn_hold(conn);
+               } else
+                       conn->state = BT_CONNECTED;
 
                if (test_bit(HCI_AUTH, &hdev->flags))
                        conn->link_mode |= HCI_LM_AUTH;
@@ -771,7 +832,8 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
                if (conn->type == ACL_LINK) {
                        struct hci_cp_read_remote_features cp;
                        cp.handle = ev->handle;
-                       hci_send_cmd(hdev, HCI_OP_READ_REMOTE_FEATURES, sizeof(cp), &cp);
+                       hci_send_cmd(hdev, HCI_OP_READ_REMOTE_FEATURES,
+                                                       sizeof(cp), &cp);
                }
 
                /* Set packet type for incoming connection */
@@ -781,10 +843,6 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
                        cp.pkt_type = cpu_to_le16(conn->pkt_type);
                        hci_send_cmd(hdev, HCI_OP_CHANGE_CONN_PTYPE,
                                                        sizeof(cp), &cp);
-               } else {
-                       /* Update disconnect timer */
-                       hci_conn_hold(conn);
-                       hci_conn_put(conn);
                }
        } else
                conn->state = BT_CLOSED;
@@ -804,9 +862,10 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
                }
        }
 
-       hci_proto_connect_cfm(conn, ev->status);
-       if (ev->status)
+       if (ev->status) {
+               hci_proto_connect_cfm(conn, ev->status);
                hci_conn_del(conn);
+       }
 
 unlock:
        hci_dev_unlock(hdev);
@@ -1006,14 +1065,29 @@ static inline void hci_remote_features_evt(struct hci_dev *hdev, struct sk_buff
 
        BT_DBG("%s status %d", hdev->name, ev->status);
 
-       if (ev->status)
-               return;
-
        hci_dev_lock(hdev);
 
        conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
-       if (conn)
-               memcpy(conn->features, ev->features, 8);
+       if (conn) {
+               if (!ev->status)
+                       memcpy(conn->features, ev->features, 8);
+
+               if (conn->state == BT_CONFIG) {
+                       if (!ev->status && lmp_ssp_capable(hdev) &&
+                                               lmp_ssp_capable(conn)) {
+                               struct hci_cp_read_remote_ext_features cp;
+                               cp.handle = ev->handle;
+                               cp.page = 0x01;
+                               hci_send_cmd(hdev,
+                                       HCI_OP_READ_REMOTE_EXT_FEATURES,
+                                                       sizeof(cp), &cp);
+                       } else {
+                               conn->state = BT_CONNECTED;
+                               hci_proto_connect_cfm(conn, ev->status);
+                               hci_conn_put(conn);
+                       }
+               }
+       }
 
        hci_dev_unlock(hdev);
 }
@@ -1180,6 +1254,14 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_cs_remote_name_req(hdev, ev->status);
                break;
 
+       case HCI_OP_READ_REMOTE_FEATURES:
+               hci_cs_read_remote_features(hdev, ev->status);
+               break;
+
+       case HCI_OP_READ_REMOTE_EXT_FEATURES:
+               hci_cs_read_remote_ext_features(hdev, ev->status);
+               break;
+
        case HCI_OP_SETUP_SYNC_CONN:
                hci_cs_setup_sync_conn(hdev, ev->status);
                break;
@@ -1422,19 +1504,24 @@ static inline void hci_remote_ext_features_evt(struct hci_dev *hdev, struct sk_b
 
        BT_DBG("%s", hdev->name);
 
-       if (ev->status || ev->page != 0x01)
-               return;
-
        hci_dev_lock(hdev);
 
        conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
        if (conn) {
-               struct inquiry_entry *ie;
+               if (!ev->status && ev->page == 0x01) {
+                       struct inquiry_entry *ie;
 
-               if ((ie = hci_inquiry_cache_lookup(hdev, &conn->dst)))
-                       ie->data.ssp_mode = (ev->features[0] & 0x01);
+                       if ((ie = hci_inquiry_cache_lookup(hdev, &conn->dst)))
+                               ie->data.ssp_mode = (ev->features[0] & 0x01);
 
-               conn->ssp_mode = (ev->features[0] & 0x01);
+                       conn->ssp_mode = (ev->features[0] & 0x01);
+               }
+
+               if (conn->state == BT_CONFIG) {
+                       conn->state = BT_CONNECTED;
+                       hci_proto_connect_cfm(conn, ev->status);
+                       hci_conn_put(conn);
+               }
        }
 
        hci_dev_unlock(hdev);