Bluetooth: Move Start Discovery to req_workqueue
authorJohan Hedberg <johan.hedberg@intel.com>
Wed, 11 Nov 2015 06:30:30 +0000 (08:30 +0200)
committerMarcel Holtmann <marcel@holtmann.org>
Thu, 19 Nov 2015 16:50:30 +0000 (17:50 +0100)
Since discovery also deals with LE scanning it makes sense to move it
behind the same req_workqueue as other LE scanning changes. This also
simplifies the logic since we do many of the actions in a synchronous
manner.

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

index 1f75aebbd8c41f15b20d1f2e1f91377e20ace41a..72ea8a6d7d706836d3b56ce293f268d0db9d7ddd 100644 (file)
@@ -327,6 +327,7 @@ struct hci_dev {
        struct work_struct      cmd_work;
        struct work_struct      tx_work;
 
+       struct work_struct      discov_update;
        struct work_struct      bg_scan_update;
        struct delayed_work     le_scan_disable;
        struct delayed_work     le_scan_restart;
@@ -1473,6 +1474,7 @@ void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status);
 void mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
                                    u8 status);
 void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status);
+void mgmt_start_discovery_complete(struct hci_dev *hdev, u8 status);
 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);
index ecfa4105e00d7302cd980bead9dc9defc3da68e4..da1e30b85e770ef483ead7a690ccaeeb73c5f2d0 100644 (file)
@@ -1042,8 +1042,209 @@ static void le_scan_restart_work(struct work_struct *work)
        le_scan_restart_work_complete(hdev, status);
 }
 
+static int bredr_inquiry(struct hci_request *req, unsigned long opt)
+{
+       struct hci_cp_inquiry cp;
+       /* General inquiry access code (GIAC) */
+       u8 lap[3] = { 0x33, 0x8b, 0x9e };
+
+       BT_DBG("%s", req->hdev->name);
+
+       hci_dev_lock(req->hdev);
+       hci_inquiry_cache_flush(req->hdev);
+       hci_dev_unlock(req->hdev);
+
+       memset(&cp, 0, sizeof(cp));
+       memcpy(&cp.lap, lap, sizeof(cp.lap));
+       cp.length = DISCOV_BREDR_INQUIRY_LEN;
+
+       hci_req_add(req, HCI_OP_INQUIRY, sizeof(cp), &cp);
+
+       return 0;
+}
+
+static void cancel_adv_timeout(struct hci_dev *hdev)
+{
+       if (hdev->adv_instance_timeout) {
+               hdev->adv_instance_timeout = 0;
+               cancel_delayed_work(&hdev->adv_instance_expire);
+       }
+}
+
+static void disable_advertising(struct hci_request *req)
+{
+       u8 enable = 0x00;
+
+       hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
+}
+
+static int active_scan(struct hci_request *req, unsigned long opt)
+{
+       uint16_t interval = opt;
+       struct hci_dev *hdev = req->hdev;
+       struct hci_cp_le_set_scan_param param_cp;
+       struct hci_cp_le_set_scan_enable enable_cp;
+       u8 own_addr_type;
+       int err;
+
+       BT_DBG("%s", hdev->name);
+
+       if (hci_dev_test_flag(hdev, HCI_LE_ADV)) {
+               hci_dev_lock(hdev);
+
+               /* Don't let discovery abort an outgoing connection attempt
+                * that's using directed advertising.
+                */
+               if (hci_lookup_le_connect(hdev)) {
+                       hci_dev_unlock(hdev);
+                       return -EBUSY;
+               }
+
+               cancel_adv_timeout(hdev);
+               hci_dev_unlock(hdev);
+
+               disable_advertising(req);
+       }
+
+       /* If controller is scanning, it means the background scanning is
+        * running. Thus, we should temporarily stop it in order to set the
+        * discovery scanning parameters.
+        */
+       if (hci_dev_test_flag(hdev, HCI_LE_SCAN))
+               hci_req_add_le_scan_disable(req);
+
+       /* All active scans will be done with either a resolvable private
+        * address (when privacy feature has been enabled) or non-resolvable
+        * private address.
+        */
+       err = hci_update_random_address(req, true, &own_addr_type);
+       if (err < 0)
+               own_addr_type = ADDR_LE_DEV_PUBLIC;
+
+       memset(&param_cp, 0, sizeof(param_cp));
+       param_cp.type = LE_SCAN_ACTIVE;
+       param_cp.interval = cpu_to_le16(interval);
+       param_cp.window = cpu_to_le16(DISCOV_LE_SCAN_WIN);
+       param_cp.own_address_type = own_addr_type;
+
+       hci_req_add(req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp),
+                   &param_cp);
+
+       memset(&enable_cp, 0, sizeof(enable_cp));
+       enable_cp.enable = LE_SCAN_ENABLE;
+       enable_cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
+
+       hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(enable_cp),
+                   &enable_cp);
+
+       return 0;
+}
+
+static int interleaved_discov(struct hci_request *req, unsigned long opt)
+{
+       int err;
+
+       BT_DBG("%s", req->hdev->name);
+
+       err = active_scan(req, opt);
+       if (err)
+               return err;
+
+       return bredr_inquiry(req, opt);
+}
+
+static void start_discovery(struct hci_dev *hdev, u8 *status)
+{
+       unsigned long timeout;
+
+       BT_DBG("%s type %u", hdev->name, hdev->discovery.type);
+
+       switch (hdev->discovery.type) {
+       case DISCOV_TYPE_BREDR:
+               if (!hci_dev_test_flag(hdev, HCI_INQUIRY))
+                       hci_req_sync(hdev, bredr_inquiry, 0, HCI_CMD_TIMEOUT,
+                                    status);
+               return;
+       case DISCOV_TYPE_INTERLEAVED:
+               /* 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);
+                       /* During simultaneous discovery, we double LE scan
+                        * interval. We must leave some time for the controller
+                        * to do BR/EDR inquiry.
+                        */
+                       hci_req_sync(hdev, interleaved_discov,
+                                    DISCOV_LE_SCAN_INT * 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_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_CMD_TIMEOUT, status);
+               break;
+       default:
+               *status = HCI_ERROR_UNSPECIFIED;
+               return;
+       }
+
+       if (*status)
+               return;
+
+       BT_DBG("%s timeout %u ms", hdev->name, jiffies_to_msecs(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.result_filtering) {
+               hdev->discovery.scan_start = jiffies;
+               hdev->discovery.scan_duration = timeout;
+       }
+
+       queue_delayed_work(hdev->req_workqueue, &hdev->le_scan_disable,
+                          timeout);
+}
+
+static void discov_update(struct work_struct *work)
+{
+       struct hci_dev *hdev = container_of(work, struct hci_dev,
+                                           discov_update);
+       u8 status = 0;
+
+       switch (hdev->discovery.state) {
+       case DISCOVERY_STARTING:
+               start_discovery(hdev, &status);
+               mgmt_start_discovery_complete(hdev, status);
+               if (status)
+                       hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+               else
+                       hci_discovery_set_state(hdev, DISCOVERY_FINDING);
+               break;
+       case DISCOVERY_STOPPED:
+       default:
+               return;
+       }
+}
+
 void hci_request_setup(struct hci_dev *hdev)
 {
+       INIT_WORK(&hdev->discov_update, discov_update);
        INIT_WORK(&hdev->bg_scan_update, bg_scan_update);
        INIT_DELAYED_WORK(&hdev->le_scan_disable, le_scan_disable_work);
        INIT_DELAYED_WORK(&hdev->le_scan_restart, le_scan_restart_work);
@@ -1051,6 +1252,7 @@ void hci_request_setup(struct hci_dev *hdev)
 
 void hci_request_cancel_all(struct hci_dev *hdev)
 {
+       cancel_work_sync(&hdev->discov_update);
        cancel_work_sync(&hdev->bg_scan_update);
        cancel_delayed_work_sync(&hdev->le_scan_disable);
        cancel_delayed_work_sync(&hdev->le_scan_restart);
index e634b4d85249170050a692fb6de3c43c14e919f1..63b099471c92fd575be89d1abdc439c79ff93431 100644 (file)
@@ -4164,145 +4164,9 @@ done:
        return err;
 }
 
-static bool trigger_bredr_inquiry(struct hci_request *req, u8 *status)
-{
-       struct hci_dev *hdev = req->hdev;
-       struct hci_cp_inquiry cp;
-       /* General inquiry access code (GIAC) */
-       u8 lap[3] = { 0x33, 0x8b, 0x9e };
-
-       *status = mgmt_bredr_support(hdev);
-       if (*status)
-               return false;
-
-       if (hci_dev_test_flag(hdev, HCI_INQUIRY)) {
-               *status = MGMT_STATUS_BUSY;
-               return false;
-       }
-
-       hci_inquiry_cache_flush(hdev);
-
-       memset(&cp, 0, sizeof(cp));
-       memcpy(&cp.lap, lap, sizeof(cp.lap));
-       cp.length = DISCOV_BREDR_INQUIRY_LEN;
-
-       hci_req_add(req, HCI_OP_INQUIRY, sizeof(cp), &cp);
-
-       return true;
-}
-
-static bool trigger_le_scan(struct hci_request *req, u16 interval, u8 *status)
-{
-       struct hci_dev *hdev = req->hdev;
-       struct hci_cp_le_set_scan_param param_cp;
-       struct hci_cp_le_set_scan_enable enable_cp;
-       u8 own_addr_type;
-       int err;
-
-       *status = mgmt_le_support(hdev);
-       if (*status)
-               return false;
-
-       if (hci_dev_test_flag(hdev, HCI_LE_ADV)) {
-               /* Don't let discovery abort an outgoing connection attempt
-                * that's using directed advertising.
-                */
-               if (hci_lookup_le_connect(hdev)) {
-                       *status = MGMT_STATUS_REJECTED;
-                       return false;
-               }
-
-               cancel_adv_timeout(hdev);
-               disable_advertising(req);
-       }
-
-       /* If controller is scanning, it means the background scanning is
-        * running. Thus, we should temporarily stop it in order to set the
-        * discovery scanning parameters.
-        */
-       if (hci_dev_test_flag(hdev, HCI_LE_SCAN))
-               hci_req_add_le_scan_disable(req);
-
-       /* All active scans will be done with either a resolvable private
-        * address (when privacy feature has been enabled) or non-resolvable
-        * private address.
-        */
-       err = hci_update_random_address(req, true, &own_addr_type);
-       if (err < 0) {
-               *status = MGMT_STATUS_FAILED;
-               return false;
-       }
-
-       memset(&param_cp, 0, sizeof(param_cp));
-       param_cp.type = LE_SCAN_ACTIVE;
-       param_cp.interval = cpu_to_le16(interval);
-       param_cp.window = cpu_to_le16(DISCOV_LE_SCAN_WIN);
-       param_cp.own_address_type = own_addr_type;
-
-       hci_req_add(req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp),
-                   &param_cp);
-
-       memset(&enable_cp, 0, sizeof(enable_cp));
-       enable_cp.enable = LE_SCAN_ENABLE;
-       enable_cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
-
-       hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(enable_cp),
-                   &enable_cp);
-
-       return true;
-}
-
-static bool trigger_discovery(struct hci_request *req, u8 *status)
-{
-       struct hci_dev *hdev = req->hdev;
-
-       switch (hdev->discovery.type) {
-       case DISCOV_TYPE_BREDR:
-               if (!trigger_bredr_inquiry(req, status))
-                       return false;
-               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;
-               }
-               /* fall through */
-
-       case DISCOV_TYPE_LE:
-               if (!trigger_le_scan(req, DISCOV_LE_SCAN_INT, status))
-                       return false;
-               break;
-
-       default:
-               *status = MGMT_STATUS_INVALID_PARAMS;
-               return false;
-       }
-
-       return true;
-}
-
-static void start_discovery_complete(struct hci_dev *hdev, u8 status,
-                                    u16 opcode)
+void mgmt_start_discovery_complete(struct hci_dev *hdev, u8 status)
 {
        struct mgmt_pending_cmd *cmd;
-       unsigned long timeout;
 
        BT_DBG("status %d", status);
 
@@ -4317,61 +4181,6 @@ static void start_discovery_complete(struct hci_dev *hdev, u8 status,
                mgmt_pending_remove(cmd);
        }
 
-       if (status) {
-               hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
-               goto unlock;
-       }
-
-       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);
-               break;
-       case DISCOV_TYPE_INTERLEAVED:
-                /* 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;
-               break;
-       default:
-               BT_ERR("Invalid discovery type %d", hdev->discovery.type);
-               timeout = 0;
-               break;
-       }
-
-       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.result_filtering) {
-                       hdev->discovery.scan_start = jiffies;
-                       hdev->discovery.scan_duration = timeout;
-               }
-
-               queue_delayed_work(hdev->req_workqueue,
-                                  &hdev->le_scan_disable, timeout);
-       }
-
-unlock:
        hci_dev_unlock(hdev);
 }
 
@@ -4407,7 +4216,6 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
 {
        struct mgmt_cp_start_discovery *cp = data;
        struct mgmt_pending_cmd *cmd;
-       struct hci_request req;
        u8 status;
        int err;
 
@@ -4436,14 +4244,6 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
                goto failed;
        }
 
-       cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, hdev, data, len);
-       if (!cmd) {
-               err = -ENOMEM;
-               goto failed;
-       }
-
-       cmd->cmd_complete = generic_cmd_complete;
-
        /* Clear the discovery filter first to free any previously
         * allocated memory for the UUID list.
         */
@@ -4452,22 +4252,17 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
        hdev->discovery.type = cp->type;
        hdev->discovery.report_invalid_rssi = false;
 
-       hci_req_init(&req, hdev);
-
-       if (!trigger_discovery(&req, &status)) {
-               err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY,
-                                       status, &cp->type, sizeof(cp->type));
-               mgmt_pending_remove(cmd);
+       cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, hdev, data, len);
+       if (!cmd) {
+               err = -ENOMEM;
                goto failed;
        }
 
-       err = hci_req_run(&req, start_discovery_complete);
-       if (err < 0) {
-               mgmt_pending_remove(cmd);
-               goto failed;
-       }
+       cmd->cmd_complete = generic_cmd_complete;
 
        hci_discovery_set_state(hdev, DISCOVERY_STARTING);
+       queue_work(hdev->req_workqueue, &hdev->discov_update);
+       err = 0;
 
 failed:
        hci_dev_unlock(hdev);
@@ -4486,7 +4281,6 @@ static int start_service_discovery(struct sock *sk, struct hci_dev *hdev,
 {
        struct mgmt_cp_start_service_discovery *cp = data;
        struct mgmt_pending_cmd *cmd;
-       struct hci_request req;
        const u16 max_uuid_count = ((U16_MAX - sizeof(*cp)) / 16);
        u16 uuid_count, expected_len;
        u8 status;
@@ -4574,23 +4368,9 @@ static int start_service_discovery(struct sock *sk, struct hci_dev *hdev,
                }
        }
 
-       hci_req_init(&req, hdev);
-
-       if (!trigger_discovery(&req, &status)) {
-               err = mgmt_cmd_complete(sk, hdev->id,
-                                       MGMT_OP_START_SERVICE_DISCOVERY,
-                                       status, &cp->type, sizeof(cp->type));
-               mgmt_pending_remove(cmd);
-               goto failed;
-       }
-
-       err = hci_req_run(&req, start_discovery_complete);
-       if (err < 0) {
-               mgmt_pending_remove(cmd);
-               goto failed;
-       }
-
        hci_discovery_set_state(hdev, DISCOVERY_STARTING);
+       queue_work(hdev->req_workqueue, &hdev->discov_update);
+       err = 0;
 
 failed:
        hci_dev_unlock(hdev);