Hi Abhishek, > To prevent spurious wake ups, we disable any discovery or advertising > when we enter suspend and restore it when we exit suspend. While paused, > we disable any management requests to modify discovery or advertising. > > Signed-off-by: Abhishek Pandit-Subedi <abhishekpandit@xxxxxxxxxxxx> > --- > > Changes in v4: None > Changes in v3: None > Changes in v2: > * Refactored pause discovery + advertising into its own patch > > include/net/bluetooth/hci_core.h | 11 ++++++++ > net/bluetooth/hci_request.c | 43 ++++++++++++++++++++++++++++++++ > net/bluetooth/mgmt.c | 41 ++++++++++++++++++++++++++++++ > 3 files changed, 95 insertions(+) > > diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h > index 4eb5b2786048..af264a247636 100644 > --- a/include/net/bluetooth/hci_core.h > +++ b/include/net/bluetooth/hci_core.h > @@ -91,6 +91,12 @@ struct discovery_state { > #define SUSPEND_NOTIFIER_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ > > enum suspend_tasks { > + SUSPEND_PAUSE_DISCOVERY, > + SUSPEND_UNPAUSE_DISCOVERY, > + > + SUSPEND_PAUSE_ADVERTISING, > + SUSPEND_UNPAUSE_ADVERTISING, > + > SUSPEND_SCAN_DISABLE, > SUSPEND_SCAN_ENABLE, > SUSPEND_DISCONNECTING, > @@ -409,6 +415,11 @@ struct hci_dev { > > struct discovery_state discovery; > > + int discovery_old_state; > + bool discovery_paused; > + int advertising_old_state; > + bool advertising_paused; > + > struct notifier_block suspend_notifier; > struct work_struct suspend_prepare; > enum suspended_state suspend_state_next; > diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c > index 88fd95d70f89..e25cfb6fd9aa 100644 > --- a/net/bluetooth/hci_request.c > +++ b/net/bluetooth/hci_request.c > @@ -1036,6 +1036,28 @@ void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next) > /* Mark device as suspended */ > hdev->suspended = true; > > + /* Pause discovery if not already stopped */ > + old_state = hdev->discovery.state; > + if (old_state != DISCOVERY_STOPPED) { > + set_bit(SUSPEND_PAUSE_DISCOVERY, hdev->suspend_tasks); > + hci_discovery_set_state(hdev, DISCOVERY_STOPPING); > + queue_work(hdev->req_workqueue, &hdev->discov_update); > + } > + > + hdev->discovery_paused = true; > + hdev->discovery_old_state = old_state; > + > + /* Stop advertising */ > + old_state = hci_dev_test_flag(hdev, HCI_ADVERTISING); > + if (old_state) { > + set_bit(SUSPEND_PAUSE_ADVERTISING, hdev->suspend_tasks); > + cancel_delayed_work(&hdev->discov_off); > + queue_delayed_work(hdev->req_workqueue, > + &hdev->discov_off, 0); > + } > + > + hdev->advertising_paused = true; > + hdev->advertising_old_state = old_state; > /* Disable page scan */ > page_scan = SCAN_DISABLED; > hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &page_scan); > @@ -1081,6 +1103,27 @@ void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next) > hci_req_clear_event_filter(&req); > /* Reset passive/background scanning to normal */ > hci_req_config_le_suspend_scan(&req); > + > + /* Unpause advertising */ > + hdev->advertising_paused = false; > + if (hdev->advertising_old_state) { > + set_bit(SUSPEND_UNPAUSE_ADVERTISING, > + hdev->suspend_tasks); > + hci_dev_set_flag(hdev, HCI_ADVERTISING); > + queue_work(hdev->req_workqueue, > + &hdev->discoverable_update); > + hdev->advertising_old_state = 0; > + } > + > + /* Unpause discovery */ > + hdev->discovery_paused = false; > + if (hdev->discovery_old_state != DISCOVERY_STOPPED && > + hdev->discovery_old_state != DISCOVERY_STOPPING) { > + set_bit(SUSPEND_UNPAUSE_DISCOVERY, hdev->suspend_tasks); > + hci_discovery_set_state(hdev, DISCOVERY_STARTING); > + queue_work(hdev->req_workqueue, &hdev->discov_update); > + } > + > hci_req_run(&req, suspend_req_complete); > } > > diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c > index f6751ce0d561..28572579c06d 100644 > --- a/net/bluetooth/mgmt.c > +++ b/net/bluetooth/mgmt.c > @@ -1387,6 +1387,12 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data, > goto failed; > } > > + if (hdev->advertising_paused) { > + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, > + MGMT_STATUS_BUSY); > + goto failed; > + } > + > if (!hdev_is_powered(hdev)) { > bool changed = false; > > @@ -3870,6 +3876,13 @@ void mgmt_start_discovery_complete(struct hci_dev *hdev, u8 status) > } > > hci_dev_unlock(hdev); > + > + /* Handle suspend notifier */ > + if (test_and_clear_bit(SUSPEND_UNPAUSE_DISCOVERY, > + hdev->suspend_tasks)) { > + BT_DBG("Unpaused discovery"); > + wake_up(&hdev->suspend_wait_q); > + } > } > > static bool discovery_type_is_valid(struct hci_dev *hdev, uint8_t type, > @@ -3931,6 +3944,13 @@ static int start_discovery_internal(struct sock *sk, struct hci_dev *hdev, > goto failed; > } > > + /* Can't start discovery when it is paused */ > + if (hdev->discovery_paused) { > + err = mgmt_cmd_complete(sk, hdev->id, op, MGMT_STATUS_BUSY, > + &cp->type, sizeof(cp->type)); > + goto failed; > + } > + > /* Clear the discovery filter first to free any previously > * allocated memory for the UUID list. > */ > @@ -4098,6 +4118,12 @@ void mgmt_stop_discovery_complete(struct hci_dev *hdev, u8 status) > } > > hci_dev_unlock(hdev); > + > + /* Handle suspend notifier */ > + if (test_and_clear_bit(SUSPEND_PAUSE_DISCOVERY, hdev->suspend_tasks)) { > + BT_DBG("Paused discovery"); > + wake_up(&hdev->suspend_wait_q); > + } > } > > static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data, > @@ -4329,6 +4355,17 @@ static void set_advertising_complete(struct hci_dev *hdev, u8 status, > if (match.sk) > sock_put(match.sk); > > + /* Handle suspend notifier */ > + if (test_and_clear_bit(SUSPEND_PAUSE_ADVERTISING, > + hdev->suspend_tasks)) { > + BT_DBG("Paused advertising"); > + wake_up(&hdev->suspend_wait_q); > + } else if (test_and_clear_bit(SUSPEND_UNPAUSE_ADVERTISING, > + hdev->suspend_tasks)) { > + BT_DBG("Unpaused advertising"); > + wake_up(&hdev->suspend_wait_q); > + } > + > /* If "Set Advertising" was just disabled and instance advertising was > * set up earlier, then re-enable multi-instance advertising. > */ > @@ -4380,6 +4417,10 @@ static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data, > return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING, > MGMT_STATUS_INVALID_PARAMS); > > + if (hdev->advertising_paused) > + return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING, > + MGMT_STATUS_BUSY); > + > hci_dev_lock(hdev); > > val = !!cp->val; looks good. Just make this 4/5 and use bt_dev_dbg where possible. Regards Marcel