Bluetooth: Add simultaneous dual mode scan
authorJakub Pawlowski <jpawlowski@google.com>
Tue, 17 Mar 2015 16:04:14 +0000 (09:04 -0700)
committerJohan Hedberg <johan.hedberg@intel.com>
Tue, 17 Mar 2015 16:31:00 +0000 (18:31 +0200)
When doing scan through mgmt api, some controllers can do both le and
classic scan at same time. They can be distinguished by
HCI_QUIRK_SIMULTANEOUS_DISCOVERY set.

This patch enables them to use this feature when doing dual mode scan.
Instead of doing le, then classic scan, both scans are run at once.

Signed-off-by: Jakub Pawlowski <jpawlowski@google.com>
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
net/bluetooth/hci_core.c
net/bluetooth/hci_event.c
net/bluetooth/mgmt.c

index 750d3445f2d26b08e384ef89833e6b09e05b3569..773f2164d9a130c6690b3c85f4bad6ff15cad49f 100644 (file)
@@ -2902,12 +2902,26 @@ static void le_scan_disable_work_complete(struct hci_dev *hdev, u8 status,
 
                hci_dev_lock(hdev);
 
-               hci_inquiry_cache_flush(hdev);
+               if (test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY,
+                            &hdev->quirks)) {
+                       /* If we were running LE only scan, change discovery
+                        * state. If we were running both LE and BR/EDR inquiry
+                        * simultaneously, and BR/EDR inquiry is already
+                        * finished, stop discovery, otherwise BR/EDR inquiry
+                        * will stop discovery when finished.
+                        */
+                       if (!test_bit(HCI_INQUIRY, &hdev->flags))
+                               hci_discovery_set_state(hdev,
+                                                       DISCOVERY_STOPPED);
+               } else {
+                       hci_inquiry_cache_flush(hdev);
 
-               err = hci_req_run(&req, inquiry_complete);
-               if (err) {
-                       BT_ERR("Inquiry request failed: err %d", err);
-                       hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+                       err = hci_req_run(&req, inquiry_complete);
+                       if (err) {
+                               BT_ERR("Inquiry request failed: err %d", err);
+                               hci_discovery_set_state(hdev,
+                                                       DISCOVERY_STOPPED);
+                       }
                }
 
                hci_dev_unlock(hdev);
index d800f0c5aa2157763cb5f8816b2393557a602892..62f92a50896120d72ed77cd677768acdce6ba36e 100644 (file)
@@ -2126,7 +2126,16 @@ static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
                goto unlock;
 
        if (list_empty(&discov->resolve)) {
-               hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+               /* When BR/EDR inquiry is active and no LE scanning is in
+                * progress, then change discovery state to indicate completion.
+                *
+                * When running LE scanning and BR/EDR inquiry simultaneously
+                * and the LE scan already finished, then change the discovery
+                * state to indicate completion.
+                */
+               if (!hci_dev_test_flag(hdev, HCI_LE_SCAN) ||
+                   !test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks))
+                       hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
                goto unlock;
        }
 
@@ -2135,7 +2144,16 @@ static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
                e->name_state = NAME_PENDING;
                hci_discovery_set_state(hdev, DISCOVERY_RESOLVING);
        } else {
-               hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+               /* When BR/EDR inquiry is active and no LE scanning is in
+                * progress, then change discovery state to indicate completion.
+                *
+                * When running LE scanning and BR/EDR inquiry simultaneously
+                * and the LE scan already finished, then change the discovery
+                * state to indicate completion.
+                */
+               if (!hci_dev_test_flag(hdev, HCI_LE_SCAN) ||
+                   !test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks))
+                       hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
        }
 
 unlock:
index 5a4b9d5a224fb89e5fc531510982d3ca93b166f0..7bcdf61afe11a83b474811d02074e50c44fc905f 100644 (file)
@@ -1408,9 +1408,10 @@ static bool hci_stop_discovery(struct hci_request *req)
 
        switch (hdev->discovery.state) {
        case DISCOVERY_FINDING:
-               if (test_bit(HCI_INQUIRY, &hdev->flags)) {
+               if (test_bit(HCI_INQUIRY, &hdev->flags))
                        hci_req_add(req, HCI_OP_INQUIRY_CANCEL, 0, NULL);
-               } else {
+
+               if (hci_dev_test_flag(hdev, HCI_LE_SCAN)) {
                        cancel_delayed_work(&hdev->le_scan_disable);
                        hci_req_add_le_scan_disable(req);
                }
@@ -4019,6 +4020,22 @@ static bool trigger_discovery(struct hci_request *req, u8 *status)
                break;
 
        case DISCOV_TYPE_INTERLEAVED:
+               if (test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY,
+                            &hdev->quirks)) {
+                       /* During simultaneous discovery, we double LE scan
+                        * interval. We must leave some time for the controller
+                        * to do BR/EDR inquiry.
+                        */
+                       if (!trigger_le_scan(req, DISCOV_LE_SCAN_INT * 2,
+                                            status))
+                               return false;
+
+                       if (!trigger_bredr_inquiry(req, status))
+                               return false;
+
+                       return true;
+               }
+
                if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) {
                        *status = MGMT_STATUS_NOT_SUPPORTED;
                        return false;
@@ -4072,7 +4089,18 @@ static void start_discovery_complete(struct hci_dev *hdev, u8 status,
                timeout = msecs_to_jiffies(DISCOV_LE_TIMEOUT);
                break;
        case DISCOV_TYPE_INTERLEAVED:
-               timeout = msecs_to_jiffies(hdev->discov_interleaved_timeout);
+                /* When running simultaneous discovery, the LE scanning time
+                * should occupy the whole discovery time sine BR/EDR inquiry
+                * and LE scanning are scheduled by the controller.
+                *
+                * For interleaving discovery in comparison, BR/EDR inquiry
+                * and LE scanning are done sequentially with separate
+                * timeouts.
+                */
+               if (test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks))
+                       timeout = msecs_to_jiffies(DISCOV_LE_TIMEOUT);
+               else
+                       timeout = msecs_to_jiffies(hdev->discov_interleaved_timeout);
                break;
        case DISCOV_TYPE_BREDR:
                timeout = 0;