]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - net/bluetooth/mgmt.c
Bluetooth: Use HCI request in interleaved discovery
[mirror_ubuntu-bionic-kernel.git] / net / bluetooth / mgmt.c
index 35fef22703e9dc3661c88d3adb5b5abc9532c463..743100f3ab9c431c016611d7e5b274120d7d1d96 100644 (file)
@@ -102,18 +102,6 @@ static const u16 mgmt_events[] = {
        MGMT_EV_PASSKEY_NOTIFY,
 };
 
-/*
- * These LE scan and inquiry parameters were chosen according to LE General
- * Discovery Procedure specification.
- */
-#define LE_SCAN_WIN                    0x12
-#define LE_SCAN_INT                    0x12
-#define LE_SCAN_TIMEOUT_LE_ONLY                msecs_to_jiffies(10240)
-#define LE_SCAN_TIMEOUT_BREDR_LE       msecs_to_jiffies(5120)
-
-#define INQUIRY_LEN_BREDR              0x08    /* TGAP(100) */
-#define INQUIRY_LEN_BREDR_LE           0x04    /* TGAP(100)/2 */
-
 #define CACHE_TIMEOUT  msecs_to_jiffies(2 * 1000)
 
 #define hdev_is_powered(hdev) (test_bit(HCI_UP, &hdev->flags) && \
@@ -2633,28 +2621,72 @@ static int remove_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
        return err;
 }
 
-int mgmt_interleaved_discovery(struct hci_dev *hdev)
+static int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status)
 {
+       struct pending_cmd *cmd;
+       u8 type;
        int err;
 
-       BT_DBG("%s", hdev->name);
+       hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
 
-       hci_dev_lock(hdev);
+       cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
+       if (!cmd)
+               return -ENOENT;
 
-       err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR_LE);
-       if (err < 0)
-               hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+       type = hdev->discovery.type;
 
-       hci_dev_unlock(hdev);
+       err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status),
+                          &type, sizeof(type));
+       mgmt_pending_remove(cmd);
 
        return err;
 }
 
+static void start_discovery_complete(struct hci_dev *hdev, u8 status)
+{
+       BT_DBG("status %d", status);
+
+       if (status) {
+               hci_dev_lock(hdev);
+               mgmt_start_discovery_failed(hdev, status);
+               hci_dev_unlock(hdev);
+               return;
+       }
+
+       hci_dev_lock(hdev);
+       hci_discovery_set_state(hdev, DISCOVERY_FINDING);
+       hci_dev_unlock(hdev);
+
+       switch (hdev->discovery.type) {
+       case DISCOV_TYPE_LE:
+               queue_delayed_work(hdev->workqueue, &hdev->le_scan_disable,
+                                  DISCOV_LE_TIMEOUT);
+               break;
+
+       case DISCOV_TYPE_INTERLEAVED:
+               queue_delayed_work(hdev->workqueue, &hdev->le_scan_disable,
+                                  DISCOV_INTERLEAVED_TIMEOUT);
+               break;
+
+       case DISCOV_TYPE_BREDR:
+               break;
+
+       default:
+               BT_ERR("Invalid discovery type %d", hdev->discovery.type);
+       }
+}
+
 static int start_discovery(struct sock *sk, struct hci_dev *hdev,
                           void *data, u16 len)
 {
        struct mgmt_cp_start_discovery *cp = data;
        struct pending_cmd *cmd;
+       struct hci_cp_le_set_scan_param param_cp;
+       struct hci_cp_le_set_scan_enable enable_cp;
+       struct hci_cp_inquiry inq_cp;
+       struct hci_request req;
+       /* General inquiry access code (GIAC) */
+       u8 lap[3] = { 0x33, 0x8b, 0x9e };
        int err;
 
        BT_DBG("%s", hdev->name);
@@ -2687,6 +2719,8 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
 
        hdev->discovery.type = cp->type;
 
+       hci_req_init(&req, hdev);
+
        switch (hdev->discovery.type) {
        case DISCOV_TYPE_BREDR:
                if (!lmp_bredr_capable(hdev)) {
@@ -2696,31 +2730,64 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
                        goto failed;
                }
 
-               err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR);
+               if (test_bit(HCI_INQUIRY, &hdev->flags)) {
+                       err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
+                                        MGMT_STATUS_BUSY);
+                       mgmt_pending_remove(cmd);
+                       goto failed;
+               }
+
+               hci_inquiry_cache_flush(hdev);
+
+               memset(&inq_cp, 0, sizeof(inq_cp));
+               memcpy(&inq_cp.lap, lap, sizeof(inq_cp.lap));
+               inq_cp.length = DISCOV_BREDR_INQUIRY_LEN;
+               hci_req_add(&req, HCI_OP_INQUIRY, sizeof(inq_cp), &inq_cp);
                break;
 
        case DISCOV_TYPE_LE:
-               if (!lmp_host_le_capable(hdev)) {
+       case DISCOV_TYPE_INTERLEAVED:
+               if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
                        err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
                                         MGMT_STATUS_NOT_SUPPORTED);
                        mgmt_pending_remove(cmd);
                        goto failed;
                }
 
-               err = hci_le_scan(hdev, LE_SCAN_ACTIVE, LE_SCAN_INT,
-                                 LE_SCAN_WIN, LE_SCAN_TIMEOUT_LE_ONLY);
-               break;
-
-       case DISCOV_TYPE_INTERLEAVED:
-               if (!lmp_host_le_capable(hdev) || !lmp_bredr_capable(hdev)) {
+               if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED &&
+                   !lmp_bredr_capable(hdev)) {
                        err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
                                         MGMT_STATUS_NOT_SUPPORTED);
                        mgmt_pending_remove(cmd);
                        goto failed;
                }
 
-               err = hci_le_scan(hdev, LE_SCAN_ACTIVE, LE_SCAN_INT,
-                                 LE_SCAN_WIN, LE_SCAN_TIMEOUT_BREDR_LE);
+               if (test_bit(HCI_LE_PERIPHERAL, &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_SCAN, &hdev->dev_flags)) {
+                       err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
+                                        MGMT_STATUS_BUSY);
+                       mgmt_pending_remove(cmd);
+                       goto failed;
+               }
+
+               memset(&param_cp, 0, sizeof(param_cp));
+               param_cp.type = LE_SCAN_ACTIVE;
+               param_cp.interval = cpu_to_le16(DISCOV_LE_SCAN_INT);
+               param_cp.window = cpu_to_le16(DISCOV_LE_SCAN_WIN);
+               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);
                break;
 
        default:
@@ -2730,6 +2797,7 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
                goto failed;
        }
 
+       err = hci_req_run(&req, start_discovery_complete);
        if (err < 0)
                mgmt_pending_remove(cmd);
        else
@@ -3418,6 +3486,27 @@ new_settings:
        return err;
 }
 
+int mgmt_set_powered_failed(struct hci_dev *hdev, int err)
+{
+       struct pending_cmd *cmd;
+       u8 status;
+
+       cmd = mgmt_pending_find(MGMT_OP_SET_POWERED, hdev);
+       if (!cmd)
+               return -ENOENT;
+
+       if (err == -ERFKILL)
+               status = MGMT_STATUS_RFKILLED;
+       else
+               status = MGMT_STATUS_FAILED;
+
+       err = cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_POWERED, status);
+
+       mgmt_pending_remove(cmd);
+
+       return err;
+}
+
 int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable)
 {
        struct cmd_lookup match = { NULL, hdev };
@@ -4093,27 +4182,6 @@ int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
                          sizeof(*ev) + eir_len, NULL);
 }
 
-int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status)
-{
-       struct pending_cmd *cmd;
-       u8 type;
-       int err;
-
-       hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
-
-       cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
-       if (!cmd)
-               return -ENOENT;
-
-       type = hdev->discovery.type;
-
-       err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status),
-                          &type, sizeof(type));
-       mgmt_pending_remove(cmd);
-
-       return err;
-}
-
 int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status)
 {
        struct pending_cmd *cmd;