Skip to content

Commit 8b064a3

Browse files
Johan Hedbergholtmann
Johan Hedberg
authored andcommitted
Bluetooth: Clean up HCI state when doing power off
To be friendly to user space and to behave well with controllers that lack a proper internal power off procedure we should try to clean up as much state as possible before requesting the HCI driver to power off. This patch updates the power off procedure that's triggered by mgmt_set_powered to clean any scan modes, stop LE scanning and advertising and to disconnect any open connections. The asynchronous cleanup procedure uses the HCI request framework, however since HCI_Disconnect is only covered until its Command Status event we need some extra tracking/waiting of disconnections. This is done by monitoring when hci_conn_count() indicates that there are no more connections. Signed-off-by: Johan Hedberg <[email protected]> Signed-off-by: Marcel Holtmann <[email protected]>
1 parent 7c4cfab commit 8b064a3

File tree

1 file changed

+66
-4
lines changed

1 file changed

+66
-4
lines changed

net/bluetooth/mgmt.c

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,6 +1026,49 @@ static int send_settings_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev)
10261026
sizeof(settings));
10271027
}
10281028

1029+
static void clean_up_hci_complete(struct hci_dev *hdev, u8 status)
1030+
{
1031+
BT_DBG("%s status 0x%02x", hdev->name, status);
1032+
1033+
if (hci_conn_count(hdev) == 0)
1034+
queue_work(hdev->req_workqueue, &hdev->power_off.work);
1035+
}
1036+
1037+
static int clean_up_hci_state(struct hci_dev *hdev)
1038+
{
1039+
struct hci_request req;
1040+
struct hci_conn *conn;
1041+
1042+
hci_req_init(&req, hdev);
1043+
1044+
if (test_bit(HCI_ISCAN, &hdev->flags) ||
1045+
test_bit(HCI_PSCAN, &hdev->flags)) {
1046+
u8 scan = 0x00;
1047+
hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
1048+
}
1049+
1050+
if (test_bit(HCI_ADVERTISING, &hdev->dev_flags))
1051+
disable_advertising(&req);
1052+
1053+
if (test_bit(HCI_LE_SCAN, &hdev->dev_flags)) {
1054+
struct hci_cp_le_set_scan_enable cp;
1055+
1056+
memset(&cp, 0, sizeof(cp));
1057+
cp.enable = LE_SCAN_DISABLE;
1058+
hci_req_add(&req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
1059+
}
1060+
1061+
list_for_each_entry(conn, &hdev->conn_hash.list, list) {
1062+
struct hci_cp_disconnect dc;
1063+
1064+
dc.handle = cpu_to_le16(conn->handle);
1065+
dc.reason = 0x15; /* Terminated due to Power Off */
1066+
hci_req_add(&req, HCI_OP_DISCONNECT, sizeof(dc), &dc);
1067+
}
1068+
1069+
return hci_req_run(&req, clean_up_hci_complete);
1070+
}
1071+
10291072
static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
10301073
u16 len)
10311074
{
@@ -1069,12 +1112,19 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
10691112
goto failed;
10701113
}
10711114

1072-
if (cp->val)
1115+
if (cp->val) {
10731116
queue_work(hdev->req_workqueue, &hdev->power_on);
1074-
else
1075-
queue_work(hdev->req_workqueue, &hdev->power_off.work);
1117+
err = 0;
1118+
} else {
1119+
/* Disconnect connections, stop scans, etc */
1120+
err = clean_up_hci_state(hdev);
10761121

1077-
err = 0;
1122+
/* ENODATA means there were no HCI commands queued */
1123+
if (err == -ENODATA) {
1124+
queue_work(hdev->req_workqueue, &hdev->power_off.work);
1125+
err = 0;
1126+
}
1127+
}
10781128

10791129
failed:
10801130
hci_dev_unlock(hdev);
@@ -5028,8 +5078,20 @@ void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
50285078
bool mgmt_connected)
50295079
{
50305080
struct mgmt_ev_device_disconnected ev;
5081+
struct pending_cmd *power_off;
50315082
struct sock *sk = NULL;
50325083

5084+
power_off = mgmt_pending_find(MGMT_OP_SET_POWERED, hdev);
5085+
if (power_off) {
5086+
struct mgmt_mode *cp = power_off->param;
5087+
5088+
/* The connection is still in hci_conn_hash so test for 1
5089+
* instead of 0 to know if this is the last one.
5090+
*/
5091+
if (!cp->val && hci_conn_count(hdev) == 1)
5092+
queue_work(hdev->req_workqueue, &hdev->power_off.work);
5093+
}
5094+
50335095
if (!mgmt_connected)
50345096
return;
50355097

0 commit comments

Comments
 (0)