Skip to content

Commit

Permalink
Bluetooth: advmon offload MSFT add monitor
Browse files Browse the repository at this point in the history
Enables advertising monitor offloading to the controller, if MSFT
extension is supported. The kernel won't adjust the monitor parameters
to match what the controller supports - that is the user space's
responsibility.

This patch only manages the addition of monitors. Monitor removal is
going to be handled by another patch.

Signed-off-by: Archie Pusaka <apusaka@chromium.org>
Reviewed-by: Manish Mandlik <mmandlik@chromium.org>
Reviewed-by: Miao-chen Chou <mcchou@chromium.org>
Reviewed-by: Yun-Hao Chung <howardchung@google.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
  • Loading branch information
Archie Pusaka authored and Marcel Holtmann committed Jan 25, 2021
1 parent b4a221e commit a2a4ded
Show file tree
Hide file tree
Showing 5 changed files with 356 additions and 43 deletions.
17 changes: 14 additions & 3 deletions include/net/bluetooth/hci_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -261,13 +261,20 @@ struct adv_rssi_thresholds {
struct adv_monitor {
struct list_head patterns;
struct adv_rssi_thresholds rssi;
bool active;
__u16 handle;

enum {
ADV_MONITOR_STATE_NOT_REGISTERED,
ADV_MONITOR_STATE_REGISTERED,
ADV_MONITOR_STATE_OFFLOADED
} state;
};

#define HCI_MIN_ADV_MONITOR_HANDLE 1
#define HCI_MAX_ADV_MONITOR_NUM_HANDLES 32
#define HCI_MAX_ADV_MONITOR_NUM_HANDLES 32
#define HCI_MAX_ADV_MONITOR_NUM_PATTERNS 16
#define HCI_ADV_MONITOR_EXT_NONE 1
#define HCI_ADV_MONITOR_EXT_MSFT 2

#define HCI_MAX_SHORT_NAME_LENGTH 10

Expand Down Expand Up @@ -1326,9 +1333,12 @@ void hci_adv_instances_set_rpa_expired(struct hci_dev *hdev, bool rpa_expired);

void hci_adv_monitors_clear(struct hci_dev *hdev);
void hci_free_adv_monitor(struct adv_monitor *monitor);
int hci_add_adv_monitor(struct hci_dev *hdev, struct adv_monitor *monitor);
int hci_add_adv_patterns_monitor_complete(struct hci_dev *hdev, u8 status);
bool hci_add_adv_monitor(struct hci_dev *hdev, struct adv_monitor *monitor,
int *err);
int hci_remove_adv_monitor(struct hci_dev *hdev, u16 handle);
bool hci_is_adv_monitoring(struct hci_dev *hdev);
int hci_get_adv_monitor_offload_ext(struct hci_dev *hdev);

void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb);

Expand Down Expand Up @@ -1804,6 +1814,7 @@ void mgmt_advertising_added(struct sock *sk, struct hci_dev *hdev,
void mgmt_advertising_removed(struct sock *sk, struct hci_dev *hdev,
u8 instance);
int mgmt_phy_configuration_changed(struct hci_dev *hdev, struct sock *skip);
int mgmt_add_adv_patterns_monitor_complete(struct hci_dev *hdev, u8 status);

u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency,
u16 to_multiplier);
Expand Down
55 changes: 46 additions & 9 deletions net/bluetooth/hci_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -3070,27 +3070,56 @@ void hci_free_adv_monitor(struct adv_monitor *monitor)
kfree(monitor);
}

/* This function requires the caller holds hdev->lock */
int hci_add_adv_monitor(struct hci_dev *hdev, struct adv_monitor *monitor)
int hci_add_adv_patterns_monitor_complete(struct hci_dev *hdev, u8 status)
{
return mgmt_add_adv_patterns_monitor_complete(hdev, status);
}

/* Assigns handle to a monitor, and if offloading is supported and power is on,
* also attempts to forward the request to the controller.
* Returns true if request is forwarded (result is pending), false otherwise.
* This function requires the caller holds hdev->lock.
*/
bool hci_add_adv_monitor(struct hci_dev *hdev, struct adv_monitor *monitor,
int *err)
{
int min, max, handle;

if (!monitor)
return -EINVAL;
*err = 0;

if (!monitor) {
*err = -EINVAL;
return false;
}

min = HCI_MIN_ADV_MONITOR_HANDLE;
max = HCI_MIN_ADV_MONITOR_HANDLE + HCI_MAX_ADV_MONITOR_NUM_HANDLES;
handle = idr_alloc(&hdev->adv_monitors_idr, monitor, min, max,
GFP_KERNEL);
if (handle < 0)
return handle;
if (handle < 0) {
*err = handle;
return false;
}

hdev->adv_monitors_cnt++;
monitor->handle = handle;

hci_update_background_scan(hdev);
if (!hdev_is_powered(hdev))
return false;

return 0;
switch (hci_get_adv_monitor_offload_ext(hdev)) {
case HCI_ADV_MONITOR_EXT_NONE:
hci_update_background_scan(hdev);
bt_dev_dbg(hdev, "%s add monitor status %d", hdev->name, *err);
/* Message was not forwarded to controller - not an error */
return false;
case HCI_ADV_MONITOR_EXT_MSFT:
*err = msft_add_monitor_pattern(hdev, monitor);
bt_dev_dbg(hdev, "%s add monitor msft status %d", hdev->name,
*err);
break;
}

return (*err == 0);
}

static int free_adv_monitor(int id, void *ptr, void *data)
Expand Down Expand Up @@ -3134,6 +3163,14 @@ bool hci_is_adv_monitoring(struct hci_dev *hdev)
return !idr_is_empty(&hdev->adv_monitors_idr);
}

int hci_get_adv_monitor_offload_ext(struct hci_dev *hdev)
{
if (msft_monitor_supported(hdev))
return HCI_ADV_MONITOR_EXT_MSFT;

return HCI_ADV_MONITOR_EXT_NONE;
}

struct bdaddr_list *hci_bdaddr_list_lookup(struct list_head *bdaddr_list,
bdaddr_t *bdaddr, u8 type)
{
Expand Down
114 changes: 84 additions & 30 deletions net/bluetooth/mgmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -4185,19 +4185,19 @@ static int read_adv_mon_features(struct sock *sk, struct hci_dev *hdev,
int handle, err;
size_t rp_size = 0;
__u32 supported = 0;
__u32 enabled = 0;
__u16 num_handles = 0;
__u16 handles[HCI_MAX_ADV_MONITOR_NUM_HANDLES];

BT_DBG("request for %s", hdev->name);

hci_dev_lock(hdev);

if (msft_get_features(hdev) & MSFT_FEATURE_MASK_LE_ADV_MONITOR)
if (msft_monitor_supported(hdev))
supported |= MGMT_ADV_MONITOR_FEATURE_MASK_OR_PATTERNS;

idr_for_each_entry(&hdev->adv_monitors_idr, monitor, handle) {
idr_for_each_entry(&hdev->adv_monitors_idr, monitor, handle)
handles[num_handles++] = monitor->handle;
}

hci_dev_unlock(hdev);

Expand All @@ -4206,11 +4206,11 @@ static int read_adv_mon_features(struct sock *sk, struct hci_dev *hdev,
if (!rp)
return -ENOMEM;

/* Once controller-based monitoring is in place, the enabled_features
* should reflect the use.
*/
/* All supported features are currently enabled */
enabled = supported;

rp->supported_features = cpu_to_le32(supported);
rp->enabled_features = 0;
rp->enabled_features = cpu_to_le32(enabled);
rp->max_num_handles = cpu_to_le16(HCI_MAX_ADV_MONITOR_NUM_HANDLES);
rp->max_num_patterns = HCI_MAX_ADV_MONITOR_NUM_PATTERNS;
rp->num_handles = cpu_to_le16(num_handles);
Expand All @@ -4226,44 +4226,105 @@ static int read_adv_mon_features(struct sock *sk, struct hci_dev *hdev,
return err;
}

int mgmt_add_adv_patterns_monitor_complete(struct hci_dev *hdev, u8 status)
{
struct mgmt_rp_add_adv_patterns_monitor rp;
struct mgmt_pending_cmd *cmd;
struct adv_monitor *monitor;
int err = 0;

hci_dev_lock(hdev);

cmd = pending_find(MGMT_OP_ADD_ADV_PATTERNS_MONITOR_RSSI, hdev);
if (!cmd) {
cmd = pending_find(MGMT_OP_ADD_ADV_PATTERNS_MONITOR, hdev);
if (!cmd)
goto done;
}

monitor = cmd->user_data;
rp.monitor_handle = cpu_to_le16(monitor->handle);

if (!status) {
mgmt_adv_monitor_added(cmd->sk, hdev, monitor->handle);
hdev->adv_monitors_cnt++;
if (monitor->state == ADV_MONITOR_STATE_NOT_REGISTERED)
monitor->state = ADV_MONITOR_STATE_REGISTERED;
hci_update_background_scan(hdev);
}

err = mgmt_cmd_complete(cmd->sk, cmd->index, cmd->opcode,
mgmt_status(status), &rp, sizeof(rp));
mgmt_pending_remove(cmd);

done:
hci_dev_unlock(hdev);
bt_dev_dbg(hdev, "add monitor %d complete, status %d",
rp.monitor_handle, status);

return err;
}

static int __add_adv_patterns_monitor(struct sock *sk, struct hci_dev *hdev,
struct adv_monitor *m, u8 status, u16 op)
struct adv_monitor *m, u8 status,
void *data, u16 len, u16 op)
{
struct mgmt_rp_add_adv_patterns_monitor rp;
unsigned int prev_adv_monitors_cnt;
struct mgmt_pending_cmd *cmd;
int err;
bool pending;

hci_dev_lock(hdev);

if (status)
goto failed;
goto unlock;

hci_dev_lock(hdev);
if (pending_find(MGMT_OP_SET_LE, hdev) ||
pending_find(MGMT_OP_ADD_ADV_PATTERNS_MONITOR, hdev) ||
pending_find(MGMT_OP_ADD_ADV_PATTERNS_MONITOR_RSSI, hdev) ||
pending_find(MGMT_OP_REMOVE_ADV_MONITOR, hdev)) {
status = MGMT_STATUS_BUSY;
goto unlock;
}

prev_adv_monitors_cnt = hdev->adv_monitors_cnt;
cmd = mgmt_pending_add(sk, op, hdev, data, len);
if (!cmd) {
status = MGMT_STATUS_NO_RESOURCES;
goto unlock;
}

err = hci_add_adv_monitor(hdev, m);
pending = hci_add_adv_monitor(hdev, m, &err);
if (err) {
if (err == -ENOSPC)
if (err == -ENOSPC || err == -ENOMEM)
status = MGMT_STATUS_NO_RESOURCES;
else if (err == -EINVAL)
status = MGMT_STATUS_INVALID_PARAMS;
else
status = MGMT_STATUS_FAILED;

mgmt_pending_remove(cmd);
goto unlock;
}

if (hdev->adv_monitors_cnt > prev_adv_monitors_cnt)
if (!pending) {
mgmt_pending_remove(cmd);
rp.monitor_handle = cpu_to_le16(m->handle);
mgmt_adv_monitor_added(sk, hdev, m->handle);
m->state = ADV_MONITOR_STATE_REGISTERED;
hdev->adv_monitors_cnt++;

hci_dev_unlock(hdev);
hci_dev_unlock(hdev);
return mgmt_cmd_complete(sk, hdev->id, op, MGMT_STATUS_SUCCESS,
&rp, sizeof(rp));
}

rp.monitor_handle = cpu_to_le16(m->handle);
hci_dev_unlock(hdev);

return mgmt_cmd_complete(sk, hdev->id, op,
MGMT_STATUS_SUCCESS, &rp, sizeof(rp));
cmd->user_data = m;
return 0;

unlock:
hci_dev_unlock(hdev);

failed:
hci_free_adv_monitor(m);
return mgmt_cmd_status(sk, hdev->id, op, status);
}
Expand Down Expand Up @@ -4298,13 +4359,9 @@ static u8 parse_adv_monitor_pattern(struct adv_monitor *m, u8 pattern_count,
{
u8 offset = 0, length = 0;
struct adv_pattern *p = NULL;
unsigned int mp_cnt = 0;
int i;

for (i = 0; i < pattern_count; i++) {
if (++mp_cnt > HCI_MAX_ADV_MONITOR_NUM_PATTERNS)
return MGMT_STATUS_INVALID_PARAMS;

offset = patterns[i].offset;
length = patterns[i].length;
if (offset >= HCI_MAX_AD_LENGTH ||
Expand All @@ -4325,9 +4382,6 @@ static u8 parse_adv_monitor_pattern(struct adv_monitor *m, u8 pattern_count,
list_add(&p->list, &m->patterns);
}

if (mp_cnt != pattern_count)
return MGMT_STATUS_INVALID_PARAMS;

return MGMT_STATUS_SUCCESS;
}

Expand Down Expand Up @@ -4364,7 +4418,7 @@ static int add_adv_patterns_monitor(struct sock *sk, struct hci_dev *hdev,
status = parse_adv_monitor_pattern(m, cp->pattern_count, cp->patterns);

done:
return __add_adv_patterns_monitor(sk, hdev, m, status,
return __add_adv_patterns_monitor(sk, hdev, m, status, data, len,
MGMT_OP_ADD_ADV_PATTERNS_MONITOR);
}

Expand Down Expand Up @@ -4401,7 +4455,7 @@ static int add_adv_patterns_monitor_rssi(struct sock *sk, struct hci_dev *hdev,
status = parse_adv_monitor_pattern(m, cp->pattern_count, cp->patterns);

done:
return __add_adv_patterns_monitor(sk, hdev, m, status,
return __add_adv_patterns_monitor(sk, hdev, m, status, data, len,
MGMT_OP_ADD_ADV_PATTERNS_MONITOR_RSSI);
}

Expand Down
Loading

0 comments on commit a2a4ded

Please sign in to comment.