Bluetooth: Use extended scanning if controller supports
authorJaganath Kanakkassery <jaganath.k.os@gmail.com>
Fri, 6 Jul 2018 11:35:28 +0000 (17:05 +0530)
committerMarcel Holtmann <marcel@holtmann.org>
Fri, 6 Jul 2018 20:41:17 +0000 (22:41 +0200)
This implements Set extended scan param and set extended scan enable
commands and use it for start LE scan based on controller support.

The new features added in these commands are setting of new PHY for
scanning and setting of scan duration. Both features are disabled
for now, meaning only 1M PHY is set and scan duration is set to 0
which means that scanning will be done untill scan disable is called.

< HCI Command: LE Set Extended Scan Parameters (0x08|0x0041) plen 8
        Own address type: Random (0x01)
        Filter policy: Accept all advertisement (0x00)
        PHYs: 0x01
        Entry 0: LE 1M
          Type: Active (0x01)
          Interval: 11.250 msec (0x0012)
          Window: 11.250 msec (0x0012)
> HCI Event: Command Complete (0x0e) plen 4
      LE Set Extended Scan Parameters (0x08|0x0041) ncmd 1
        Status: Success (0x00)
< HCI Command: LE Set Extended Scan Enable (0x08|0x0042) plen 6
        Extended scan: Enabled (0x01)
        Filter duplicates: Enabled (0x01)
        Duration: 0 msec (0x0000)
        Period: 0.00 sec (0x0000)
> HCI Event: Command Complete (0x0e) plen 4
      LE Set Extended Scan Enable (0x08|0x0042) ncmd 2
        Status: Success (0x00)

Signed-off-by: Jaganath Kanakkassery <jaganathx.kanakkassery@intel.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
net/bluetooth/hci_event.c
net/bluetooth/hci_request.c

index 4af1a3a4d9b1d7dfae4b4cfa33e14a04927c1ea8..8c2868f439e7f58c4557b4d75bbdd52dce90e0f6 100644 (file)
@@ -1514,6 +1514,30 @@ struct hci_cp_le_set_default_phy {
        __u8    rx_phys;
 } __packed;
 
+#define HCI_OP_LE_SET_EXT_SCAN_PARAMS   0x2041
+struct hci_cp_le_set_ext_scan_params {
+       __u8    own_addr_type;
+       __u8    filter_policy;
+       __u8    scanning_phys;
+       __u8    data[0];
+} __packed;
+
+#define LE_SCAN_PHY_1M 0x01
+
+struct hci_cp_le_scan_phy_params {
+       __u8    type;
+       __le16  interval;
+       __le16  window;
+} __packed;
+
+#define HCI_OP_LE_SET_EXT_SCAN_ENABLE   0x2042
+struct hci_cp_le_set_ext_scan_enable {
+       __u8    enable;
+       __u8    filter_dup;
+       __le16  duration;
+       __le16  period;
+} __packed;
+
 /* ---- HCI Events ---- */
 #define HCI_EV_INQUIRY_COMPLETE                0x01
 
index 409f49bd833827c06a278d7178b224b82a89418b..cc0bde74dd45c0eaed0b4aa7f4aba8ed390b5d3f 100644 (file)
@@ -1158,6 +1158,10 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
 #define bredr_sc_enabled(dev)  (lmp_sc_capable(dev) && \
                                hci_dev_test_flag(dev, HCI_SC_ENABLED))
 
+/* Use ext scanning if set ext scan param and ext scan enable is supported */
+#define use_ext_scan(dev) (((dev)->commands[37] & 0x20) && \
+                          ((dev)->commands[37] & 0x40))
+
 /* ----- HCI protocols ----- */
 #define HCI_PROTO_DEFER             0x01
 
index 9ec07cd4ab13a0b7dc0983bcd40506ef620fdf10..15afad005d725454fd8f46c311b43ffd85e0830f 100644 (file)
@@ -1098,6 +1098,31 @@ static void hci_cc_le_set_scan_param(struct hci_dev *hdev, struct sk_buff *skb)
        hci_dev_unlock(hdev);
 }
 
+static void hci_cc_le_set_ext_scan_param(struct hci_dev *hdev,
+                                        struct sk_buff *skb)
+{
+       struct hci_cp_le_set_ext_scan_params *cp;
+       __u8 status = *((__u8 *) skb->data);
+       struct hci_cp_le_scan_phy_params *phy_param;
+
+       BT_DBG("%s status 0x%2.2x", hdev->name, status);
+
+       if (status)
+               return;
+
+       cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_EXT_SCAN_PARAMS);
+       if (!cp)
+               return;
+
+       phy_param = (void *)cp->data;
+
+       hci_dev_lock(hdev);
+
+       hdev->le_scan_type = phy_param->type;
+
+       hci_dev_unlock(hdev);
+}
+
 static bool has_pending_adv_report(struct hci_dev *hdev)
 {
        struct discovery_state *d = &hdev->discovery;
@@ -1202,6 +1227,24 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
        le_set_scan_enable_complete(hdev, cp->enable);
 }
 
+static void hci_cc_le_set_ext_scan_enable(struct hci_dev *hdev,
+                                     struct sk_buff *skb)
+{
+       struct hci_cp_le_set_ext_scan_enable *cp;
+       __u8 status = *((__u8 *) skb->data);
+
+       BT_DBG("%s status 0x%2.2x", hdev->name, status);
+
+       if (status)
+               return;
+
+       cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_EXT_SCAN_ENABLE);
+       if (!cp)
+               return;
+
+       le_set_scan_enable_complete(hdev, cp->enable);
+}
+
 static void hci_cc_le_read_white_list_size(struct hci_dev *hdev,
                                           struct sk_buff *skb)
 {
@@ -3079,6 +3122,14 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb,
                hci_cc_write_ssp_debug_mode(hdev, skb);
                break;
 
+       case HCI_OP_LE_SET_EXT_SCAN_PARAMS:
+               hci_cc_le_set_ext_scan_param(hdev, skb);
+               break;
+
+       case HCI_OP_LE_SET_EXT_SCAN_ENABLE:
+               hci_cc_le_set_ext_scan_enable(hdev, skb);
+               break;
+
        default:
                BT_DBG("%s opcode 0x%4.4x", hdev->name, *opcode);
                break;
index 76dcc3f14cea65be5208fe8ebb4af45ae67d5982..faf7c711234c5790fd916e2fd676dbd97bcdc6c5 100644 (file)
@@ -647,11 +647,22 @@ void __hci_req_update_eir(struct hci_request *req)
 
 void hci_req_add_le_scan_disable(struct hci_request *req)
 {
-       struct hci_cp_le_set_scan_enable cp;
+       struct hci_dev *hdev = req->hdev;
 
-       memset(&cp, 0, sizeof(cp));
-       cp.enable = LE_SCAN_DISABLE;
-       hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
+       if (use_ext_scan(hdev)) {
+               struct hci_cp_le_set_ext_scan_enable cp;
+
+               memset(&cp, 0, sizeof(cp));
+               cp.enable = LE_SCAN_DISABLE;
+               hci_req_add(req, HCI_OP_LE_SET_EXT_SCAN_ENABLE, sizeof(cp),
+                           &cp);
+       } else {
+               struct hci_cp_le_set_scan_enable cp;
+
+               memset(&cp, 0, sizeof(cp));
+               cp.enable = LE_SCAN_DISABLE;
+               hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
+       }
 }
 
 static void add_to_white_list(struct hci_request *req,
@@ -770,23 +781,60 @@ 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)
 {
-       struct hci_cp_le_set_scan_param param_cp;
-       struct hci_cp_le_set_scan_enable enable_cp;
-
-       memset(&param_cp, 0, sizeof(param_cp));
-       param_cp.type = type;
-       param_cp.interval = cpu_to_le16(interval);
-       param_cp.window = cpu_to_le16(window);
-       param_cp.own_address_type = own_addr_type;
-       param_cp.filter_policy = filter_policy;
-       hci_req_add(req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp),
-                   &param_cp);
+       struct hci_dev *hdev = req->hdev;
 
-       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);
+       /* Use ext scanning if set ext scan param and ext scan enable is
+        * supported
+        */
+       if (use_ext_scan(hdev)) {
+               struct hci_cp_le_set_ext_scan_params *ext_param_cp;
+               struct hci_cp_le_set_ext_scan_enable ext_enable_cp;
+               struct hci_cp_le_scan_phy_params *phy_params;
+               /* Ony single PHY (1M) is supported as of now */
+               u8 data[sizeof(*ext_param_cp) + sizeof(*phy_params) * 1];
+
+               ext_param_cp = (void *)data;
+               phy_params = (void *)ext_param_cp->data;
+
+               memset(ext_param_cp, 0, sizeof(*ext_param_cp));
+               ext_param_cp->own_addr_type = own_addr_type;
+               ext_param_cp->filter_policy = filter_policy;
+               ext_param_cp->scanning_phys = LE_SCAN_PHY_1M;
+
+               memset(phy_params, 0, sizeof(*phy_params));
+               phy_params->type = type;
+               phy_params->interval = cpu_to_le16(interval);
+               phy_params->window = cpu_to_le16(window);
+
+               hci_req_add(req, HCI_OP_LE_SET_EXT_SCAN_PARAMS,
+                           sizeof(*ext_param_cp) + sizeof(*phy_params),
+                           ext_param_cp);
+
+               memset(&ext_enable_cp, 0, sizeof(ext_enable_cp));
+               ext_enable_cp.enable = LE_SCAN_ENABLE;
+               ext_enable_cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
+
+               hci_req_add(req, HCI_OP_LE_SET_EXT_SCAN_ENABLE,
+                           sizeof(ext_enable_cp), &ext_enable_cp);
+       } else {
+               struct hci_cp_le_set_scan_param param_cp;
+               struct hci_cp_le_set_scan_enable enable_cp;
+
+               memset(&param_cp, 0, sizeof(param_cp));
+               param_cp.type = type;
+               param_cp.interval = cpu_to_le16(interval);
+               param_cp.window = cpu_to_le16(window);
+               param_cp.own_address_type = own_addr_type;
+               param_cp.filter_policy = filter_policy;
+               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);
+       }
 }
 
 void hci_req_add_le_passive_scan(struct hci_request *req)
@@ -1948,7 +1996,6 @@ discov_stopped:
 static int le_scan_restart(struct hci_request *req, unsigned long opt)
 {
        struct hci_dev *hdev = req->hdev;
-       struct hci_cp_le_set_scan_enable cp;
 
        /* If controller is not scanning we are done. */
        if (!hci_dev_test_flag(hdev, HCI_LE_SCAN))
@@ -1956,10 +2003,23 @@ static int le_scan_restart(struct hci_request *req, unsigned long opt)
 
        hci_req_add_le_scan_disable(req);
 
-       memset(&cp, 0, sizeof(cp));
-       cp.enable = LE_SCAN_ENABLE;
-       cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
-       hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
+       if (use_ext_scan(hdev)) {
+               struct hci_cp_le_set_ext_scan_enable ext_enable_cp;
+
+               memset(&ext_enable_cp, 0, sizeof(ext_enable_cp));
+               ext_enable_cp.enable = LE_SCAN_ENABLE;
+               ext_enable_cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
+
+               hci_req_add(req, HCI_OP_LE_SET_EXT_SCAN_ENABLE,
+                           sizeof(ext_enable_cp), &ext_enable_cp);
+       } else {
+               struct hci_cp_le_set_scan_enable cp;
+
+               memset(&cp, 0, sizeof(cp));
+               cp.enable = LE_SCAN_ENABLE;
+               cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
+               hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
+       }
 
        return 0;
 }