Commit b139553d authored by Miao-chen Chou's avatar Miao-chen Chou Committed by Johan Hedberg

Bluetooth: Add handler of MGMT_OP_ADD_ADV_PATTERNS_MONITOR

This adds the request handler of MGMT_OP_ADD_ADV_PATTERNS_MONITOR command.
Note that the controller-based monitoring is not yet in place. This tracks
the content of the monitor without sending HCI traffic, so the request
returns immediately.

The following manual test was performed.
- Issue btmgmt advmon-add with valid and invalid inputs.
- Issue btmgmt advmon-add more the allowed number of monitors.
Signed-off-by: default avatarMiao-chen Chou <mcchou@chromium.org>
Signed-off-by: default avatarMarcel Holtmann <marcel@holtmann.org>
Signed-off-by: default avatarJohan Hedberg <johan.hedberg@intel.com>
parent e5e1e7fd
......@@ -1281,6 +1281,8 @@ int hci_remove_adv_instance(struct hci_dev *hdev, u8 instance);
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);
void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb);
......
......@@ -2998,9 +2998,49 @@ int hci_add_adv_instance(struct hci_dev *hdev, u8 instance, u32 flags,
/* This function requires the caller holds hdev->lock */
void hci_adv_monitors_clear(struct hci_dev *hdev)
{
struct adv_monitor *monitor;
int handle;
idr_for_each_entry(&hdev->adv_monitors_idr, monitor, handle)
hci_free_adv_monitor(monitor);
idr_destroy(&hdev->adv_monitors_idr);
}
void hci_free_adv_monitor(struct adv_monitor *monitor)
{
struct adv_pattern *pattern;
struct adv_pattern *tmp;
if (!monitor)
return;
list_for_each_entry_safe(pattern, tmp, &monitor->patterns, list)
kfree(pattern);
kfree(monitor);
}
/* This function requires the caller holds hdev->lock */
int hci_add_adv_monitor(struct hci_dev *hdev, struct adv_monitor *monitor)
{
int min, max, handle;
if (!monitor)
return -EINVAL;
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;
hdev->adv_monitors_cnt++;
monitor->handle = handle;
return 0;
}
struct bdaddr_list *hci_bdaddr_list_lookup(struct list_head *bdaddr_list,
bdaddr_t *bdaddr, u8 type)
{
......
......@@ -120,6 +120,7 @@ static const u16 mgmt_commands[] = {
MGMT_OP_GET_DEVICE_FLAGS,
MGMT_OP_SET_DEVICE_FLAGS,
MGMT_OP_READ_ADV_MONITOR_FEATURES,
MGMT_OP_ADD_ADV_PATTERNS_MONITOR,
};
static const u16 mgmt_events[] = {
......@@ -4020,6 +4021,103 @@ static int read_adv_mon_features(struct sock *sk, struct hci_dev *hdev,
MGMT_STATUS_SUCCESS, rp, rp_size);
}
static int add_adv_patterns_monitor(struct sock *sk, struct hci_dev *hdev,
void *data, u16 len)
{
struct mgmt_cp_add_adv_patterns_monitor *cp = data;
struct mgmt_rp_add_adv_patterns_monitor rp;
struct adv_monitor *m = NULL;
struct adv_pattern *p = NULL;
__u8 cp_ofst = 0, cp_len = 0;
unsigned int mp_cnt = 0;
int err, i;
BT_DBG("request for %s", hdev->name);
if (len <= sizeof(*cp) || cp->pattern_count == 0) {
err = mgmt_cmd_status(sk, hdev->id,
MGMT_OP_ADD_ADV_PATTERNS_MONITOR,
MGMT_STATUS_INVALID_PARAMS);
goto failed;
}
m = kmalloc(sizeof(*m), GFP_KERNEL);
if (!m) {
err = -ENOMEM;
goto failed;
}
INIT_LIST_HEAD(&m->patterns);
m->active = false;
for (i = 0; i < cp->pattern_count; i++) {
if (++mp_cnt > HCI_MAX_ADV_MONITOR_NUM_PATTERNS) {
err = mgmt_cmd_status(sk, hdev->id,
MGMT_OP_ADD_ADV_PATTERNS_MONITOR,
MGMT_STATUS_INVALID_PARAMS);
goto failed;
}
cp_ofst = cp->patterns[i].offset;
cp_len = cp->patterns[i].length;
if (cp_ofst >= HCI_MAX_AD_LENGTH ||
cp_len > HCI_MAX_AD_LENGTH ||
(cp_ofst + cp_len) > HCI_MAX_AD_LENGTH) {
err = mgmt_cmd_status(sk, hdev->id,
MGMT_OP_ADD_ADV_PATTERNS_MONITOR,
MGMT_STATUS_INVALID_PARAMS);
goto failed;
}
p = kmalloc(sizeof(*p), GFP_KERNEL);
if (!p) {
err = -ENOMEM;
goto failed;
}
p->ad_type = cp->patterns[i].ad_type;
p->offset = cp->patterns[i].offset;
p->length = cp->patterns[i].length;
memcpy(p->value, cp->patterns[i].value, p->length);
INIT_LIST_HEAD(&p->list);
list_add(&p->list, &m->patterns);
}
if (mp_cnt != cp->pattern_count) {
err = mgmt_cmd_status(sk, hdev->id,
MGMT_OP_ADD_ADV_PATTERNS_MONITOR,
MGMT_STATUS_INVALID_PARAMS);
goto failed;
}
hci_dev_lock(hdev);
err = hci_add_adv_monitor(hdev, m);
if (err) {
if (err == -ENOSPC) {
mgmt_cmd_status(sk, hdev->id,
MGMT_OP_ADD_ADV_PATTERNS_MONITOR,
MGMT_STATUS_NO_RESOURCES);
}
goto unlock;
}
hci_dev_unlock(hdev);
rp.monitor_handle = cpu_to_le16(m->handle);
return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_ADV_PATTERNS_MONITOR,
MGMT_STATUS_SUCCESS, &rp, sizeof(rp));
unlock:
hci_dev_unlock(hdev);
failed:
hci_free_adv_monitor(m);
return err;
}
static void read_local_oob_data_complete(struct hci_dev *hdev, u8 status,
u16 opcode, struct sk_buff *skb)
{
......@@ -7489,6 +7587,8 @@ static const struct hci_mgmt_handler mgmt_handlers[] = {
{ get_device_flags, MGMT_GET_DEVICE_FLAGS_SIZE },
{ set_device_flags, MGMT_SET_DEVICE_FLAGS_SIZE },
{ read_adv_mon_features, MGMT_READ_ADV_MONITOR_FEATURES_SIZE },
{ add_adv_patterns_monitor,MGMT_ADD_ADV_PATTERNS_MONITOR_SIZE,
HCI_MGMT_VAR_LEN },
};
void mgmt_index_added(struct hci_dev *hdev)
......
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