Bluetooth: Stop scanning on LE connection
authorAndre Guedes <andre.guedes@openbossa.org>
Wed, 26 Feb 2014 23:21:42 +0000 (20:21 -0300)
committerMarcel Holtmann <marcel@holtmann.org>
Thu, 27 Feb 2014 03:41:33 +0000 (19:41 -0800)
Some LE controllers don't support scanning and creating a connection
at the same time. So we should always stop scanning in order to
establish the connection.

Since we may prematurely stop the discovery procedure in favor of
the connection establishment, we should also cancel hdev->le_scan_
disable delayed work and set the discovery state to DISCOVERY_STOPPED.

This change does a small improvement since it is not mandatory the
user stops scanning before connecting anymore. Moreover, this change
is required by upcoming LE auto connection mechanism in order to work
properly with controllers that don't support background scanning and
connection establishment at the same time.

In future, we might want to do a small optimization by checking if
controller is able to scan and connect at the same time. For now,
we want the simplest approach so we always stop scanning (even if
the controller is able to carry out both operations).

Signed-off-by: Andre Guedes <andre.guedes@openbossa.org>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
include/net/bluetooth/hci.h
net/bluetooth/hci_conn.c

index 1bb45a47a78add4885e31a10b36bca0745a52244..c3834d3aecbb9b117d4a67b2645484f513b72103 100644 (file)
@@ -356,6 +356,7 @@ enum {
 
 /* ---- HCI Error Codes ---- */
 #define HCI_ERROR_AUTH_FAILURE         0x05
+#define HCI_ERROR_MEMORY_EXCEEDED      0x07
 #define HCI_ERROR_CONNECTION_TIMEOUT   0x08
 #define HCI_ERROR_REJ_BAD_ADDR         0x0f
 #define HCI_ERROR_REMOTE_USER_TERM     0x13
index dc8aad94642653f3bb7c3d4096ec58e3a2ef2ba4..2b8bfda3ea35d93ad8cb4f52ac139924ff8f1350 100644 (file)
@@ -594,12 +594,86 @@ static int hci_create_le_conn(struct hci_conn *conn)
        return 0;
 }
 
+static void hci_req_add_le_create_conn(struct hci_request *req,
+                                      struct hci_conn *conn)
+{
+       struct hci_cp_le_create_conn cp;
+       struct hci_dev *hdev = conn->hdev;
+       u8 own_addr_type;
+
+       memset(&cp, 0, sizeof(cp));
+
+       /* Update random address, but set require_privacy to false so
+        * that we never connect with an unresolvable address.
+        */
+       if (hci_update_random_address(req, false, &own_addr_type))
+               return;
+
+       /* Save the address type used for this connnection attempt so we able
+        * to retrieve this information if we need it.
+        */
+       conn->src_type = own_addr_type;
+
+       cp.scan_interval = cpu_to_le16(hdev->le_scan_interval);
+       cp.scan_window = cpu_to_le16(hdev->le_scan_window);
+       bacpy(&cp.peer_addr, &conn->dst);
+       cp.peer_addr_type = conn->dst_type;
+       cp.own_address_type = own_addr_type;
+       cp.conn_interval_min = cpu_to_le16(conn->le_conn_min_interval);
+       cp.conn_interval_max = cpu_to_le16(conn->le_conn_max_interval);
+       cp.supervision_timeout = __constant_cpu_to_le16(0x002a);
+       cp.min_ce_len = __constant_cpu_to_le16(0x0000);
+       cp.max_ce_len = __constant_cpu_to_le16(0x0000);
+
+       hci_req_add(req, HCI_OP_LE_CREATE_CONN, sizeof(cp), &cp);
+}
+
+static void stop_scan_complete(struct hci_dev *hdev, u8 status)
+{
+       struct hci_request req;
+       struct hci_conn *conn;
+       int err;
+
+       conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
+       if (!conn)
+               return;
+
+       if (status) {
+               BT_DBG("HCI request failed to stop scanning: status 0x%2.2x",
+                      status);
+
+               hci_dev_lock(hdev);
+               hci_le_conn_failed(conn, status);
+               hci_dev_unlock(hdev);
+               return;
+       }
+
+       /* Since we may have prematurely stopped discovery procedure, we should
+        * update discovery state.
+        */
+       cancel_delayed_work(&hdev->le_scan_disable);
+       hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+
+       hci_req_init(&req, hdev);
+
+       hci_req_add_le_create_conn(&req, conn);
+
+       err = hci_req_run(&req, create_le_conn_complete);
+       if (err) {
+               hci_dev_lock(hdev);
+               hci_le_conn_failed(conn, HCI_ERROR_MEMORY_EXCEEDED);
+               hci_dev_unlock(hdev);
+               return;
+       }
+}
+
 static struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
                                    u8 dst_type, u8 sec_level, u8 auth_type)
 {
        struct hci_conn_params *params;
        struct hci_conn *conn;
        struct smp_irk *irk;
+       struct hci_request req;
        int err;
 
        if (test_bit(HCI_ADVERTISING, &hdev->flags))
@@ -675,9 +749,23 @@ static struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
                conn->le_conn_max_interval = hdev->le_conn_max_interval;
        }
 
-       err = hci_create_le_conn(conn);
-       if (err)
+       hci_req_init(&req, hdev);
+
+       /* If controller is scanning, we stop it since some controllers are
+        * not able to scan and connect at the same time.
+        */
+       if (test_bit(HCI_LE_SCAN, &hdev->dev_flags)) {
+               hci_req_add_le_scan_disable(&req);
+               err = hci_req_run(&req, stop_scan_complete);
+       } else {
+               hci_req_add_le_create_conn(&req, conn);
+               err = hci_req_run(&req, create_le_conn_complete);
+       }
+
+       if (err) {
+               hci_conn_del(conn);
                return ERR_PTR(err);
+       }
 
 done:
        hci_conn_hold(conn);