Bluetooth: Configure controller address resolution if available
[linux-2.6-microblaze.git] / net / bluetooth / hci_request.c
index 649e1e5..7d0ba53 100644 (file)
@@ -34,9 +34,6 @@
 #define HCI_REQ_PEND     1
 #define HCI_REQ_CANCELED  2
 
-#define LE_SUSPEND_SCAN_WINDOW         0x0012
-#define LE_SUSPEND_SCAN_INTERVAL       0x0060
-
 void hci_req_init(struct hci_request *req, struct hci_dev *hdev)
 {
        skb_queue_head_init(&req->cmd_q);
@@ -366,13 +363,11 @@ void __hci_req_write_fast_connectable(struct hci_request *req, bool enable)
                /* 160 msec page scan interval */
                acp.interval = cpu_to_le16(0x0100);
        } else {
-               type = PAGE_SCAN_TYPE_STANDARD; /* default */
-
-               /* default 1.28 sec page scan */
-               acp.interval = cpu_to_le16(0x0800);
+               type = hdev->def_page_scan_type;
+               acp.interval = cpu_to_le16(hdev->def_page_scan_int);
        }
 
-       acp.window = cpu_to_le16(0x0012);
+       acp.window = cpu_to_le16(hdev->def_page_scan_window);
 
        if (__cpu_to_le16(hdev->page_scan_interval) != acp.interval ||
            __cpu_to_le16(hdev->page_scan_window) != acp.window)
@@ -418,11 +413,15 @@ static void __hci_update_background_scan(struct hci_request *req)
         */
        hci_discovery_filter_clear(hdev);
 
+       BT_DBG("%s ADV monitoring is %s", hdev->name,
+              hci_is_adv_monitoring(hdev) ? "on" : "off");
+
        if (list_empty(&hdev->pend_le_conns) &&
-           list_empty(&hdev->pend_le_reports)) {
+           list_empty(&hdev->pend_le_reports) &&
+           !hci_is_adv_monitoring(hdev)) {
                /* If there is no pending LE connections or devices
-                * to be scanned for, we should stop the background
-                * scanning.
+                * to be scanned for or no ADV monitors, we should stop the
+                * background scanning.
                 */
 
                /* If controller is not scanning we are done. */
@@ -676,6 +675,12 @@ void hci_req_add_le_scan_disable(struct hci_request *req)
                cp.enable = LE_SCAN_DISABLE;
                hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
        }
+
+       if (use_ll_privacy(hdev) &&
+           hci_dev_test_flag(hdev, HCI_LL_RPA_RESOLUTION)) {
+               __u8 enable = 0x00;
+               hci_req_add(req, HCI_OP_LE_SET_ADDR_RESOLV_ENABLE, 1, &enable);
+       }
 }
 
 static void del_from_white_list(struct hci_request *req, bdaddr_t *bdaddr,
@@ -715,7 +720,8 @@ static int add_to_white_list(struct hci_request *req,
        }
 
        /* During suspend, only wakeable devices can be in whitelist */
-       if (hdev->suspended && !params->wakeable)
+       if (hdev->suspended && !hci_conn_test_flag(HCI_CONN_FLAG_REMOTE_WAKEUP,
+                                                  params->current_flags))
                return 0;
 
        *num_entries += 1;
@@ -798,6 +804,14 @@ static u8 update_white_list(struct hci_request *req)
                        return 0x00;
        }
 
+       /* Once the controller offloading of advertisement monitor is in place,
+        * the if condition should include the support of MSFT extension
+        * support. If suspend is ongoing, whitelist should be the default to
+        * prevent waking by random advertisements.
+        */
+       if (!idr_is_empty(&hdev->adv_monitors_idr) && !hdev->suspended)
+               return 0x00;
+
        /* Select filter policy to use white list */
        return 0x01;
 }
@@ -808,10 +822,21 @@ static bool scan_use_rpa(struct hci_dev *hdev)
 }
 
 static void hci_req_start_scan(struct hci_request *req, u8 type, u16 interval,
-                              u16 window, u8 own_addr_type, u8 filter_policy)
+                              u16 window, u8 own_addr_type, u8 filter_policy,
+                              bool addr_resolv)
 {
        struct hci_dev *hdev = req->hdev;
 
+       if (hdev->scanning_paused) {
+               bt_dev_dbg(hdev, "Scanning is paused for suspend");
+               return;
+       }
+
+       if (use_ll_privacy(hdev) && addr_resolv) {
+               u8 enable = 0x01;
+               hci_req_add(req, HCI_OP_LE_SET_ADDR_RESOLV_ENABLE, 1, &enable);
+       }
+
        /* Use ext scanning if set ext scan param and ext scan enable is
         * supported
         */
@@ -885,12 +910,18 @@ static void hci_req_start_scan(struct hci_request *req, u8 type, u16 interval,
        }
 }
 
+/* Ensure to call hci_req_add_le_scan_disable() first to disable the
+ * controller based address resolution to be able to reconfigure
+ * resolving list.
+ */
 void hci_req_add_le_passive_scan(struct hci_request *req)
 {
        struct hci_dev *hdev = req->hdev;
        u8 own_addr_type;
        u8 filter_policy;
-       u8 window, interval;
+       u16 window, interval;
+       /* Background scanning should run with address resolution */
+       bool addr_resolv = true;
 
        if (hdev->scanning_paused) {
                bt_dev_dbg(hdev, "Scanning is paused for suspend");
@@ -927,8 +958,8 @@ void hci_req_add_le_passive_scan(struct hci_request *req)
                filter_policy |= 0x02;
 
        if (hdev->suspended) {
-               window = LE_SUSPEND_SCAN_WINDOW;
-               interval = LE_SUSPEND_SCAN_INTERVAL;
+               window = hdev->le_scan_window_suspend;
+               interval = hdev->le_scan_int_suspend;
        } else {
                window = hdev->le_scan_window;
                interval = hdev->le_scan_interval;
@@ -936,7 +967,7 @@ void hci_req_add_le_passive_scan(struct hci_request *req)
 
        bt_dev_dbg(hdev, "LE passive scan with whitelist = %d", filter_policy);
        hci_req_start_scan(req, LE_SCAN_PASSIVE, interval, window,
-                          own_addr_type, filter_policy);
+                          own_addr_type, filter_policy, addr_resolv);
 }
 
 static u8 get_adv_instance_scan_rsp_len(struct hci_dev *hdev, u8 instance)
@@ -973,15 +1004,19 @@ static void hci_req_clear_event_filter(struct hci_request *req)
 
 static void hci_req_set_event_filter(struct hci_request *req)
 {
-       struct bdaddr_list *b;
+       struct bdaddr_list_with_flags *b;
        struct hci_cp_set_event_filter f;
        struct hci_dev *hdev = req->hdev;
-       u8 scan;
+       u8 scan = SCAN_DISABLED;
 
        /* Always clear event filter when starting */
        hci_req_clear_event_filter(req);
 
-       list_for_each_entry(b, &hdev->wakeable, list) {
+       list_for_each_entry(b, &hdev->whitelist, list) {
+               if (!hci_conn_test_flag(HCI_CONN_FLAG_REMOTE_WAKEUP,
+                                       b->current_flags))
+                       continue;
+
                memset(&f, 0, sizeof(f));
                bacpy(&f.addr_conn_flt.bdaddr, &b->bdaddr);
                f.flt_type = HCI_FLT_CONN_SETUP;
@@ -990,16 +1025,17 @@ static void hci_req_set_event_filter(struct hci_request *req)
 
                bt_dev_dbg(hdev, "Adding event filters for %pMR", &b->bdaddr);
                hci_req_add(req, HCI_OP_SET_EVENT_FLT, sizeof(f), &f);
+               scan = SCAN_PAGE;
        }
 
-       scan = !list_empty(&hdev->wakeable) ? SCAN_PAGE : SCAN_DISABLED;
        hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
 }
 
 static void hci_req_config_le_suspend_scan(struct hci_request *req)
 {
-       /* Can't change params without disabling first */
-       hci_req_add_le_scan_disable(req);
+       /* Before changing params disable scan if enabled */
+       if (hci_dev_test_flag(req->hdev, HCI_LE_SCAN))
+               hci_req_add_le_scan_disable(req);
 
        /* Configure params and enable scanning */
        hci_req_add_le_passive_scan(req);
@@ -1065,8 +1101,9 @@ void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next)
                page_scan = SCAN_DISABLED;
                hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &page_scan);
 
-               /* Disable LE passive scan */
-               hci_req_add_le_scan_disable(&req);
+               /* Disable LE passive scan if enabled */
+               if (hci_dev_test_flag(hdev, HCI_LE_SCAN))
+                       hci_req_add_le_scan_disable(&req);
 
                /* Mark task needing completion */
                set_bit(SUSPEND_SCAN_DISABLE, hdev->suspend_tasks);
@@ -1090,7 +1127,7 @@ void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next)
                                   disconnect_counter);
                        set_bit(SUSPEND_DISCONNECTING, hdev->suspend_tasks);
                }
-       } else if (next == BT_SUSPEND_COMPLETE) {
+       } else if (next == BT_SUSPEND_CONFIGURE_WAKE) {
                /* Unpause to take care of updating scanning params */
                hdev->scanning_paused = false;
                /* Enable event filter for paired devices */
@@ -1160,13 +1197,8 @@ static u8 get_cur_adv_instance_scan_rsp_len(struct hci_dev *hdev)
 void __hci_req_disable_advertising(struct hci_request *req)
 {
        if (ext_adv_capable(req->hdev)) {
-               struct hci_cp_le_set_ext_adv_enable cp;
-
-               cp.enable = 0x00;
-               /* Disable all sets since we only support one set at the moment */
-               cp.num_of_sets = 0x00;
+               __hci_req_disable_ext_adv_instance(req, 0x00);
 
-               hci_req_add(req, HCI_OP_LE_SET_EXT_ADV_ENABLE, sizeof(cp), &cp);
        } else {
                u8 enable = 0x00;
 
@@ -1447,7 +1479,7 @@ void __hci_req_update_scan_rsp_data(struct hci_request *req, u8 instance)
                memcpy(hdev->scan_rsp_data, cp.data, sizeof(cp.data));
                hdev->scan_rsp_data_len = len;
 
-               cp.handle = 0;
+               cp.handle = instance;
                cp.length = len;
                cp.operation = LE_SET_ADV_DATA_OP_COMPLETE;
                cp.frag_pref = LE_SET_ADV_DATA_NO_FRAG;
@@ -1591,7 +1623,7 @@ void __hci_req_update_adv_data(struct hci_request *req, u8 instance)
                hdev->adv_data_len = len;
 
                cp.length = len;
-               cp.handle = 0;
+               cp.handle = instance;
                cp.operation = LE_SET_ADV_DATA_OP_COMPLETE;
                cp.frag_pref = LE_SET_ADV_DATA_NO_FRAG;
 
@@ -1786,8 +1818,6 @@ int __hci_req_setup_ext_adv_instance(struct hci_request *req, u8 instance)
        int err;
        struct adv_info *adv_instance;
        bool secondary_adv;
-       /* In ext adv set param interval is 3 octets */
-       const u8 adv_interval[3] = { 0x00, 0x08, 0x00 };
 
        if (instance > 0) {
                adv_instance = hci_find_adv_instance(hdev, instance);
@@ -1820,8 +1850,9 @@ int __hci_req_setup_ext_adv_instance(struct hci_request *req, u8 instance)
 
        memset(&cp, 0, sizeof(cp));
 
-       memcpy(cp.min_interval, adv_interval, sizeof(cp.min_interval));
-       memcpy(cp.max_interval, adv_interval, sizeof(cp.max_interval));
+       /* In ext adv set param interval is 3 octets */
+       hci_cpu_to_le24(hdev->le_adv_min_interval, cp.min_interval);
+       hci_cpu_to_le24(hdev->le_adv_max_interval, cp.max_interval);
 
        secondary_adv = (flags & MGMT_ADV_FLAG_SEC_MASK);
 
@@ -1876,7 +1907,7 @@ int __hci_req_setup_ext_adv_instance(struct hci_request *req, u8 instance)
 
                memset(&cp, 0, sizeof(cp));
 
-               cp.handle = 0;
+               cp.handle = instance;
                bacpy(&cp.bdaddr, &random_addr);
 
                hci_req_add(req,
@@ -1932,13 +1963,59 @@ int __hci_req_enable_ext_advertising(struct hci_request *req, u8 instance)
        return 0;
 }
 
+int __hci_req_disable_ext_adv_instance(struct hci_request *req, u8 instance)
+{
+       struct hci_dev *hdev = req->hdev;
+       struct hci_cp_le_set_ext_adv_enable *cp;
+       struct hci_cp_ext_adv_set *adv_set;
+       u8 data[sizeof(*cp) + sizeof(*adv_set) * 1];
+       u8 req_size;
+
+       /* If request specifies an instance that doesn't exist, fail */
+       if (instance > 0 && !hci_find_adv_instance(hdev, instance))
+               return -EINVAL;
+
+       memset(data, 0, sizeof(data));
+
+       cp = (void *)data;
+       adv_set = (void *)cp->data;
+
+       /* Instance 0x00 indicates all advertising instances will be disabled */
+       cp->num_of_sets = !!instance;
+       cp->enable = 0x00;
+
+       adv_set->handle = instance;
+
+       req_size = sizeof(*cp) + sizeof(*adv_set) * cp->num_of_sets;
+       hci_req_add(req, HCI_OP_LE_SET_EXT_ADV_ENABLE, req_size, data);
+
+       return 0;
+}
+
+int __hci_req_remove_ext_adv_instance(struct hci_request *req, u8 instance)
+{
+       struct hci_dev *hdev = req->hdev;
+
+       /* If request specifies an instance that doesn't exist, fail */
+       if (instance > 0 && !hci_find_adv_instance(hdev, instance))
+               return -EINVAL;
+
+       hci_req_add(req, HCI_OP_LE_REMOVE_ADV_SET, sizeof(instance), &instance);
+
+       return 0;
+}
+
 int __hci_req_start_ext_adv(struct hci_request *req, u8 instance)
 {
        struct hci_dev *hdev = req->hdev;
+       struct adv_info *adv_instance = hci_find_adv_instance(hdev, instance);
        int err;
 
-       if (hci_dev_test_flag(hdev, HCI_LE_ADV))
-               __hci_req_disable_advertising(req);
+       /* If instance isn't pending, the chip knows about it, and it's safe to
+        * disable
+        */
+       if (adv_instance && !adv_instance->pending)
+               __hci_req_disable_ext_adv_instance(req, instance);
 
        err = __hci_req_setup_ext_adv_instance(req, instance);
        if (err < 0)
@@ -2086,7 +2163,7 @@ void hci_req_clear_adv_instance(struct hci_dev *hdev, struct sock *sk,
            hci_dev_test_flag(hdev, HCI_ADVERTISING))
                return;
 
-       if (next_instance)
+       if (next_instance && !ext_adv_capable(hdev))
                __hci_req_schedule_adv_instance(req, next_instance->instance,
                                                false);
 }
@@ -2645,6 +2722,11 @@ static int le_scan_restart(struct hci_request *req, unsigned long opt)
        if (!hci_dev_test_flag(hdev, HCI_LE_SCAN))
                return 0;
 
+       if (hdev->scanning_paused) {
+               bt_dev_dbg(hdev, "Scanning is paused for suspend");
+               return 0;
+       }
+
        hci_req_add_le_scan_disable(req);
 
        if (use_ext_scan(hdev)) {
@@ -2723,6 +2805,10 @@ static int active_scan(struct hci_request *req, unsigned long opt)
        uint16_t interval = opt;
        struct hci_dev *hdev = req->hdev;
        u8 own_addr_type;
+       /* White list is not used for discovery */
+       u8 filter_policy = 0x00;
+       /* Discovery doesn't require controller address resolution */
+       bool addr_resolv = false;
        int err;
 
        BT_DBG("%s", hdev->name);
@@ -2743,8 +2829,9 @@ static int active_scan(struct hci_request *req, unsigned long opt)
        if (err < 0)
                own_addr_type = ADDR_LE_DEV_PUBLIC;
 
-       hci_req_start_scan(req, LE_SCAN_ACTIVE, interval, DISCOV_LE_SCAN_WIN,
-                          own_addr_type, 0);
+       hci_req_start_scan(req, LE_SCAN_ACTIVE, interval,
+                          hdev->le_scan_window_discovery, own_addr_type,
+                          filter_policy, addr_resolv);
        return 0;
 }
 
@@ -2791,18 +2878,18 @@ static void start_discovery(struct hci_dev *hdev, u8 *status)
                         * to do BR/EDR inquiry.
                         */
                        hci_req_sync(hdev, interleaved_discov,
-                                    DISCOV_LE_SCAN_INT * 2, HCI_CMD_TIMEOUT,
+                                    hdev->le_scan_int_discovery * 2, HCI_CMD_TIMEOUT,
                                     status);
                        break;
                }
 
                timeout = msecs_to_jiffies(hdev->discov_interleaved_timeout);
-               hci_req_sync(hdev, active_scan, DISCOV_LE_SCAN_INT,
+               hci_req_sync(hdev, active_scan, hdev->le_scan_int_discovery,
                             HCI_CMD_TIMEOUT, status);
                break;
        case DISCOV_TYPE_LE:
                timeout = msecs_to_jiffies(DISCOV_LE_TIMEOUT);
-               hci_req_sync(hdev, active_scan, DISCOV_LE_SCAN_INT,
+               hci_req_sync(hdev, active_scan, hdev->le_scan_int_discovery,
                             HCI_CMD_TIMEOUT, status);
                break;
        default: