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

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>

authored by

Abhishek Pandit-Subedi and committed by
Marcel Holtmann
4f40afc6 9952d90e

+169 -10
+11 -6
include/net/bluetooth/hci.h
··· 942 942 #define HCI_OP_RESET 0x0c03 943 943 944 944 #define HCI_OP_SET_EVENT_FLT 0x0c05 945 - struct hci_cp_set_event_flt { 946 - __u8 flt_type; 947 - __u8 cond_type; 948 - __u8 condition[]; 945 + #define HCI_SET_EVENT_FLT_SIZE 9 946 + struct hci_cp_set_event_filter { 947 + __u8 flt_type; 948 + __u8 cond_type; 949 + struct { 950 + bdaddr_t bdaddr; 951 + __u8 auto_accept; 952 + } __packed addr_conn_flt; 949 953 } __packed; 950 954 951 955 /* Filter types */ ··· 963 959 #define HCI_CONN_SETUP_ALLOW_BDADDR 0x02 964 960 965 961 /* CONN_SETUP Conditions */ 966 - #define HCI_CONN_SETUP_AUTO_OFF 0x01 967 - #define HCI_CONN_SETUP_AUTO_ON 0x02 962 + #define HCI_CONN_SETUP_AUTO_OFF 0x01 963 + #define HCI_CONN_SETUP_AUTO_ON 0x02 964 + #define HCI_CONN_SETUP_AUTO_ON_WITH_RS 0x03 968 965 969 966 #define HCI_OP_READ_STORED_LINK_KEY 0x0c0d 970 967 struct hci_cp_read_stored_link_key {
+9 -1
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_SCAN_DISABLE, 95 + SUSPEND_SCAN_ENABLE, 96 + SUSPEND_DISCONNECTING, 97 + 94 98 SUSPEND_POWERING_DOWN, 95 99 96 100 SUSPEND_PREPARE_NOTIFIER, ··· 103 99 104 100 enum suspended_state { 105 101 BT_RUNNING = 0, 106 - BT_SUSPENDED, 102 + BT_SUSPEND_DISCONNECT, 103 + BT_SUSPEND_COMPLETE, 107 104 }; 108 105 109 106 struct hci_conn_hash { ··· 414 409 struct work_struct suspend_prepare; 415 410 enum suspended_state suspend_state_next; 416 411 enum suspended_state suspend_state; 412 + bool scanning_paused; 413 + bool suspended; 417 414 418 415 wait_queue_head_t suspend_wait_q; 419 416 DECLARE_BITMAP(suspend_tasks, __SUSPEND_NUM_TASKS); ··· 425 418 struct list_head mgmt_pending; 426 419 struct list_head blacklist; 427 420 struct list_head whitelist; 421 + struct list_head wakeable; 428 422 struct list_head uuids; 429 423 struct list_head link_keys; 430 424 struct list_head long_term_keys;
+19 -3
net/bluetooth/hci_core.c
··· 3325 3325 goto done; 3326 3326 3327 3327 if (action == PM_SUSPEND_PREPARE) { 3328 - hdev->suspend_state_next = BT_SUSPENDED; 3328 + /* Suspend consists of two actions: 3329 + * - First, disconnect everything and make the controller not 3330 + * connectable (disabling scanning) 3331 + * - Second, program event filter/whitelist and enable scan 3332 + */ 3333 + hdev->suspend_state_next = BT_SUSPEND_DISCONNECT; 3329 3334 set_bit(SUSPEND_PREPARE_NOTIFIER, hdev->suspend_tasks); 3330 3335 queue_work(hdev->req_workqueue, &hdev->suspend_prepare); 3331 - 3332 3336 ret = hci_suspend_wait_event(hdev); 3337 + 3338 + /* If the disconnect portion failed, don't attempt to complete 3339 + * by configuring the whitelist. The suspend notifier will 3340 + * follow a cancelled suspend with a PM_POST_SUSPEND 3341 + * notification. 3342 + */ 3343 + if (!ret) { 3344 + hdev->suspend_state_next = BT_SUSPEND_COMPLETE; 3345 + set_bit(SUSPEND_PREPARE_NOTIFIER, hdev->suspend_tasks); 3346 + queue_work(hdev->req_workqueue, &hdev->suspend_prepare); 3347 + ret = hci_suspend_wait_event(hdev); 3348 + } 3333 3349 } else if (action == PM_POST_SUSPEND) { 3334 3350 hdev->suspend_state_next = BT_RUNNING; 3335 3351 set_bit(SUSPEND_PREPARE_NOTIFIER, hdev->suspend_tasks); 3336 3352 queue_work(hdev->req_workqueue, &hdev->suspend_prepare); 3337 - 3338 3353 ret = hci_suspend_wait_event(hdev); 3339 3354 } 3340 3355 ··· 3414 3399 INIT_LIST_HEAD(&hdev->mgmt_pending); 3415 3400 INIT_LIST_HEAD(&hdev->blacklist); 3416 3401 INIT_LIST_HEAD(&hdev->whitelist); 3402 + INIT_LIST_HEAD(&hdev->wakeable); 3417 3403 INIT_LIST_HEAD(&hdev->uuids); 3418 3404 INIT_LIST_HEAD(&hdev->link_keys); 3419 3405 INIT_LIST_HEAD(&hdev->long_term_keys);
+24
net/bluetooth/hci_event.c
··· 2505 2505 static void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) 2506 2506 { 2507 2507 struct hci_ev_conn_complete *ev = (void *) skb->data; 2508 + struct inquiry_entry *ie; 2508 2509 struct hci_conn *conn; 2509 2510 2510 2511 BT_DBG("%s", hdev->name); ··· 2514 2513 2515 2514 conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr); 2516 2515 if (!conn) { 2516 + /* Connection may not exist if auto-connected. Check the inquiry 2517 + * cache to see if we've already discovered this bdaddr before. 2518 + * If found and link is an ACL type, create a connection class 2519 + * automatically. 2520 + */ 2521 + ie = hci_inquiry_cache_lookup(hdev, &ev->bdaddr); 2522 + if (ie && ev->link_type == ACL_LINK) { 2523 + conn = hci_conn_add(hdev, ev->link_type, &ev->bdaddr, 2524 + HCI_ROLE_SLAVE); 2525 + if (!conn) { 2526 + bt_dev_err(hdev, "no memory for new conn"); 2527 + goto unlock; 2528 + } 2529 + } 2530 + 2517 2531 if (ev->link_type != SCO_LINK) 2518 2532 goto unlock; 2519 2533 ··· 2789 2773 2790 2774 hci_disconn_cfm(conn, ev->reason); 2791 2775 hci_conn_del(conn); 2776 + 2777 + /* The suspend notifier is waiting for all devices to disconnect so 2778 + * clear the bit from pending tasks and inform the wait queue. 2779 + */ 2780 + if (list_empty(&hdev->conn_hash.list) && 2781 + test_and_clear_bit(SUSPEND_DISCONNECTING, hdev->suspend_tasks)) { 2782 + wake_up(&hdev->suspend_wait_q); 2783 + } 2792 2784 2793 2785 /* Re-enable advertising if necessary, since it might 2794 2786 * have been disabled by the connection. From the
+106
net/bluetooth/hci_request.c
··· 918 918 return adv_instance->scan_rsp_len; 919 919 } 920 920 921 + static void hci_req_clear_event_filter(struct hci_request *req) 922 + { 923 + struct hci_cp_set_event_filter f; 924 + 925 + memset(&f, 0, sizeof(f)); 926 + f.flt_type = HCI_FLT_CLEAR_ALL; 927 + hci_req_add(req, HCI_OP_SET_EVENT_FLT, 1, &f); 928 + 929 + /* Update page scan state (since we may have modified it when setting 930 + * the event filter). 931 + */ 932 + __hci_req_update_scan(req); 933 + } 934 + 935 + static void hci_req_set_event_filter(struct hci_request *req) 936 + { 937 + struct bdaddr_list *b; 938 + struct hci_cp_set_event_filter f; 939 + struct hci_dev *hdev = req->hdev; 940 + u8 scan; 941 + 942 + /* Always clear event filter when starting */ 943 + hci_req_clear_event_filter(req); 944 + 945 + list_for_each_entry(b, &hdev->wakeable, list) { 946 + memset(&f, 0, sizeof(f)); 947 + bacpy(&f.addr_conn_flt.bdaddr, &b->bdaddr); 948 + f.flt_type = HCI_FLT_CONN_SETUP; 949 + f.cond_type = HCI_CONN_SETUP_ALLOW_BDADDR; 950 + f.addr_conn_flt.auto_accept = HCI_CONN_SETUP_AUTO_ON; 951 + 952 + bt_dev_dbg(hdev, "Adding event filters for %pMR", &b->bdaddr); 953 + hci_req_add(req, HCI_OP_SET_EVENT_FLT, sizeof(f), &f); 954 + } 955 + 956 + scan = !list_empty(&hdev->wakeable) ? SCAN_PAGE : SCAN_DISABLED; 957 + hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); 958 + } 959 + 960 + static void suspend_req_complete(struct hci_dev *hdev, u8 status, u16 opcode) 961 + { 962 + bt_dev_dbg(hdev, "Request complete opcode=0x%x, status=0x%x", opcode, 963 + status); 964 + if (test_and_clear_bit(SUSPEND_SCAN_ENABLE, hdev->suspend_tasks) || 965 + test_and_clear_bit(SUSPEND_SCAN_DISABLE, hdev->suspend_tasks)) { 966 + wake_up(&hdev->suspend_wait_q); 967 + } 968 + } 969 + 921 970 /* Call with hci_dev_lock */ 922 971 void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next) 923 972 { 973 + struct hci_conn *conn; 974 + struct hci_request req; 975 + u8 page_scan; 976 + int disconnect_counter; 977 + 924 978 if (next == hdev->suspend_state) { 925 979 bt_dev_dbg(hdev, "Same state before and after: %d", next); 926 980 goto done; 981 + } 982 + 983 + hdev->suspend_state = next; 984 + hci_req_init(&req, hdev); 985 + 986 + if (next == BT_SUSPEND_DISCONNECT) { 987 + /* Mark device as suspended */ 988 + hdev->suspended = true; 989 + 990 + /* Disable page scan */ 991 + page_scan = SCAN_DISABLED; 992 + hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &page_scan); 993 + 994 + /* Mark task needing completion */ 995 + set_bit(SUSPEND_SCAN_DISABLE, hdev->suspend_tasks); 996 + 997 + /* Prevent disconnects from causing scanning to be re-enabled */ 998 + hdev->scanning_paused = true; 999 + 1000 + /* Run commands before disconnecting */ 1001 + hci_req_run(&req, suspend_req_complete); 1002 + 1003 + disconnect_counter = 0; 1004 + /* Soft disconnect everything (power off) */ 1005 + list_for_each_entry(conn, &hdev->conn_hash.list, list) { 1006 + hci_disconnect(conn, HCI_ERROR_REMOTE_POWER_OFF); 1007 + disconnect_counter++; 1008 + } 1009 + 1010 + if (disconnect_counter > 0) { 1011 + bt_dev_dbg(hdev, 1012 + "Had %d disconnects. Will wait on them", 1013 + disconnect_counter); 1014 + set_bit(SUSPEND_DISCONNECTING, hdev->suspend_tasks); 1015 + } 1016 + } else if (next == BT_SUSPEND_COMPLETE) { 1017 + /* Unpause to take care of updating scanning params */ 1018 + hdev->scanning_paused = false; 1019 + /* Enable event filter for paired devices */ 1020 + hci_req_set_event_filter(&req); 1021 + /* Pause scan changes again. */ 1022 + hdev->scanning_paused = true; 1023 + hci_req_run(&req, suspend_req_complete); 1024 + } else { 1025 + hdev->suspended = false; 1026 + hdev->scanning_paused = false; 1027 + 1028 + hci_req_clear_event_filter(&req); 1029 + hci_req_run(&req, suspend_req_complete); 927 1030 } 928 1031 929 1032 hdev->suspend_state = next; ··· 2131 2028 return; 2132 2029 2133 2030 if (mgmt_powering_down(hdev)) 2031 + return; 2032 + 2033 + if (hdev->scanning_paused) 2134 2034 return; 2135 2035 2136 2036 if (hci_dev_test_flag(hdev, HCI_CONNECTABLE) ||