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

Bluetooth: Pause discovery and advertising during suspend

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

authored by

Abhishek Pandit-Subedi and committed by
Marcel Holtmann
4867bd00 dd522a74

+96
+11
include/net/bluetooth/hci_core.h
··· 91 91 #define SUSPEND_NOTIFIER_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ 92 92 93 93 enum suspend_tasks { 94 + SUSPEND_PAUSE_DISCOVERY, 95 + SUSPEND_UNPAUSE_DISCOVERY, 96 + 97 + SUSPEND_PAUSE_ADVERTISING, 98 + SUSPEND_UNPAUSE_ADVERTISING, 99 + 94 100 SUSPEND_SCAN_DISABLE, 95 101 SUSPEND_SCAN_ENABLE, 96 102 SUSPEND_DISCONNECTING, ··· 415 409 void *smp_bredr_data; 416 410 417 411 struct discovery_state discovery; 412 + 413 + int discovery_old_state; 414 + bool discovery_paused; 415 + int advertising_old_state; 416 + bool advertising_paused; 418 417 419 418 struct notifier_block suspend_notifier; 420 419 struct work_struct suspend_prepare;
+44
net/bluetooth/hci_request.c
··· 1021 1021 /* Call with hci_dev_lock */ 1022 1022 void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next) 1023 1023 { 1024 + int old_state; 1024 1025 struct hci_conn *conn; 1025 1026 struct hci_request req; 1026 1027 u8 page_scan; ··· 1039 1038 /* Mark device as suspended */ 1040 1039 hdev->suspended = true; 1041 1040 1041 + /* Pause discovery if not already stopped */ 1042 + old_state = hdev->discovery.state; 1043 + if (old_state != DISCOVERY_STOPPED) { 1044 + set_bit(SUSPEND_PAUSE_DISCOVERY, hdev->suspend_tasks); 1045 + hci_discovery_set_state(hdev, DISCOVERY_STOPPING); 1046 + queue_work(hdev->req_workqueue, &hdev->discov_update); 1047 + } 1048 + 1049 + hdev->discovery_paused = true; 1050 + hdev->discovery_old_state = old_state; 1051 + 1052 + /* Stop advertising */ 1053 + old_state = hci_dev_test_flag(hdev, HCI_ADVERTISING); 1054 + if (old_state) { 1055 + set_bit(SUSPEND_PAUSE_ADVERTISING, hdev->suspend_tasks); 1056 + cancel_delayed_work(&hdev->discov_off); 1057 + queue_delayed_work(hdev->req_workqueue, 1058 + &hdev->discov_off, 0); 1059 + } 1060 + 1061 + hdev->advertising_paused = true; 1062 + hdev->advertising_old_state = old_state; 1042 1063 /* Disable page scan */ 1043 1064 page_scan = SCAN_DISABLED; 1044 1065 hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &page_scan); ··· 1107 1084 hci_req_clear_event_filter(&req); 1108 1085 /* Reset passive/background scanning to normal */ 1109 1086 hci_req_config_le_suspend_scan(&req); 1087 + 1088 + /* Unpause advertising */ 1089 + hdev->advertising_paused = false; 1090 + if (hdev->advertising_old_state) { 1091 + set_bit(SUSPEND_UNPAUSE_ADVERTISING, 1092 + hdev->suspend_tasks); 1093 + hci_dev_set_flag(hdev, HCI_ADVERTISING); 1094 + queue_work(hdev->req_workqueue, 1095 + &hdev->discoverable_update); 1096 + hdev->advertising_old_state = 0; 1097 + } 1098 + 1099 + /* Unpause discovery */ 1100 + hdev->discovery_paused = false; 1101 + if (hdev->discovery_old_state != DISCOVERY_STOPPED && 1102 + hdev->discovery_old_state != DISCOVERY_STOPPING) { 1103 + set_bit(SUSPEND_UNPAUSE_DISCOVERY, hdev->suspend_tasks); 1104 + hci_discovery_set_state(hdev, DISCOVERY_STARTING); 1105 + queue_work(hdev->req_workqueue, &hdev->discov_update); 1106 + } 1107 + 1110 1108 hci_req_run(&req, suspend_req_complete); 1111 1109 } 1112 1110
+41
net/bluetooth/mgmt.c
··· 1390 1390 goto failed; 1391 1391 } 1392 1392 1393 + if (hdev->advertising_paused) { 1394 + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, 1395 + MGMT_STATUS_BUSY); 1396 + goto failed; 1397 + } 1398 + 1393 1399 if (!hdev_is_powered(hdev)) { 1394 1400 bool changed = false; 1395 1401 ··· 3935 3929 } 3936 3930 3937 3931 hci_dev_unlock(hdev); 3932 + 3933 + /* Handle suspend notifier */ 3934 + if (test_and_clear_bit(SUSPEND_UNPAUSE_DISCOVERY, 3935 + hdev->suspend_tasks)) { 3936 + bt_dev_dbg(hdev, "Unpaused discovery"); 3937 + wake_up(&hdev->suspend_wait_q); 3938 + } 3938 3939 } 3939 3940 3940 3941 static bool discovery_type_is_valid(struct hci_dev *hdev, uint8_t type, ··· 3999 3986 4000 3987 if (!discovery_type_is_valid(hdev, cp->type, &status)) { 4001 3988 err = mgmt_cmd_complete(sk, hdev->id, op, status, 3989 + &cp->type, sizeof(cp->type)); 3990 + goto failed; 3991 + } 3992 + 3993 + /* Can't start discovery when it is paused */ 3994 + if (hdev->discovery_paused) { 3995 + err = mgmt_cmd_complete(sk, hdev->id, op, MGMT_STATUS_BUSY, 4002 3996 &cp->type, sizeof(cp->type)); 4003 3997 goto failed; 4004 3998 } ··· 4177 4157 } 4178 4158 4179 4159 hci_dev_unlock(hdev); 4160 + 4161 + /* Handle suspend notifier */ 4162 + if (test_and_clear_bit(SUSPEND_PAUSE_DISCOVERY, hdev->suspend_tasks)) { 4163 + bt_dev_dbg(hdev, "Paused discovery"); 4164 + wake_up(&hdev->suspend_wait_q); 4165 + } 4180 4166 } 4181 4167 4182 4168 static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data, ··· 4414 4388 if (match.sk) 4415 4389 sock_put(match.sk); 4416 4390 4391 + /* Handle suspend notifier */ 4392 + if (test_and_clear_bit(SUSPEND_PAUSE_ADVERTISING, 4393 + hdev->suspend_tasks)) { 4394 + bt_dev_dbg(hdev, "Paused advertising"); 4395 + wake_up(&hdev->suspend_wait_q); 4396 + } else if (test_and_clear_bit(SUSPEND_UNPAUSE_ADVERTISING, 4397 + hdev->suspend_tasks)) { 4398 + bt_dev_dbg(hdev, "Unpaused advertising"); 4399 + wake_up(&hdev->suspend_wait_q); 4400 + } 4401 + 4417 4402 /* If "Set Advertising" was just disabled and instance advertising was 4418 4403 * set up earlier, then re-enable multi-instance advertising. 4419 4404 */ ··· 4475 4438 if (cp->val != 0x00 && cp->val != 0x01 && cp->val != 0x02) 4476 4439 return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING, 4477 4440 MGMT_STATUS_INVALID_PARAMS); 4441 + 4442 + if (hdev->advertising_paused) 4443 + return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING, 4444 + MGMT_STATUS_BUSY); 4478 4445 4479 4446 hci_dev_lock(hdev); 4480 4447