Commit 2f20216c authored by Abhishek Pandit-Subedi's avatar Abhishek Pandit-Subedi Committed by Marcel Holtmann

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: default avatarAbhishek Pandit-Subedi <abhishekpandit@chromium.org>
Reviewed-by: default avatarMiao-chen Chou <mcchou@chromium.org>
Signed-off-by: default avatarMarcel Holtmann <marcel@holtmann.org>
parent f0cfc486
...@@ -484,6 +484,9 @@ struct hci_dev { ...@@ -484,6 +484,9 @@ struct hci_dev {
enum suspended_state suspend_state; enum suspended_state suspend_state;
bool scanning_paused; bool scanning_paused;
bool suspended; bool suspended;
u8 wake_reason;
bdaddr_t wake_addr;
u8 wake_addr_type;
wait_queue_head_t suspend_wait_q; wait_queue_head_t suspend_wait_q;
DECLARE_BITMAP(suspend_tasks, __SUSPEND_NUM_TASKS); DECLARE_BITMAP(suspend_tasks, __SUSPEND_NUM_TASKS);
......
...@@ -1042,3 +1042,7 @@ struct mgmt_ev_controller_resume { ...@@ -1042,3 +1042,7 @@ struct mgmt_ev_controller_resume {
__u8 wake_reason; __u8 wake_reason;
struct mgmt_addr_info addr; struct mgmt_addr_info addr;
} __packed; } __packed;
#define MGMT_WAKE_REASON_NON_BT_WAKE 0x0
#define MGMT_WAKE_REASON_UNEXPECTED 0x1
#define MGMT_WAKE_REASON_REMOTE_WAKE 0x2
...@@ -3497,12 +3497,24 @@ static int hci_change_suspend_state(struct hci_dev *hdev, ...@@ -3497,12 +3497,24 @@ static int hci_change_suspend_state(struct hci_dev *hdev,
return hci_suspend_wait_event(hdev); return hci_suspend_wait_event(hdev);
} }
static void hci_clear_wake_reason(struct hci_dev *hdev)
{
hci_dev_lock(hdev);
hdev->wake_reason = 0;
bacpy(&hdev->wake_addr, BDADDR_ANY);
hdev->wake_addr_type = 0;
hci_dev_unlock(hdev);
}
static int hci_suspend_notifier(struct notifier_block *nb, unsigned long action, static int hci_suspend_notifier(struct notifier_block *nb, unsigned long action,
void *data) void *data)
{ {
struct hci_dev *hdev = struct hci_dev *hdev =
container_of(nb, struct hci_dev, suspend_notifier); container_of(nb, struct hci_dev, suspend_notifier);
int ret = 0; int ret = 0;
u8 state = BT_RUNNING;
/* If powering down, wait for completion. */ /* If powering down, wait for completion. */
if (mgmt_powering_down(hdev)) { if (mgmt_powering_down(hdev)) {
...@@ -3523,15 +3535,27 @@ static int hci_suspend_notifier(struct notifier_block *nb, unsigned long action, ...@@ -3523,15 +3535,27 @@ static int hci_suspend_notifier(struct notifier_block *nb, unsigned long action,
* - Second, program event filter/whitelist and enable scan * - Second, program event filter/whitelist and enable scan
*/ */
ret = hci_change_suspend_state(hdev, BT_SUSPEND_DISCONNECT); ret = hci_change_suspend_state(hdev, BT_SUSPEND_DISCONNECT);
if (!ret)
state = BT_SUSPEND_DISCONNECT;
/* Only configure whitelist if disconnect succeeded and wake /* Only configure whitelist if disconnect succeeded and wake
* isn't being prevented. * isn't being prevented.
*/ */
if (!ret && !(hdev->prevent_wake && hdev->prevent_wake(hdev))) if (!ret && !(hdev->prevent_wake && hdev->prevent_wake(hdev))) {
ret = hci_change_suspend_state(hdev, ret = hci_change_suspend_state(hdev,
BT_SUSPEND_CONFIGURE_WAKE); BT_SUSPEND_CONFIGURE_WAKE);
if (!ret)
state = BT_SUSPEND_CONFIGURE_WAKE;
}
hci_clear_wake_reason(hdev);
mgmt_suspending(hdev, state);
} else if (action == PM_POST_SUSPEND) { } else if (action == PM_POST_SUSPEND) {
ret = hci_change_suspend_state(hdev, BT_RUNNING); ret = hci_change_suspend_state(hdev, BT_RUNNING);
mgmt_resuming(hdev, hdev->wake_reason, &hdev->wake_addr,
hdev->wake_addr_type);
} }
done: done:
......
...@@ -6000,6 +6000,75 @@ static bool hci_get_cmd_complete(struct hci_dev *hdev, u16 opcode, ...@@ -6000,6 +6000,75 @@ static bool hci_get_cmd_complete(struct hci_dev *hdev, u16 opcode,
return true; return true;
} }
static void hci_store_wake_reason(struct hci_dev *hdev, u8 event,
struct sk_buff *skb)
{
struct hci_ev_le_advertising_info *adv;
struct hci_ev_le_direct_adv_info *direct_adv;
struct hci_ev_le_ext_adv_report *ext_adv;
const struct hci_ev_conn_complete *conn_complete = (void *)skb->data;
const struct hci_ev_conn_request *conn_request = (void *)skb->data;
hci_dev_lock(hdev);
/* If we are currently suspended and this is the first BT event seen,
* save the wake reason associated with the event.
*/
if (!hdev->suspended || hdev->wake_reason)
goto unlock;
/* Default to remote wake. Values for wake_reason are documented in the
* Bluez mgmt api docs.
*/
hdev->wake_reason = MGMT_WAKE_REASON_REMOTE_WAKE;
/* Once configured for remote wakeup, we should only wake up for
* reconnections. It's useful to see which device is waking us up so
* keep track of the bdaddr of the connection event that woke us up.
*/
if (event == HCI_EV_CONN_REQUEST) {
bacpy(&hdev->wake_addr, &conn_complete->bdaddr);
hdev->wake_addr_type = BDADDR_BREDR;
} else if (event == HCI_EV_CONN_COMPLETE) {
bacpy(&hdev->wake_addr, &conn_request->bdaddr);
hdev->wake_addr_type = BDADDR_BREDR;
} else if (event == HCI_EV_LE_META) {
struct hci_ev_le_meta *le_ev = (void *)skb->data;
u8 subevent = le_ev->subevent;
u8 *ptr = &skb->data[sizeof(*le_ev)];
u8 num_reports = *ptr;
if ((subevent == HCI_EV_LE_ADVERTISING_REPORT ||
subevent == HCI_EV_LE_DIRECT_ADV_REPORT ||
subevent == HCI_EV_LE_EXT_ADV_REPORT) &&
num_reports) {
adv = (void *)(ptr + 1);
direct_adv = (void *)(ptr + 1);
ext_adv = (void *)(ptr + 1);
switch (subevent) {
case HCI_EV_LE_ADVERTISING_REPORT:
bacpy(&hdev->wake_addr, &adv->bdaddr);
hdev->wake_addr_type = adv->bdaddr_type;
break;
case HCI_EV_LE_DIRECT_ADV_REPORT:
bacpy(&hdev->wake_addr, &direct_adv->bdaddr);
hdev->wake_addr_type = direct_adv->bdaddr_type;
break;
case HCI_EV_LE_EXT_ADV_REPORT:
bacpy(&hdev->wake_addr, &ext_adv->bdaddr);
hdev->wake_addr_type = ext_adv->bdaddr_type;
break;
}
}
} else {
hdev->wake_reason = MGMT_WAKE_REASON_UNEXPECTED;
}
unlock:
hci_dev_unlock(hdev);
}
void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
{ {
struct hci_event_hdr *hdr = (void *) skb->data; struct hci_event_hdr *hdr = (void *) skb->data;
...@@ -6033,6 +6102,9 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) ...@@ -6033,6 +6102,9 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
skb_pull(skb, HCI_EVENT_HDR_SIZE); skb_pull(skb, HCI_EVENT_HDR_SIZE);
/* Store wake reason if we're suspended */
hci_store_wake_reason(hdev, event, skb);
switch (event) { switch (event) {
case HCI_EV_INQUIRY_COMPLETE: case HCI_EV_INQUIRY_COMPLETE:
hci_inquiry_complete_evt(hdev, skb); hci_inquiry_complete_evt(hdev, skb);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment