When doing scan through mgmt api, some controllers can do both le and classic scan at same time. They can be distinguished by HCI_QUIRK_SIMULTAENOUS_DISCOVERY set. This patch enables them to use this feature when doing dual mode scan. Instead of doing le, then classic scan, both scans are run at once. Signed-off-by: Jakub Pawlowski <jpawlowski@xxxxxxxxxx> --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_core.c | 26 ++++++++++++---- net/bluetooth/hci_event.c | 18 +++++++++-- net/bluetooth/mgmt.c | 66 ++++++++++++++++++++++++++++------------ 4 files changed, 84 insertions(+), 27 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index acec914..5cd9451 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1285,6 +1285,7 @@ void hci_sock_dev_event(struct hci_dev *hdev, int event); #define DISCOV_LE_SCAN_WIN 0x12 #define DISCOV_LE_SCAN_INT 0x12 #define DISCOV_LE_TIMEOUT 10240 /* msec */ +#define DISCOV_LE_SIMULTAENOUS_SCAN_WIN 0x24 #define DISCOV_INTERLEAVED_TIMEOUT 5120 /* msec */ #define DISCOV_INTERLEAVED_INQUIRY_LEN 0x04 #define DISCOV_BREDR_INQUIRY_LEN 0x08 diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index dbd26bc..c4f2316 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2866,12 +2866,26 @@ static void le_scan_disable_work_complete(struct hci_dev *hdev, u8 status, hci_dev_lock(hdev); - hci_inquiry_cache_flush(hdev); - - err = hci_req_run(&req, inquiry_complete); - if (err) { - BT_ERR("Inquiry request failed: err %d", err); - hci_discovery_set_state(hdev, DISCOVERY_STOPPED); + if (!test_bit(HCI_QUIRK_SIMULTAENOUS_DISCOVERY, + &hdev->quirks)) { + hci_inquiry_cache_flush(hdev); + + err = hci_req_run(&req, inquiry_complete); + if (err) { + BT_ERR("Inquiry request failed: err %d", err); + hci_discovery_set_state(hdev, + DISCOVERY_STOPPED); + } + } else { + /* If we were running le only scan, change discovery + * state. If we were running both le and classic scans + * simultaenously, and classic is already finished, + * stop discovery, otherwise classic scan will stop + * discovery when finished. + */ + if (!test_bit(HCI_INQUIRY, &hdev->flags)) + hci_discovery_set_state(hdev, + DISCOVERY_STOPPED); } hci_dev_unlock(hdev); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index e9b17b5..d69de31 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2127,7 +2127,14 @@ static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) goto unlock; if (list_empty(&discov->resolve)) { - hci_discovery_set_state(hdev, DISCOVERY_STOPPED); + /* if we were running classic discovery change discovery state. + * If we were running both le and classic scans simultaenously, + * and le is already finished, change state, otherwise le + * scan will stop discovery when finished. + */ + if (!test_bit(HCI_LE_SCAN, &hdev->flags) || + !test_bit(HCI_QUIRK_SIMULTAENOUS_DISCOVERY, &hdev->quirks)) + hci_discovery_set_state(hdev, DISCOVERY_STOPPED); goto unlock; } @@ -2136,7 +2143,14 @@ static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) e->name_state = NAME_PENDING; hci_discovery_set_state(hdev, DISCOVERY_RESOLVING); } else { - hci_discovery_set_state(hdev, DISCOVERY_STOPPED); + /* if we were running classic discovery change discovery state. + * If we were running both le and classic scans simultaenously, + * and le is already finished, change state, otherwise le + * scan will stop discovery when finished. + */ + if (!test_bit(HCI_LE_SCAN, &hdev->flags) || + !test_bit(HCI_QUIRK_SIMULTAENOUS_DISCOVERY, &hdev->quirks)) + hci_discovery_set_state(hdev, DISCOVERY_STOPPED); } unlock: diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 1e4635a..9d95290 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3783,34 +3783,44 @@ done: return err; } -static bool trigger_discovery(struct hci_request *req, u8 *status) +static bool trigger_classic_discovery(struct hci_request *req, 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; struct hci_cp_inquiry inq_cp; /* General inquiry access code (GIAC) */ u8 lap[3] = { 0x33, 0x8b, 0x9e }; + + *status = mgmt_bredr_support(hdev); + if (*status) + return false; + + if (test_bit(HCI_INQUIRY, &hdev->flags)) { + *status = MGMT_STATUS_BUSY; + return false; + } + + 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); + return true; +} + +static bool trigger_discovery(struct hci_request *req, 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; + __le16 interval; u8 own_addr_type; int err; switch (hdev->discovery.type) { case DISCOV_TYPE_BREDR: - *status = mgmt_bredr_support(hdev); - if (*status) - return false; - - if (test_bit(HCI_INQUIRY, &hdev->flags)) { - *status = MGMT_STATUS_BUSY; + if (!trigger_classic_discovery(req, status)) return false; - } - - 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: @@ -3858,8 +3868,17 @@ static bool trigger_discovery(struct hci_request *req, u8 *status) return false; } + /* During simultaenous scan, scan interval can't be equal to + * scan window. We must leave some time to do BR/EDR scan. + */ + if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED && + test_bit(HCI_QUIRK_SIMULTAENOUS_DISCOVERY, &hdev->quirks)) + interval = cpu_to_le16(DISCOV_LE_SIMULTAENOUS_SCAN_WIN); + else + interval = cpu_to_le16(DISCOV_LE_SCAN_INT); + param_cp.type = LE_SCAN_ACTIVE; - param_cp.interval = cpu_to_le16(DISCOV_LE_SCAN_INT); + param_cp.interval = 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), @@ -3870,6 +3889,11 @@ static bool trigger_discovery(struct hci_request *req, u8 *status) enable_cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE; hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(enable_cp), &enable_cp); + + if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED && + test_bit(HCI_QUIRK_SIMULTAENOUS_DISCOVERY, &hdev->quirks)) + if (!trigger_classic_discovery(req, status)) + return false; break; default: @@ -3914,7 +3938,11 @@ static void start_discovery_complete(struct hci_dev *hdev, u8 status, timeout = msecs_to_jiffies(DISCOV_LE_TIMEOUT); break; case DISCOV_TYPE_INTERLEAVED: - timeout = msecs_to_jiffies(hdev->discov_interleaved_timeout); + if (test_bit(HCI_QUIRK_SIMULTAENOUS_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; -- 2.2.0.rc0.207.ga3a616c -- To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html