Bluetooth: Fix advertising and active scanning co-existence
authorJohan Hedberg <johan.hedberg@intel.com>
Tue, 8 Jul 2014 12:07:53 +0000 (15:07 +0300)
committerMarcel Holtmann <marcel@holtmann.org>
Tue, 8 Jul 2014 12:22:06 +0000 (14:22 +0200)
Many controllers allow simultaneous active scanning and advertising
(e.g. Intel and Broadcom) but some do not (e.g. CSR). It's therefore
safest to implement mutual exclusion of these states in the kernel.

This patch ensures that the two states are never entered simultaneously.
Extra precaution needs to be taken for outgoing connection attempts in
slave role (i.e. through directed advertising) in which case the
operation that came first has precedence and the one that comes after
gets a rejection.

Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
net/bluetooth/hci_conn.c
net/bluetooth/hci_event.c
net/bluetooth/mgmt.c

index 1517f1549f85b0f0260d1cca89a326af916485df..490ee8846d9e21e910220df687907243dd855feb 100644 (file)
@@ -772,6 +772,16 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
 
        /* If requested to connect as slave use directed advertising */
        if (!master) {
+               /* If we're active scanning most controllers are unable
+                * to initiate advertising. Simply reject the attempt.
+                */
+               if (test_bit(HCI_LE_SCAN, &hdev->dev_flags) &&
+                   hdev->le_scan_type == LE_SCAN_ACTIVE) {
+                       skb_queue_purge(&req.cmd_q);
+                       hci_conn_del(conn);
+                       return ERR_PTR(-EBUSY);
+               }
+
                hci_req_directed_advertising(&req, conn);
                goto create_conn;
        }
index 8fbf604ba2289a11bc5800849bbc1407aa823d42..5d3095d7d4b0b56c0fb32201ef900230be69cdb1 100644 (file)
@@ -1176,13 +1176,21 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
                cancel_delayed_work(&hdev->le_scan_disable);
 
                clear_bit(HCI_LE_SCAN, &hdev->dev_flags);
+
                /* The HCI_LE_SCAN_INTERRUPTED flag indicates that we
                 * interrupted scanning due to a connect request. Mark
-                * therefore discovery as stopped.
+                * therefore discovery as stopped. If this was not
+                * because of a connect request advertising might have
+                * been disabled because of active scanning, so
+                * re-enable it again if necessary.
                 */
                if (test_and_clear_bit(HCI_LE_SCAN_INTERRUPTED,
                                       &hdev->dev_flags))
                        hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+               else if (!test_bit(HCI_LE_ADV, &hdev->dev_flags) &&
+                        hdev->discovery.state != DISCOVERY_STARTING)
+                       mgmt_reenable_advertising(hdev);
+
                break;
 
        default:
index 9549d7366da249a2a9e39da79ac8d5afa8a08f05..944e6463fd6148e6ddf567f740c4c52597aaeeac 100644 (file)
@@ -3726,11 +3726,21 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
                        goto failed;
                }
 
-               if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) {
-                       err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
-                                        MGMT_STATUS_REJECTED);
-                       mgmt_pending_remove(cmd);
-                       goto failed;
+               if (test_bit(HCI_LE_ADV, &hdev->dev_flags)) {
+                       /* Don't let discovery abort an outgoing
+                        * connection attempt that's using directed
+                        * advertising.
+                        */
+                       if (hci_conn_hash_lookup_state(hdev, LE_LINK,
+                                                      BT_CONNECT)) {
+                               err = cmd_status(sk, hdev->id,
+                                                MGMT_OP_START_DISCOVERY,
+                                                MGMT_STATUS_REJECTED);
+                               mgmt_pending_remove(cmd);
+                               goto failed;
+                       }
+
+                       disable_advertising(&req);
                }
 
                /* If controller is scanning, it means the background scanning
@@ -4078,7 +4088,9 @@ static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data,
         * necessary).
         */
        if (!hdev_is_powered(hdev) || val == enabled ||
-           hci_conn_num(hdev, LE_LINK) > 0) {
+           hci_conn_num(hdev, LE_LINK) > 0 ||
+           (test_bit(HCI_LE_SCAN, &hdev->dev_flags) &&
+            hdev->le_scan_type == LE_SCAN_ACTIVE)) {
                bool changed = false;
 
                if (val != test_bit(HCI_ADVERTISING, &hdev->dev_flags)) {