Merge tag 'usb-3.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
[firefly-linux-kernel-4.4.55.git] / net / bluetooth / mgmt.c
index ba3b4a5820b13794807dd4233a82e0d20514bce8..9ec5390c85eba61c3c3bcb5a813c8d8af326cf33 100644 (file)
@@ -3896,6 +3896,9 @@ static void start_discovery_complete(struct hci_dev *hdev, u8 status,
 
        hci_discovery_set_state(hdev, DISCOVERY_FINDING);
 
+       /* If the scan involves LE scan, pick proper timeout to schedule
+        * hdev->le_scan_disable that will stop it.
+        */
        switch (hdev->discovery.type) {
        case DISCOV_TYPE_LE:
                timeout = msecs_to_jiffies(DISCOV_LE_TIMEOUT);
@@ -3912,9 +3915,23 @@ static void start_discovery_complete(struct hci_dev *hdev, u8 status,
                break;
        }
 
-       if (timeout)
+       if (timeout) {
+               /* When service discovery is used and the controller has
+                * a strict duplicate filter, it is important to remember
+                * the start and duration of the scan. This is required
+                * for restarting scanning during the discovery phase.
+                */
+               if (test_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER,
+                            &hdev->quirks) &&
+                   (hdev->discovery.uuid_count > 0 ||
+                    hdev->discovery.rssi != HCI_RSSI_INVALID)) {
+                       hdev->discovery.scan_start = jiffies;
+                       hdev->discovery.scan_duration = timeout;
+               }
+
                queue_delayed_work(hdev->workqueue,
                                   &hdev->le_scan_disable, timeout);
+       }
 
 unlock:
        hci_dev_unlock(hdev);
@@ -7151,28 +7168,21 @@ void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
                cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
                           mgmt_status(status));
        } else {
-               if (bredr_sc_enabled(hdev) && hash256 && rand256) {
-                       struct mgmt_rp_read_local_oob_ext_data rp;
+               struct mgmt_rp_read_local_oob_data rp;
+               size_t rp_size = sizeof(rp);
 
-                       memcpy(rp.hash192, hash192, sizeof(rp.hash192));
-                       memcpy(rp.rand192, rand192, sizeof(rp.rand192));
+               memcpy(rp.hash192, hash192, sizeof(rp.hash192));
+               memcpy(rp.rand192, rand192, sizeof(rp.rand192));
 
+               if (bredr_sc_enabled(hdev) && hash256 && rand256) {
                        memcpy(rp.hash256, hash256, sizeof(rp.hash256));
                        memcpy(rp.rand256, rand256, sizeof(rp.rand256));
-
-                       cmd_complete(cmd->sk, hdev->id,
-                                    MGMT_OP_READ_LOCAL_OOB_DATA, 0,
-                                    &rp, sizeof(rp));
                } else {
-                       struct mgmt_rp_read_local_oob_data rp;
-
-                       memcpy(rp.hash, hash192, sizeof(rp.hash));
-                       memcpy(rp.rand, rand192, sizeof(rp.rand));
-
-                       cmd_complete(cmd->sk, hdev->id,
-                                    MGMT_OP_READ_LOCAL_OOB_DATA, 0,
-                                    &rp, sizeof(rp));
+                       rp_size -= sizeof(rp.hash256) + sizeof(rp.rand256);
                }
+
+               cmd_complete(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, 0,
+                            &rp, rp_size);
        }
 
        mgmt_pending_remove(cmd);
@@ -7245,6 +7255,21 @@ static bool eir_has_uuids(u8 *eir, u16 eir_len, u16 uuid_count, u8 (*uuids)[16])
        return false;
 }
 
+static void restart_le_scan(struct hci_dev *hdev)
+{
+       /* If controller is not scanning we are done. */
+       if (!test_bit(HCI_LE_SCAN, &hdev->dev_flags))
+               return;
+
+       if (time_after(jiffies + DISCOV_LE_RESTART_DELAY,
+                      hdev->discovery.scan_start +
+                      hdev->discovery.scan_duration))
+               return;
+
+       queue_delayed_work(hdev->workqueue, &hdev->le_scan_restart,
+                          DISCOV_LE_RESTART_DELAY);
+}
+
 void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
                       u8 addr_type, u8 *dev_class, s8 rssi, u32 flags,
                       u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len)
@@ -7267,14 +7292,18 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
 
        /* When using service discovery with a RSSI threshold, then check
         * if such a RSSI threshold is specified. If a RSSI threshold has
-        * been specified, then all results with a RSSI smaller than the
-        * RSSI threshold will be dropped.
+        * been specified, and HCI_QUIRK_STRICT_DUPLICATE_FILTER is not set,
+        * then all results with a RSSI smaller than the RSSI threshold will be
+        * dropped. If the quirk is set, let it through for further processing,
+        * as we might need to restart the scan.
         *
         * For BR/EDR devices (pre 1.2) providing no RSSI during inquiry,
         * the results are also dropped.
         */
        if (hdev->discovery.rssi != HCI_RSSI_INVALID &&
-           (rssi < hdev->discovery.rssi || rssi == HCI_RSSI_INVALID))
+           (rssi == HCI_RSSI_INVALID ||
+           (rssi < hdev->discovery.rssi &&
+            !test_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks))))
                return;
 
        /* Make sure that the buffer is big enough. The 5 extra bytes
@@ -7309,12 +7338,20 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
                 * kept and checking possible scan response data
                 * will be skipped.
                 */
-               if (hdev->discovery.uuid_count > 0)
+               if (hdev->discovery.uuid_count > 0) {
                        match = eir_has_uuids(eir, eir_len,
                                              hdev->discovery.uuid_count,
                                              hdev->discovery.uuids);
-               else
+                       /* If duplicate filtering does not report RSSI changes,
+                        * then restart scanning to ensure updated result with
+                        * updated RSSI values.
+                        */
+                       if (match && test_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER,
+                                             &hdev->quirks))
+                               restart_le_scan(hdev);
+               } else {
                        match = true;
+               }
 
                if (!match && !scan_rsp_len)
                        return;
@@ -7347,6 +7384,14 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
                                                     hdev->discovery.uuid_count,
                                                     hdev->discovery.uuids))
                                return;
+
+                       /* If duplicate filtering does not report RSSI changes,
+                        * then restart scanning to ensure updated result with
+                        * updated RSSI values.
+                        */
+                       if (test_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER,
+                                    &hdev->quirks))
+                               restart_le_scan(hdev);
                }
 
                /* Append scan response data to event */
@@ -7360,6 +7405,14 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
                        return;
        }
 
+       /* Validate the reported RSSI value against the RSSI threshold once more
+        * incase HCI_QUIRK_STRICT_DUPLICATE_FILTER forced a restart of LE
+        * scanning.
+        */
+       if (hdev->discovery.rssi != HCI_RSSI_INVALID &&
+           rssi < hdev->discovery.rssi)
+               return;
+
        ev->eir_len = cpu_to_le16(eir_len + scan_rsp_len);
        ev_size = sizeof(*ev) + eir_len + scan_rsp_len;