[Bluetooth] Use ACL config stage to retrieve remote features
[pandora-kernel.git] / net / bluetooth / hci_event.c
index c8fda7d..e3e360c 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);