Bluetooth: Handle LE devices during suspend
To handle LE devices, we must first disable passive scanning and disconnect all connected devices. Once that is complete, we update the whitelist and re-enable scanning Signed-off-by: Abhishek Pandit-Subedi <abhishekpandit@chromium.org> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
This commit is contained in:
		
							parent
							
								
									4f40afc6c7
								
							
						
					
					
						commit
						dd522a7429
					
				| @ -607,6 +607,7 @@ struct hci_conn_params { | ||||
| 
 | ||||
| 	struct hci_conn *conn; | ||||
| 	bool explicit_connect; | ||||
| 	bool wakeable; | ||||
| }; | ||||
| 
 | ||||
| extern struct list_head hci_dev_list; | ||||
|  | ||||
| @ -34,6 +34,9 @@ | ||||
| #define HCI_REQ_PEND	  1 | ||||
| #define HCI_REQ_CANCELED  2 | ||||
| 
 | ||||
| #define LE_SUSPEND_SCAN_WINDOW		0x0012 | ||||
| #define LE_SUSPEND_SCAN_INTERVAL	0x0060 | ||||
| 
 | ||||
| void hci_req_init(struct hci_request *req, struct hci_dev *hdev) | ||||
| { | ||||
| 	skb_queue_head_init(&req->cmd_q); | ||||
| @ -654,6 +657,11 @@ void hci_req_add_le_scan_disable(struct hci_request *req) | ||||
| { | ||||
| 	struct hci_dev *hdev = req->hdev; | ||||
| 
 | ||||
| 	if (hdev->scanning_paused) { | ||||
| 		bt_dev_dbg(hdev, "Scanning is paused for suspend"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (use_ext_scan(hdev)) { | ||||
| 		struct hci_cp_le_set_ext_scan_enable cp; | ||||
| 
 | ||||
| @ -670,15 +678,55 @@ void hci_req_add_le_scan_disable(struct hci_request *req) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void add_to_white_list(struct hci_request *req, | ||||
| 			      struct hci_conn_params *params) | ||||
| static void del_from_white_list(struct hci_request *req, bdaddr_t *bdaddr, | ||||
| 				u8 bdaddr_type) | ||||
| { | ||||
| 	struct hci_cp_le_del_from_white_list cp; | ||||
| 
 | ||||
| 	cp.bdaddr_type = bdaddr_type; | ||||
| 	bacpy(&cp.bdaddr, bdaddr); | ||||
| 
 | ||||
| 	bt_dev_dbg(req->hdev, "Remove %pMR (0x%x) from whitelist", &cp.bdaddr, | ||||
| 		   cp.bdaddr_type); | ||||
| 	hci_req_add(req, HCI_OP_LE_DEL_FROM_WHITE_LIST, sizeof(cp), &cp); | ||||
| } | ||||
| 
 | ||||
| /* Adds connection to white list if needed. On error, returns -1. */ | ||||
| static int add_to_white_list(struct hci_request *req, | ||||
| 			     struct hci_conn_params *params, u8 *num_entries, | ||||
| 			     bool allow_rpa) | ||||
| { | ||||
| 	struct hci_cp_le_add_to_white_list cp; | ||||
| 	struct hci_dev *hdev = req->hdev; | ||||
| 
 | ||||
| 	/* Already in white list */ | ||||
| 	if (hci_bdaddr_list_lookup(&hdev->le_white_list, ¶ms->addr, | ||||
| 				   params->addr_type)) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	/* Select filter policy to accept all advertising */ | ||||
| 	if (*num_entries >= hdev->le_white_list_size) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	/* White list can not be used with RPAs */ | ||||
| 	if (!allow_rpa && | ||||
| 	    hci_find_irk_by_addr(hdev, ¶ms->addr, params->addr_type)) { | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	/* During suspend, only wakeable devices can be in whitelist */ | ||||
| 	if (hdev->suspended && !params->wakeable) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	*num_entries += 1; | ||||
| 	cp.bdaddr_type = params->addr_type; | ||||
| 	bacpy(&cp.bdaddr, ¶ms->addr); | ||||
| 
 | ||||
| 	bt_dev_dbg(hdev, "Add %pMR (0x%x) to whitelist", &cp.bdaddr, | ||||
| 		   cp.bdaddr_type); | ||||
| 	hci_req_add(req, HCI_OP_LE_ADD_TO_WHITE_LIST, sizeof(cp), &cp); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static u8 update_white_list(struct hci_request *req) | ||||
| @ -686,7 +734,14 @@ static u8 update_white_list(struct hci_request *req) | ||||
| 	struct hci_dev *hdev = req->hdev; | ||||
| 	struct hci_conn_params *params; | ||||
| 	struct bdaddr_list *b; | ||||
| 	uint8_t white_list_entries = 0; | ||||
| 	u8 num_entries = 0; | ||||
| 	bool pend_conn, pend_report; | ||||
| 	/* We allow whitelisting even with RPAs in suspend. In the worst case,
 | ||||
| 	 * we won't be able to wake from devices that use the privacy1.2 | ||||
| 	 * features. Additionally, once we support privacy1.2 and IRK | ||||
| 	 * offloading, we can update this to also check for those conditions. | ||||
| 	 */ | ||||
| 	bool allow_rpa = hdev->suspended; | ||||
| 
 | ||||
| 	/* Go through the current white list programmed into the
 | ||||
| 	 * controller one by one and check if that address is still | ||||
| @ -695,29 +750,28 @@ static u8 update_white_list(struct hci_request *req) | ||||
| 	 * command to remove it from the controller. | ||||
| 	 */ | ||||
| 	list_for_each_entry(b, &hdev->le_white_list, list) { | ||||
| 		/* If the device is neither in pend_le_conns nor
 | ||||
| 		 * pend_le_reports then remove it from the whitelist. | ||||
| 		pend_conn = hci_pend_le_action_lookup(&hdev->pend_le_conns, | ||||
| 						      &b->bdaddr, | ||||
| 						      b->bdaddr_type); | ||||
| 		pend_report = hci_pend_le_action_lookup(&hdev->pend_le_reports, | ||||
| 							&b->bdaddr, | ||||
| 							b->bdaddr_type); | ||||
| 
 | ||||
| 		/* If the device is not likely to connect or report,
 | ||||
| 		 * remove it from the whitelist. | ||||
| 		 */ | ||||
| 		if (!hci_pend_le_action_lookup(&hdev->pend_le_conns, | ||||
| 					       &b->bdaddr, b->bdaddr_type) && | ||||
| 		    !hci_pend_le_action_lookup(&hdev->pend_le_reports, | ||||
| 					       &b->bdaddr, b->bdaddr_type)) { | ||||
| 			struct hci_cp_le_del_from_white_list cp; | ||||
| 
 | ||||
| 			cp.bdaddr_type = b->bdaddr_type; | ||||
| 			bacpy(&cp.bdaddr, &b->bdaddr); | ||||
| 
 | ||||
| 			hci_req_add(req, HCI_OP_LE_DEL_FROM_WHITE_LIST, | ||||
| 				    sizeof(cp), &cp); | ||||
| 		if (!pend_conn && !pend_report) { | ||||
| 			del_from_white_list(req, &b->bdaddr, b->bdaddr_type); | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		if (hci_find_irk_by_addr(hdev, &b->bdaddr, b->bdaddr_type)) { | ||||
| 			/* White list can not be used with RPAs */ | ||||
| 		/* White list can not be used with RPAs */ | ||||
| 		if (!allow_rpa && | ||||
| 		    hci_find_irk_by_addr(hdev, &b->bdaddr, b->bdaddr_type)) { | ||||
| 			return 0x00; | ||||
| 		} | ||||
| 
 | ||||
| 		white_list_entries++; | ||||
| 		num_entries++; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Since all no longer valid white list entries have been
 | ||||
| @ -731,47 +785,17 @@ static u8 update_white_list(struct hci_request *req) | ||||
| 	 * white list. | ||||
| 	 */ | ||||
| 	list_for_each_entry(params, &hdev->pend_le_conns, action) { | ||||
| 		if (hci_bdaddr_list_lookup(&hdev->le_white_list, | ||||
| 					   ¶ms->addr, params->addr_type)) | ||||
| 			continue; | ||||
| 
 | ||||
| 		if (white_list_entries >= hdev->le_white_list_size) { | ||||
| 			/* Select filter policy to accept all advertising */ | ||||
| 		if (add_to_white_list(req, params, &num_entries, allow_rpa)) | ||||
| 			return 0x00; | ||||
| 		} | ||||
| 
 | ||||
| 		if (hci_find_irk_by_addr(hdev, ¶ms->addr, | ||||
| 					 params->addr_type)) { | ||||
| 			/* White list can not be used with RPAs */ | ||||
| 			return 0x00; | ||||
| 		} | ||||
| 
 | ||||
| 		white_list_entries++; | ||||
| 		add_to_white_list(req, params); | ||||
| 	} | ||||
| 
 | ||||
| 	/* After adding all new pending connections, walk through
 | ||||
| 	 * the list of pending reports and also add these to the | ||||
| 	 * white list if there is still space. | ||||
| 	 * white list if there is still space. Abort if space runs out. | ||||
| 	 */ | ||||
| 	list_for_each_entry(params, &hdev->pend_le_reports, action) { | ||||
| 		if (hci_bdaddr_list_lookup(&hdev->le_white_list, | ||||
| 					   ¶ms->addr, params->addr_type)) | ||||
| 			continue; | ||||
| 
 | ||||
| 		if (white_list_entries >= hdev->le_white_list_size) { | ||||
| 			/* Select filter policy to accept all advertising */ | ||||
| 		if (add_to_white_list(req, params, &num_entries, allow_rpa)) | ||||
| 			return 0x00; | ||||
| 		} | ||||
| 
 | ||||
| 		if (hci_find_irk_by_addr(hdev, ¶ms->addr, | ||||
| 					 params->addr_type)) { | ||||
| 			/* White list can not be used with RPAs */ | ||||
| 			return 0x00; | ||||
| 		} | ||||
| 
 | ||||
| 		white_list_entries++; | ||||
| 		add_to_white_list(req, params); | ||||
| 	} | ||||
| 
 | ||||
| 	/* Select filter policy to use white list */ | ||||
| @ -866,6 +890,12 @@ void hci_req_add_le_passive_scan(struct hci_request *req) | ||||
| 	struct hci_dev *hdev = req->hdev; | ||||
| 	u8 own_addr_type; | ||||
| 	u8 filter_policy; | ||||
| 	u8 window, interval; | ||||
| 
 | ||||
| 	if (hdev->scanning_paused) { | ||||
| 		bt_dev_dbg(hdev, "Scanning is paused for suspend"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Set require_privacy to false since no SCAN_REQ are send
 | ||||
| 	 * during passive scanning. Not using an non-resolvable address | ||||
| @ -896,8 +926,17 @@ void hci_req_add_le_passive_scan(struct hci_request *req) | ||||
| 	    (hdev->le_features[0] & HCI_LE_EXT_SCAN_POLICY)) | ||||
| 		filter_policy |= 0x02; | ||||
| 
 | ||||
| 	hci_req_start_scan(req, LE_SCAN_PASSIVE, hdev->le_scan_interval, | ||||
| 			   hdev->le_scan_window, own_addr_type, filter_policy); | ||||
| 	if (hdev->suspended) { | ||||
| 		window = LE_SUSPEND_SCAN_WINDOW; | ||||
| 		interval = LE_SUSPEND_SCAN_INTERVAL; | ||||
| 	} else { | ||||
| 		window = hdev->le_scan_window; | ||||
| 		interval = hdev->le_scan_interval; | ||||
| 	} | ||||
| 
 | ||||
| 	bt_dev_dbg(hdev, "LE passive scan with whitelist = %d", filter_policy); | ||||
| 	hci_req_start_scan(req, LE_SCAN_PASSIVE, interval, window, | ||||
| 			   own_addr_type, filter_policy); | ||||
| } | ||||
| 
 | ||||
| static u8 get_adv_instance_scan_rsp_len(struct hci_dev *hdev, u8 instance) | ||||
| @ -957,6 +996,18 @@ static void hci_req_set_event_filter(struct hci_request *req) | ||||
| 	hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); | ||||
| } | ||||
| 
 | ||||
| static void hci_req_config_le_suspend_scan(struct hci_request *req) | ||||
| { | ||||
| 	/* Can't change params without disabling first */ | ||||
| 	hci_req_add_le_scan_disable(req); | ||||
| 
 | ||||
| 	/* Configure params and enable scanning */ | ||||
| 	hci_req_add_le_passive_scan(req); | ||||
| 
 | ||||
| 	/* Block suspend notifier on response */ | ||||
| 	set_bit(SUSPEND_SCAN_ENABLE, req->hdev->suspend_tasks); | ||||
| } | ||||
| 
 | ||||
| static void suspend_req_complete(struct hci_dev *hdev, u8 status, u16 opcode) | ||||
| { | ||||
| 	bt_dev_dbg(hdev, "Request complete opcode=0x%x, status=0x%x", opcode, | ||||
| @ -991,6 +1042,9 @@ void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next) | ||||
| 		page_scan = SCAN_DISABLED; | ||||
| 		hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &page_scan); | ||||
| 
 | ||||
| 		/* Disable LE passive scan */ | ||||
| 		hci_req_add_le_scan_disable(&req); | ||||
| 
 | ||||
| 		/* Mark task needing completion */ | ||||
| 		set_bit(SUSPEND_SCAN_DISABLE, hdev->suspend_tasks); | ||||
| 
 | ||||
| @ -1018,6 +1072,8 @@ void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next) | ||||
| 		hdev->scanning_paused = false; | ||||
| 		/* Enable event filter for paired devices */ | ||||
| 		hci_req_set_event_filter(&req); | ||||
| 		/* Enable passive scan at lower duty cycle */ | ||||
| 		hci_req_config_le_suspend_scan(&req); | ||||
| 		/* Pause scan changes again. */ | ||||
| 		hdev->scanning_paused = true; | ||||
| 		hci_req_run(&req, suspend_req_complete); | ||||
| @ -1026,6 +1082,8 @@ void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next) | ||||
| 		hdev->scanning_paused = false; | ||||
| 
 | ||||
| 		hci_req_clear_event_filter(&req); | ||||
| 		/* Reset passive/background scanning to normal */ | ||||
| 		hci_req_config_le_suspend_scan(&req); | ||||
| 		hci_req_run(&req, suspend_req_complete); | ||||
| 	} | ||||
| 
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user