Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <abhishekpandit@chromium.org>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>

authored by

Abhishek Pandit-Subedi and committed by
Marcel Holtmann
9952d90e 72da7b2c

+126
+23
include/net/bluetooth/hci_core.h
··· 88 88 unsigned long scan_duration; 89 89 }; 90 90 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 + 91 105 struct hci_conn_hash { 92 106 struct list_head list; 93 107 unsigned int acl_num; ··· 404 390 void *smp_bredr_data; 405 391 406 392 struct discovery_state discovery; 393 + 394 + struct notifier_block suspend_notifier; 395 + struct work_struct suspend_prepare; 396 + enum suspended_state suspend_state_next; 397 + enum suspended_state suspend_state; 398 + 399 + wait_queue_head_t suspend_wait_q; 400 + DECLARE_BITMAP(suspend_tasks, __SUSPEND_NUM_TASKS); 401 + 407 402 struct hci_conn_hash conn_hash; 408 403 409 404 struct list_head mgmt_pending;
+86
net/bluetooth/hci_core.c
··· 31 31 #include <linux/debugfs.h> 32 32 #include <linux/crypto.h> 33 33 #include <linux/property.h> 34 + #include <linux/suspend.h> 35 + #include <linux/wait.h> 34 36 #include <asm/unaligned.h> 35 37 36 38 #include <net/bluetooth/bluetooth.h> ··· 1789 1787 clear_bit(HCI_RUNNING, &hdev->flags); 1790 1788 hci_sock_dev_event(hdev, HCI_DEV_CLOSE); 1791 1789 1790 + if (test_and_clear_bit(SUSPEND_POWERING_DOWN, hdev->suspend_tasks)) 1791 + wake_up(&hdev->suspend_wait_q); 1792 + 1792 1793 /* After this point our queues are empty 1793 1794 * and no tasks are scheduled. */ 1794 1795 hdev->close(hdev); ··· 3269 3264 } 3270 3265 } 3271 3266 3267 + static int hci_suspend_wait_event(struct hci_dev *hdev) 3268 + { 3269 + #define WAKE_COND \ 3270 + (find_first_bit(hdev->suspend_tasks, __SUSPEND_NUM_TASKS) == \ 3271 + __SUSPEND_NUM_TASKS) 3272 + 3273 + int i; 3274 + int ret = wait_event_timeout(hdev->suspend_wait_q, 3275 + WAKE_COND, SUSPEND_NOTIFIER_TIMEOUT); 3276 + 3277 + if (ret == 0) { 3278 + bt_dev_dbg(hdev, "Timed out waiting for suspend"); 3279 + for (i = 0; i < __SUSPEND_NUM_TASKS; ++i) { 3280 + if (test_bit(i, hdev->suspend_tasks)) 3281 + bt_dev_dbg(hdev, "Bit %d is set", i); 3282 + clear_bit(i, hdev->suspend_tasks); 3283 + } 3284 + 3285 + ret = -ETIMEDOUT; 3286 + } else { 3287 + ret = 0; 3288 + } 3289 + 3290 + return ret; 3291 + } 3292 + 3293 + static void hci_prepare_suspend(struct work_struct *work) 3294 + { 3295 + struct hci_dev *hdev = 3296 + container_of(work, struct hci_dev, suspend_prepare); 3297 + 3298 + hci_dev_lock(hdev); 3299 + hci_req_prepare_suspend(hdev, hdev->suspend_state_next); 3300 + hci_dev_unlock(hdev); 3301 + } 3302 + 3303 + static int hci_suspend_notifier(struct notifier_block *nb, unsigned long action, 3304 + void *data) 3305 + { 3306 + struct hci_dev *hdev = 3307 + container_of(nb, struct hci_dev, suspend_notifier); 3308 + int ret = 0; 3309 + 3310 + /* If powering down, wait for completion. */ 3311 + if (mgmt_powering_down(hdev)) { 3312 + set_bit(SUSPEND_POWERING_DOWN, hdev->suspend_tasks); 3313 + ret = hci_suspend_wait_event(hdev); 3314 + if (ret) 3315 + goto done; 3316 + } 3317 + 3318 + /* Suspend notifier should only act on events when powered. */ 3319 + if (!hdev_is_powered(hdev)) 3320 + goto done; 3321 + 3322 + if (action == PM_SUSPEND_PREPARE) { 3323 + hdev->suspend_state_next = BT_SUSPENDED; 3324 + set_bit(SUSPEND_PREPARE_NOTIFIER, hdev->suspend_tasks); 3325 + queue_work(hdev->req_workqueue, &hdev->suspend_prepare); 3326 + 3327 + ret = hci_suspend_wait_event(hdev); 3328 + } else if (action == PM_POST_SUSPEND) { 3329 + hdev->suspend_state_next = BT_RUNNING; 3330 + set_bit(SUSPEND_PREPARE_NOTIFIER, hdev->suspend_tasks); 3331 + queue_work(hdev->req_workqueue, &hdev->suspend_prepare); 3332 + 3333 + ret = hci_suspend_wait_event(hdev); 3334 + } 3335 + 3336 + done: 3337 + return ret ? notifier_from_errno(-EBUSY) : NOTIFY_STOP; 3338 + } 3272 3339 /* Alloc HCI device */ 3273 3340 struct hci_dev *hci_alloc_dev(void) 3274 3341 { ··· 3418 3341 INIT_WORK(&hdev->tx_work, hci_tx_work); 3419 3342 INIT_WORK(&hdev->power_on, hci_power_on); 3420 3343 INIT_WORK(&hdev->error_reset, hci_error_reset); 3344 + INIT_WORK(&hdev->suspend_prepare, hci_prepare_suspend); 3421 3345 3422 3346 INIT_DELAYED_WORK(&hdev->power_off, hci_power_off); 3423 3347 ··· 3427 3349 skb_queue_head_init(&hdev->raw_q); 3428 3350 3429 3351 init_waitqueue_head(&hdev->req_wait_q); 3352 + init_waitqueue_head(&hdev->suspend_wait_q); 3430 3353 3431 3354 INIT_DELAYED_WORK(&hdev->cmd_timer, hci_cmd_timeout); 3432 3355 ··· 3539 3460 hci_sock_dev_event(hdev, HCI_DEV_REG); 3540 3461 hci_dev_hold(hdev); 3541 3462 3463 + hdev->suspend_notifier.notifier_call = hci_suspend_notifier; 3464 + error = register_pm_notifier(&hdev->suspend_notifier); 3465 + if (error) 3466 + goto err_wqueue; 3467 + 3542 3468 queue_work(hdev->req_workqueue, &hdev->power_on); 3543 3469 3544 3470 return id; ··· 3576 3492 cancel_work_sync(&hdev->power_on); 3577 3493 3578 3494 hci_dev_do_close(hdev); 3495 + 3496 + unregister_pm_notifier(&hdev->suspend_notifier); 3579 3497 3580 3498 if (!test_bit(HCI_INIT, &hdev->flags) && 3581 3499 !hci_dev_test_flag(hdev, HCI_SETUP) &&
+15
net/bluetooth/hci_request.c
··· 918 918 return adv_instance->scan_rsp_len; 919 919 } 920 920 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 + 921 936 static u8 get_cur_adv_instance_scan_rsp_len(struct hci_dev *hdev) 922 937 { 923 938 u8 instance = hdev->cur_adv_instance;
+2
net/bluetooth/hci_request.h
··· 68 68 void hci_req_add_le_scan_disable(struct hci_request *req); 69 69 void hci_req_add_le_passive_scan(struct hci_request *req); 70 70 71 + void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next); 72 + 71 73 void hci_req_reenable_advertising(struct hci_dev *hdev); 72 74 void __hci_req_enable_advertising(struct hci_request *req); 73 75 void __hci_req_disable_advertising(struct hci_request *req);