Skip to content

Commit 2f20216

Browse files
apanditholtmann
authored andcommitted
Bluetooth: Emit controller suspend and resume events
Emit controller suspend and resume events when we are ready for suspend and we've resumed from suspend. The controller suspend event will report whatever suspend state was successfully entered. The controller resume event will check the first HCI event that was received after we finished preparing for suspend and, if it was a connection event, store the address of the peer that caused the event. If it was not a connection event, we mark the wake reason as an unexpected event. Here is a sample btmon trace with these events: @ MGMT Event: Controller Suspended (0x002d) plen 1 Suspend state: Page scanning and/or passive scanning (2) @ MGMT Event: Controller Resumed (0x002e) plen 8 Wake reason: Remote wake due to peer device connection (2) LE Address: CD:F3:CD:13:C5:9A (OUI CD-F3-CD) Signed-off-by: Abhishek Pandit-Subedi <[email protected]> Reviewed-by: Miao-chen Chou <[email protected]> Signed-off-by: Marcel Holtmann <[email protected]>
1 parent f0cfc48 commit 2f20216

File tree

4 files changed

+104
-1
lines changed

4 files changed

+104
-1
lines changed

include/net/bluetooth/hci_core.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,9 @@ struct hci_dev {
484484
enum suspended_state suspend_state;
485485
bool scanning_paused;
486486
bool suspended;
487+
u8 wake_reason;
488+
bdaddr_t wake_addr;
489+
u8 wake_addr_type;
487490

488491
wait_queue_head_t suspend_wait_q;
489492
DECLARE_BITMAP(suspend_tasks, __SUSPEND_NUM_TASKS);

include/net/bluetooth/mgmt.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,3 +1042,7 @@ struct mgmt_ev_controller_resume {
10421042
__u8 wake_reason;
10431043
struct mgmt_addr_info addr;
10441044
} __packed;
1045+
1046+
#define MGMT_WAKE_REASON_NON_BT_WAKE 0x0
1047+
#define MGMT_WAKE_REASON_UNEXPECTED 0x1
1048+
#define MGMT_WAKE_REASON_REMOTE_WAKE 0x2

net/bluetooth/hci_core.c

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3497,12 +3497,24 @@ static int hci_change_suspend_state(struct hci_dev *hdev,
34973497
return hci_suspend_wait_event(hdev);
34983498
}
34993499

3500+
static void hci_clear_wake_reason(struct hci_dev *hdev)
3501+
{
3502+
hci_dev_lock(hdev);
3503+
3504+
hdev->wake_reason = 0;
3505+
bacpy(&hdev->wake_addr, BDADDR_ANY);
3506+
hdev->wake_addr_type = 0;
3507+
3508+
hci_dev_unlock(hdev);
3509+
}
3510+
35003511
static int hci_suspend_notifier(struct notifier_block *nb, unsigned long action,
35013512
void *data)
35023513
{
35033514
struct hci_dev *hdev =
35043515
container_of(nb, struct hci_dev, suspend_notifier);
35053516
int ret = 0;
3517+
u8 state = BT_RUNNING;
35063518

35073519
/* If powering down, wait for completion. */
35083520
if (mgmt_powering_down(hdev)) {
@@ -3523,15 +3535,27 @@ static int hci_suspend_notifier(struct notifier_block *nb, unsigned long action,
35233535
* - Second, program event filter/whitelist and enable scan
35243536
*/
35253537
ret = hci_change_suspend_state(hdev, BT_SUSPEND_DISCONNECT);
3538+
if (!ret)
3539+
state = BT_SUSPEND_DISCONNECT;
35263540

35273541
/* Only configure whitelist if disconnect succeeded and wake
35283542
* isn't being prevented.
35293543
*/
3530-
if (!ret && !(hdev->prevent_wake && hdev->prevent_wake(hdev)))
3544+
if (!ret && !(hdev->prevent_wake && hdev->prevent_wake(hdev))) {
35313545
ret = hci_change_suspend_state(hdev,
35323546
BT_SUSPEND_CONFIGURE_WAKE);
3547+
if (!ret)
3548+
state = BT_SUSPEND_CONFIGURE_WAKE;
3549+
}
3550+
3551+
hci_clear_wake_reason(hdev);
3552+
mgmt_suspending(hdev, state);
3553+
35333554
} else if (action == PM_POST_SUSPEND) {
35343555
ret = hci_change_suspend_state(hdev, BT_RUNNING);
3556+
3557+
mgmt_resuming(hdev, hdev->wake_reason, &hdev->wake_addr,
3558+
hdev->wake_addr_type);
35353559
}
35363560

35373561
done:

net/bluetooth/hci_event.c

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6000,6 +6000,75 @@ static bool hci_get_cmd_complete(struct hci_dev *hdev, u16 opcode,
60006000
return true;
60016001
}
60026002

6003+
static void hci_store_wake_reason(struct hci_dev *hdev, u8 event,
6004+
struct sk_buff *skb)
6005+
{
6006+
struct hci_ev_le_advertising_info *adv;
6007+
struct hci_ev_le_direct_adv_info *direct_adv;
6008+
struct hci_ev_le_ext_adv_report *ext_adv;
6009+
const struct hci_ev_conn_complete *conn_complete = (void *)skb->data;
6010+
const struct hci_ev_conn_request *conn_request = (void *)skb->data;
6011+
6012+
hci_dev_lock(hdev);
6013+
6014+
/* If we are currently suspended and this is the first BT event seen,
6015+
* save the wake reason associated with the event.
6016+
*/
6017+
if (!hdev->suspended || hdev->wake_reason)
6018+
goto unlock;
6019+
6020+
/* Default to remote wake. Values for wake_reason are documented in the
6021+
* Bluez mgmt api docs.
6022+
*/
6023+
hdev->wake_reason = MGMT_WAKE_REASON_REMOTE_WAKE;
6024+
6025+
/* Once configured for remote wakeup, we should only wake up for
6026+
* reconnections. It's useful to see which device is waking us up so
6027+
* keep track of the bdaddr of the connection event that woke us up.
6028+
*/
6029+
if (event == HCI_EV_CONN_REQUEST) {
6030+
bacpy(&hdev->wake_addr, &conn_complete->bdaddr);
6031+
hdev->wake_addr_type = BDADDR_BREDR;
6032+
} else if (event == HCI_EV_CONN_COMPLETE) {
6033+
bacpy(&hdev->wake_addr, &conn_request->bdaddr);
6034+
hdev->wake_addr_type = BDADDR_BREDR;
6035+
} else if (event == HCI_EV_LE_META) {
6036+
struct hci_ev_le_meta *le_ev = (void *)skb->data;
6037+
u8 subevent = le_ev->subevent;
6038+
u8 *ptr = &skb->data[sizeof(*le_ev)];
6039+
u8 num_reports = *ptr;
6040+
6041+
if ((subevent == HCI_EV_LE_ADVERTISING_REPORT ||
6042+
subevent == HCI_EV_LE_DIRECT_ADV_REPORT ||
6043+
subevent == HCI_EV_LE_EXT_ADV_REPORT) &&
6044+
num_reports) {
6045+
adv = (void *)(ptr + 1);
6046+
direct_adv = (void *)(ptr + 1);
6047+
ext_adv = (void *)(ptr + 1);
6048+
6049+
switch (subevent) {
6050+
case HCI_EV_LE_ADVERTISING_REPORT:
6051+
bacpy(&hdev->wake_addr, &adv->bdaddr);
6052+
hdev->wake_addr_type = adv->bdaddr_type;
6053+
break;
6054+
case HCI_EV_LE_DIRECT_ADV_REPORT:
6055+
bacpy(&hdev->wake_addr, &direct_adv->bdaddr);
6056+
hdev->wake_addr_type = direct_adv->bdaddr_type;
6057+
break;
6058+
case HCI_EV_LE_EXT_ADV_REPORT:
6059+
bacpy(&hdev->wake_addr, &ext_adv->bdaddr);
6060+
hdev->wake_addr_type = ext_adv->bdaddr_type;
6061+
break;
6062+
}
6063+
}
6064+
} else {
6065+
hdev->wake_reason = MGMT_WAKE_REASON_UNEXPECTED;
6066+
}
6067+
6068+
unlock:
6069+
hci_dev_unlock(hdev);
6070+
}
6071+
60036072
void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
60046073
{
60056074
struct hci_event_hdr *hdr = (void *) skb->data;
@@ -6033,6 +6102,9 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
60336102

60346103
skb_pull(skb, HCI_EVENT_HDR_SIZE);
60356104

6105+
/* Store wake reason if we're suspended */
6106+
hci_store_wake_reason(hdev, event, skb);
6107+
60366108
switch (event) {
60376109
case HCI_EV_INQUIRY_COMPLETE:
60386110
hci_inquiry_complete_evt(hdev, skb);

0 commit comments

Comments
 (0)