Skip to content

Commit 9952d90

Browse files
apanditholtmann
authored andcommitted
Bluetooth: Handle PM_SUSPEND_PREPARE and PM_POST_SUSPEND
Register for PM_SUSPEND_PREPARE and PM_POST_SUSPEND to make sure the Bluetooth controller is prepared correctly for suspend/resume. Implement the registration, scheduling and task handling portions only in this patch. Signed-off-by: Abhishek Pandit-Subedi <[email protected]> Signed-off-by: Marcel Holtmann <[email protected]>
1 parent 72da7b2 commit 9952d90

File tree

4 files changed

+126
-0
lines changed

4 files changed

+126
-0
lines changed

include/net/bluetooth/hci_core.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,20 @@ struct discovery_state {
8888
unsigned long scan_duration;
8989
};
9090

91+
#define SUSPEND_NOTIFIER_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */
92+
93+
enum suspend_tasks {
94+
SUSPEND_POWERING_DOWN,
95+
96+
SUSPEND_PREPARE_NOTIFIER,
97+
__SUSPEND_NUM_TASKS
98+
};
99+
100+
enum suspended_state {
101+
BT_RUNNING = 0,
102+
BT_SUSPENDED,
103+
};
104+
91105
struct hci_conn_hash {
92106
struct list_head list;
93107
unsigned int acl_num;
@@ -390,6 +404,15 @@ struct hci_dev {
390404
void *smp_bredr_data;
391405

392406
struct discovery_state discovery;
407+
408+
struct notifier_block suspend_notifier;
409+
struct work_struct suspend_prepare;
410+
enum suspended_state suspend_state_next;
411+
enum suspended_state suspend_state;
412+
413+
wait_queue_head_t suspend_wait_q;
414+
DECLARE_BITMAP(suspend_tasks, __SUSPEND_NUM_TASKS);
415+
393416
struct hci_conn_hash conn_hash;
394417

395418
struct list_head mgmt_pending;

net/bluetooth/hci_core.c

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
#include <linux/debugfs.h>
3232
#include <linux/crypto.h>
3333
#include <linux/property.h>
34+
#include <linux/suspend.h>
35+
#include <linux/wait.h>
3436
#include <asm/unaligned.h>
3537

3638
#include <net/bluetooth/bluetooth.h>
@@ -1787,6 +1789,9 @@ int hci_dev_do_close(struct hci_dev *hdev)
17871789
clear_bit(HCI_RUNNING, &hdev->flags);
17881790
hci_sock_dev_event(hdev, HCI_DEV_CLOSE);
17891791

1792+
if (test_and_clear_bit(SUSPEND_POWERING_DOWN, hdev->suspend_tasks))
1793+
wake_up(&hdev->suspend_wait_q);
1794+
17901795
/* After this point our queues are empty
17911796
* and no tasks are scheduled. */
17921797
hdev->close(hdev);
@@ -3264,6 +3269,78 @@ void hci_copy_identity_address(struct hci_dev *hdev, bdaddr_t *bdaddr,
32643269
}
32653270
}
32663271

3272+
static int hci_suspend_wait_event(struct hci_dev *hdev)
3273+
{
3274+
#define WAKE_COND \
3275+
(find_first_bit(hdev->suspend_tasks, __SUSPEND_NUM_TASKS) == \
3276+
__SUSPEND_NUM_TASKS)
3277+
3278+
int i;
3279+
int ret = wait_event_timeout(hdev->suspend_wait_q,
3280+
WAKE_COND, SUSPEND_NOTIFIER_TIMEOUT);
3281+
3282+
if (ret == 0) {
3283+
bt_dev_dbg(hdev, "Timed out waiting for suspend");
3284+
for (i = 0; i < __SUSPEND_NUM_TASKS; ++i) {
3285+
if (test_bit(i, hdev->suspend_tasks))
3286+
bt_dev_dbg(hdev, "Bit %d is set", i);
3287+
clear_bit(i, hdev->suspend_tasks);
3288+
}
3289+
3290+
ret = -ETIMEDOUT;
3291+
} else {
3292+
ret = 0;
3293+
}
3294+
3295+
return ret;
3296+
}
3297+
3298+
static void hci_prepare_suspend(struct work_struct *work)
3299+
{
3300+
struct hci_dev *hdev =
3301+
container_of(work, struct hci_dev, suspend_prepare);
3302+
3303+
hci_dev_lock(hdev);
3304+
hci_req_prepare_suspend(hdev, hdev->suspend_state_next);
3305+
hci_dev_unlock(hdev);
3306+
}
3307+
3308+
static int hci_suspend_notifier(struct notifier_block *nb, unsigned long action,
3309+
void *data)
3310+
{
3311+
struct hci_dev *hdev =
3312+
container_of(nb, struct hci_dev, suspend_notifier);
3313+
int ret = 0;
3314+
3315+
/* If powering down, wait for completion. */
3316+
if (mgmt_powering_down(hdev)) {
3317+
set_bit(SUSPEND_POWERING_DOWN, hdev->suspend_tasks);
3318+
ret = hci_suspend_wait_event(hdev);
3319+
if (ret)
3320+
goto done;
3321+
}
3322+
3323+
/* Suspend notifier should only act on events when powered. */
3324+
if (!hdev_is_powered(hdev))
3325+
goto done;
3326+
3327+
if (action == PM_SUSPEND_PREPARE) {
3328+
hdev->suspend_state_next = BT_SUSPENDED;
3329+
set_bit(SUSPEND_PREPARE_NOTIFIER, hdev->suspend_tasks);
3330+
queue_work(hdev->req_workqueue, &hdev->suspend_prepare);
3331+
3332+
ret = hci_suspend_wait_event(hdev);
3333+
} else if (action == PM_POST_SUSPEND) {
3334+
hdev->suspend_state_next = BT_RUNNING;
3335+
set_bit(SUSPEND_PREPARE_NOTIFIER, hdev->suspend_tasks);
3336+
queue_work(hdev->req_workqueue, &hdev->suspend_prepare);
3337+
3338+
ret = hci_suspend_wait_event(hdev);
3339+
}
3340+
3341+
done:
3342+
return ret ? notifier_from_errno(-EBUSY) : NOTIFY_STOP;
3343+
}
32673344
/* Alloc HCI device */
32683345
struct hci_dev *hci_alloc_dev(void)
32693346
{
@@ -3341,6 +3418,7 @@ struct hci_dev *hci_alloc_dev(void)
33413418
INIT_WORK(&hdev->tx_work, hci_tx_work);
33423419
INIT_WORK(&hdev->power_on, hci_power_on);
33433420
INIT_WORK(&hdev->error_reset, hci_error_reset);
3421+
INIT_WORK(&hdev->suspend_prepare, hci_prepare_suspend);
33443422

33453423
INIT_DELAYED_WORK(&hdev->power_off, hci_power_off);
33463424

@@ -3349,6 +3427,7 @@ struct hci_dev *hci_alloc_dev(void)
33493427
skb_queue_head_init(&hdev->raw_q);
33503428

33513429
init_waitqueue_head(&hdev->req_wait_q);
3430+
init_waitqueue_head(&hdev->suspend_wait_q);
33523431

33533432
INIT_DELAYED_WORK(&hdev->cmd_timer, hci_cmd_timeout);
33543433

@@ -3460,6 +3539,11 @@ int hci_register_dev(struct hci_dev *hdev)
34603539
hci_sock_dev_event(hdev, HCI_DEV_REG);
34613540
hci_dev_hold(hdev);
34623541

3542+
hdev->suspend_notifier.notifier_call = hci_suspend_notifier;
3543+
error = register_pm_notifier(&hdev->suspend_notifier);
3544+
if (error)
3545+
goto err_wqueue;
3546+
34633547
queue_work(hdev->req_workqueue, &hdev->power_on);
34643548

34653549
return id;
@@ -3493,6 +3577,8 @@ void hci_unregister_dev(struct hci_dev *hdev)
34933577

34943578
hci_dev_do_close(hdev);
34953579

3580+
unregister_pm_notifier(&hdev->suspend_notifier);
3581+
34963582
if (!test_bit(HCI_INIT, &hdev->flags) &&
34973583
!hci_dev_test_flag(hdev, HCI_SETUP) &&
34983584
!hci_dev_test_flag(hdev, HCI_CONFIG)) {

net/bluetooth/hci_request.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -918,6 +918,21 @@ static u8 get_adv_instance_scan_rsp_len(struct hci_dev *hdev, u8 instance)
918918
return adv_instance->scan_rsp_len;
919919
}
920920

921+
/* Call with hci_dev_lock */
922+
void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next)
923+
{
924+
if (next == hdev->suspend_state) {
925+
bt_dev_dbg(hdev, "Same state before and after: %d", next);
926+
goto done;
927+
}
928+
929+
hdev->suspend_state = next;
930+
931+
done:
932+
clear_bit(SUSPEND_PREPARE_NOTIFIER, hdev->suspend_tasks);
933+
wake_up(&hdev->suspend_wait_q);
934+
}
935+
921936
static u8 get_cur_adv_instance_scan_rsp_len(struct hci_dev *hdev)
922937
{
923938
u8 instance = hdev->cur_adv_instance;

net/bluetooth/hci_request.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ void __hci_req_update_eir(struct hci_request *req);
6868
void hci_req_add_le_scan_disable(struct hci_request *req);
6969
void hci_req_add_le_passive_scan(struct hci_request *req);
7070

71+
void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next);
72+
7173
void hci_req_reenable_advertising(struct hci_dev *hdev);
7274
void __hci_req_enable_advertising(struct hci_request *req);
7375
void __hci_req_disable_advertising(struct hci_request *req);

0 commit comments

Comments
 (0)