summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorFlorian Grandel <fgrandel@gmail.com>2015-06-18 03:16:46 +0200
committerMarcel Holtmann <marcel@holtmann.org>2015-06-18 18:11:52 +0200
commit847818d9c05f8951270600c0d3260871dbc23134 (patch)
tree64b2c6ab733b134910271ca920a94eb23671baf7 /net
parent7816b82039b56308a0d685e97d4a9f4b52e239bd (diff)
downloadlinux-847818d9c05f8951270600c0d3260871dbc23134.tar.gz
linux-847818d9c05f8951270600c0d3260871dbc23134.tar.bz2
linux-847818d9c05f8951270600c0d3260871dbc23134.zip
Bluetooth: mgmt: multi adv for clear_adv_instances()
The clear_adv_instance() function could not clean up multiple advertising instances previously. It is being changed to provide both, a means to clean up a single instance and cleaning up all instances at once. An additional instance parameter is being introduced to achieve this. Passing in 0x00 to this parameter signifies that all instances should be cleaned up. This semantics has been chosen similarly to the semantics of the instance parameter in the remove_advertising() function. When removing a single instance the method also ensures that another instance will be scheduled if available. When the currently advertising method is being removed, it will be canceled immediately. Signed-off-by: Florian Grandel <fgrandel@gmail.com> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'net')
-rw-r--r--net/bluetooth/mgmt.c97
1 files changed, 81 insertions, 16 deletions
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 55765dd79070..ac5fc357c757 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -1538,27 +1538,74 @@ static void cancel_adv_timeout(struct hci_dev *hdev)
}
}
-static void clear_adv_instance(struct hci_dev *hdev)
+/* For a single instance:
+ * - force == true: The instance will be removed even when its remaining
+ * lifetime is not zero.
+ * - force == false: the instance will be deactivated but kept stored unless
+ * the remaining lifetime is zero.
+ *
+ * For instance == 0x00:
+ * - force == true: All instances will be removed regardless of their timeout
+ * setting.
+ * - force == false: Only instances that have a timeout will be removed.
+ */
+static void clear_adv_instance(struct hci_dev *hdev, struct hci_request *req,
+ u8 instance, bool force)
{
- struct hci_request req;
+ struct adv_info *adv_instance, *n, *next_instance = NULL;
+ int err;
+ u8 rem_inst;
- if (!hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE))
- return;
+ /* Cancel any timeout concerning the removed instance(s). */
+ if (!instance || hdev->cur_adv_instance == instance)
+ cancel_adv_timeout(hdev);
- if (hdev->adv_instance_timeout)
- cancel_delayed_work(&hdev->adv_instance_expire);
+ /* Get the next instance to advertise BEFORE we remove
+ * the current one. This can be the same instance again
+ * if there is only one instance.
+ */
+ if (instance && hdev->cur_adv_instance == instance)
+ next_instance = hci_get_next_instance(hdev, instance);
- memset(&hdev->adv_instance, 0, sizeof(hdev->adv_instance));
- advertising_removed(NULL, hdev, 1);
- hci_dev_clear_flag(hdev, HCI_ADVERTISING_INSTANCE);
+ if (instance == 0x00) {
+ list_for_each_entry_safe(adv_instance, n, &hdev->adv_instances,
+ list) {
+ if (!(force || adv_instance->timeout))
+ continue;
- if (!hdev_is_powered(hdev) ||
+ rem_inst = adv_instance->instance;
+ err = hci_remove_adv_instance(hdev, rem_inst);
+ if (!err)
+ advertising_removed(NULL, hdev, rem_inst);
+ }
+ hdev->cur_adv_instance = 0x00;
+ } else {
+ adv_instance = hci_find_adv_instance(hdev, instance);
+
+ if (force || (adv_instance && adv_instance->timeout &&
+ !adv_instance->remaining_time)) {
+ /* Don't advertise a removed instance. */
+ if (next_instance &&
+ next_instance->instance == instance)
+ next_instance = NULL;
+
+ err = hci_remove_adv_instance(hdev, instance);
+ if (!err)
+ advertising_removed(NULL, hdev, instance);
+ }
+ }
+
+ if (list_empty(&hdev->adv_instances)) {
+ hdev->cur_adv_instance = 0x00;
+ hci_dev_clear_flag(hdev, HCI_ADVERTISING_INSTANCE);
+ }
+
+ if (!req || !hdev_is_powered(hdev) ||
hci_dev_test_flag(hdev, HCI_ADVERTISING))
return;
- hci_req_init(&req, hdev);
- disable_advertising(&req);
- hci_req_run(&req, NULL);
+ if (next_instance)
+ schedule_adv_instance(req, next_instance->instance, false);
}
static int clean_up_hci_state(struct hci_dev *hdev)
@@ -1576,8 +1623,7 @@ static int clean_up_hci_state(struct hci_dev *hdev)
hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
}
- if (hdev->adv_instance_timeout)
- clear_adv_instance(hdev);
+ clear_adv_instance(hdev, NULL, 0x00, false);
if (hci_dev_test_flag(hdev, HCI_LE_ADV))
disable_advertising(&req);
@@ -2532,6 +2578,9 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
val = !!cp->val;
enabled = lmp_host_le_capable(hdev);
+ if (!val)
+ clear_adv_instance(hdev, NULL, 0x00, true);
+
if (!hdev_is_powered(hdev) || val == enabled) {
bool changed = false;
@@ -7018,10 +7067,26 @@ unlock:
void mgmt_adv_timeout_expired(struct hci_dev *hdev)
{
+ u8 instance;
+ struct hci_request req;
+
hdev->adv_instance_timeout = 0;
+ instance = get_current_adv_instance(hdev);
+ if (instance == 0x00)
+ return;
+
hci_dev_lock(hdev);
- clear_adv_instance(hdev);
+ hci_req_init(&req, hdev);
+
+ clear_adv_instance(hdev, &req, instance, false);
+
+ if (list_empty(&hdev->adv_instances))
+ disable_advertising(&req);
+
+ if (!skb_queue_empty(&req.cmd_q))
+ hci_req_run(&req, NULL);
+
hci_dev_unlock(hdev);
}