Bluetooth: Handle BR/EDR devices during suspend
To handle BR/EDR devices, we first disable page scan and disconnect all connected devices. Once that is complete, we add event filters (for devices that can wake the system) and re-enable page scan. Signed-off-by: Abhishek Pandit-Subedi <abhishekpandit@chromium.org> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
This commit is contained in:
committed by
Marcel Holtmann
parent
9952d90ea2
commit
4f40afc6c7
@@ -918,15 +918,118 @@ static u8 get_adv_instance_scan_rsp_len(struct hci_dev *hdev, u8 instance)
|
||||
return adv_instance->scan_rsp_len;
|
||||
}
|
||||
|
||||
static void hci_req_clear_event_filter(struct hci_request *req)
|
||||
{
|
||||
struct hci_cp_set_event_filter f;
|
||||
|
||||
memset(&f, 0, sizeof(f));
|
||||
f.flt_type = HCI_FLT_CLEAR_ALL;
|
||||
hci_req_add(req, HCI_OP_SET_EVENT_FLT, 1, &f);
|
||||
|
||||
/* Update page scan state (since we may have modified it when setting
|
||||
* the event filter).
|
||||
*/
|
||||
__hci_req_update_scan(req);
|
||||
}
|
||||
|
||||
static void hci_req_set_event_filter(struct hci_request *req)
|
||||
{
|
||||
struct bdaddr_list *b;
|
||||
struct hci_cp_set_event_filter f;
|
||||
struct hci_dev *hdev = req->hdev;
|
||||
u8 scan;
|
||||
|
||||
/* Always clear event filter when starting */
|
||||
hci_req_clear_event_filter(req);
|
||||
|
||||
list_for_each_entry(b, &hdev->wakeable, list) {
|
||||
memset(&f, 0, sizeof(f));
|
||||
bacpy(&f.addr_conn_flt.bdaddr, &b->bdaddr);
|
||||
f.flt_type = HCI_FLT_CONN_SETUP;
|
||||
f.cond_type = HCI_CONN_SETUP_ALLOW_BDADDR;
|
||||
f.addr_conn_flt.auto_accept = HCI_CONN_SETUP_AUTO_ON;
|
||||
|
||||
bt_dev_dbg(hdev, "Adding event filters for %pMR", &b->bdaddr);
|
||||
hci_req_add(req, HCI_OP_SET_EVENT_FLT, sizeof(f), &f);
|
||||
}
|
||||
|
||||
scan = !list_empty(&hdev->wakeable) ? SCAN_PAGE : SCAN_DISABLED;
|
||||
hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
|
||||
}
|
||||
|
||||
static void suspend_req_complete(struct hci_dev *hdev, u8 status, u16 opcode)
|
||||
{
|
||||
bt_dev_dbg(hdev, "Request complete opcode=0x%x, status=0x%x", opcode,
|
||||
status);
|
||||
if (test_and_clear_bit(SUSPEND_SCAN_ENABLE, hdev->suspend_tasks) ||
|
||||
test_and_clear_bit(SUSPEND_SCAN_DISABLE, hdev->suspend_tasks)) {
|
||||
wake_up(&hdev->suspend_wait_q);
|
||||
}
|
||||
}
|
||||
|
||||
/* Call with hci_dev_lock */
|
||||
void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next)
|
||||
{
|
||||
struct hci_conn *conn;
|
||||
struct hci_request req;
|
||||
u8 page_scan;
|
||||
int disconnect_counter;
|
||||
|
||||
if (next == hdev->suspend_state) {
|
||||
bt_dev_dbg(hdev, "Same state before and after: %d", next);
|
||||
goto done;
|
||||
}
|
||||
|
||||
hdev->suspend_state = next;
|
||||
hci_req_init(&req, hdev);
|
||||
|
||||
if (next == BT_SUSPEND_DISCONNECT) {
|
||||
/* Mark device as suspended */
|
||||
hdev->suspended = true;
|
||||
|
||||
/* Disable page scan */
|
||||
page_scan = SCAN_DISABLED;
|
||||
hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &page_scan);
|
||||
|
||||
/* Mark task needing completion */
|
||||
set_bit(SUSPEND_SCAN_DISABLE, hdev->suspend_tasks);
|
||||
|
||||
/* Prevent disconnects from causing scanning to be re-enabled */
|
||||
hdev->scanning_paused = true;
|
||||
|
||||
/* Run commands before disconnecting */
|
||||
hci_req_run(&req, suspend_req_complete);
|
||||
|
||||
disconnect_counter = 0;
|
||||
/* Soft disconnect everything (power off) */
|
||||
list_for_each_entry(conn, &hdev->conn_hash.list, list) {
|
||||
hci_disconnect(conn, HCI_ERROR_REMOTE_POWER_OFF);
|
||||
disconnect_counter++;
|
||||
}
|
||||
|
||||
if (disconnect_counter > 0) {
|
||||
bt_dev_dbg(hdev,
|
||||
"Had %d disconnects. Will wait on them",
|
||||
disconnect_counter);
|
||||
set_bit(SUSPEND_DISCONNECTING, hdev->suspend_tasks);
|
||||
}
|
||||
} else if (next == BT_SUSPEND_COMPLETE) {
|
||||
/* Unpause to take care of updating scanning params */
|
||||
hdev->scanning_paused = false;
|
||||
/* Enable event filter for paired devices */
|
||||
hci_req_set_event_filter(&req);
|
||||
/* Pause scan changes again. */
|
||||
hdev->scanning_paused = true;
|
||||
hci_req_run(&req, suspend_req_complete);
|
||||
} else {
|
||||
hdev->suspended = false;
|
||||
hdev->scanning_paused = false;
|
||||
|
||||
hci_req_clear_event_filter(&req);
|
||||
hci_req_run(&req, suspend_req_complete);
|
||||
}
|
||||
|
||||
hdev->suspend_state = next;
|
||||
|
||||
done:
|
||||
clear_bit(SUSPEND_PREPARE_NOTIFIER, hdev->suspend_tasks);
|
||||
@@ -2030,6 +2133,9 @@ void __hci_req_update_scan(struct hci_request *req)
|
||||
if (mgmt_powering_down(hdev))
|
||||
return;
|
||||
|
||||
if (hdev->scanning_paused)
|
||||
return;
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_CONNECTABLE) ||
|
||||
disconnected_whitelist_entries(hdev))
|
||||
scan = SCAN_PAGE;
|
||||
|
||||
Reference in New Issue
Block a user