Bluetooth: mgmt/hci_core: multi-adv for add_advertising*()
authorFlorian Grandel <fgrandel@gmail.com>
Thu, 18 Jun 2015 01:16:47 +0000 (03:16 +0200)
committerMarcel Holtmann <marcel@holtmann.org>
Thu, 18 Jun 2015 16:11:52 +0000 (18:11 +0200)
The add_advertising() and add_advertising_complete() functions reference
the now obsolete hdev->adv_instance struct. Both methods are being
refactored to access the dynamic advertising instance list instead.

This patch also introduces all logic necessary to actually deal with
multiple instance advertising. Notably the mgmt_adv_inst_expired() and
schedule_adv_inst() method are being referenced to schedule instances in
a round robin fashion.

This patch also introduces a "pending" flag into the adv_info struct.
This is necessary to identify and remove recently added advertising
instances when the HCI commands return with an error status code.
Otherwise new advertising instances could be leaked without properly
informing userspace about their existence.

Signed-off-by: Florian Grandel <fgrandel@gmail.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
include/net/bluetooth/hci_core.h
net/bluetooth/hci_core.c
net/bluetooth/mgmt.c

index b53e1b113621ff07e165414b64cdbf27f995776e..4f58a0e6e0d3a6324a0445c2a585ad73b61b0620 100644 (file)
@@ -157,6 +157,7 @@ struct oob_data {
 
 struct adv_info {
        struct list_head list;
+       bool pending;
        __u8    instance;
        __u32   flags;
        __u16   timeout;
index d1110db3b0d4b1cd87486f1538115c0a2c60993d..e50f7c3c67f87839fdf0caed73b7615a043f8d4d 100644 (file)
@@ -2721,6 +2721,7 @@ int hci_add_adv_instance(struct hci_dev *hdev, u8 instance, u32 flags,
                        return -ENOMEM;
 
                memset(adv_instance, 0, sizeof(*adv_instance));
+               adv_instance->pending = true;
                adv_instance->instance = instance;
                list_add(&adv_instance->list, &hdev->adv_instances);
                hdev->adv_instance_cnt++;
index ac5fc357c75703e6accb07d1e31fa98d4dca0be9..0cc685495510eed4bb4758fb38e73291891ef3fc 100644 (file)
@@ -7033,7 +7033,10 @@ static void add_advertising_complete(struct hci_dev *hdev, u8 status,
                                     u16 opcode)
 {
        struct mgmt_pending_cmd *cmd;
+       struct mgmt_cp_add_advertising *cp;
        struct mgmt_rp_add_advertising rp;
+       struct adv_info *adv_instance, *n;
+       u8 instance;
 
        BT_DBG("status %d", status);
 
@@ -7041,16 +7044,32 @@ static void add_advertising_complete(struct hci_dev *hdev, u8 status,
 
        cmd = pending_find(MGMT_OP_ADD_ADVERTISING, hdev);
 
-       if (status) {
+       if (status)
                hci_dev_clear_flag(hdev, HCI_ADVERTISING_INSTANCE);
-               memset(&hdev->adv_instance, 0, sizeof(hdev->adv_instance));
-               advertising_removed(cmd ? cmd->sk : NULL, hdev, 1);
+
+       list_for_each_entry_safe(adv_instance, n, &hdev->adv_instances, list) {
+               if (!adv_instance->pending)
+                       continue;
+
+               if (!status) {
+                       adv_instance->pending = false;
+                       continue;
+               }
+
+               instance = adv_instance->instance;
+
+               if (hdev->cur_adv_instance == instance)
+                       cancel_adv_timeout(hdev);
+
+               hci_remove_adv_instance(hdev, instance);
+               advertising_removed(cmd ? cmd->sk : NULL, hdev, instance);
        }
 
        if (!cmd)
                goto unlock;
 
-       rp.instance = 0x01;
+       cp = cmd->param;
+       rp.instance = cp->instance;
 
        if (status)
                mgmt_cmd_status(cmd->sk, cmd->index, cmd->opcode,
@@ -7098,7 +7117,10 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev,
        u32 flags;
        u32 supported_flags;
        u8 status;
-       u16 timeout;
+       u16 timeout, duration;
+       unsigned int prev_instance_cnt = hdev->adv_instance_cnt;
+       u8 schedule_instance = 0;
+       struct adv_info *next_instance;
        int err;
        struct mgmt_pending_cmd *cmd;
        struct hci_request req;
@@ -7112,12 +7134,13 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev,
 
        flags = __le32_to_cpu(cp->flags);
        timeout = __le16_to_cpu(cp->timeout);
+       duration = __le16_to_cpu(cp->duration);
 
-       /* The current implementation only supports adding one instance and only
-        * a subset of the specified flags.
+       /* The current implementation only supports a subset of the specified
+        * flags.
         */
        supported_flags = get_supported_adv_flags(hdev);
-       if (cp->instance != 0x01 || (flags & ~supported_flags))
+       if (flags & ~supported_flags)
                return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
                                       MGMT_STATUS_INVALID_PARAMS);
 
@@ -7145,36 +7168,51 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev,
                goto unlock;
        }
 
-       hdev->adv_instance.flags = flags;
-       hdev->adv_instance.adv_data_len = cp->adv_data_len;
-       hdev->adv_instance.scan_rsp_len = cp->scan_rsp_len;
-
-       if (cp->adv_data_len)
-               memcpy(hdev->adv_instance.adv_data, cp->data, cp->adv_data_len);
-
-       if (cp->scan_rsp_len)
-               memcpy(hdev->adv_instance.scan_rsp_data,
-                      cp->data + cp->adv_data_len, cp->scan_rsp_len);
+       err = hci_add_adv_instance(hdev, cp->instance, flags,
+                                  cp->adv_data_len, cp->data,
+                                  cp->scan_rsp_len,
+                                  cp->data + cp->adv_data_len,
+                                  timeout, duration);
+       if (err < 0) {
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
+                                     MGMT_STATUS_FAILED);
+               goto unlock;
+       }
 
-       if (hdev->adv_instance_timeout)
-               cancel_delayed_work(&hdev->adv_instance_expire);
+       /* Only trigger an advertising added event if a new instance was
+        * actually added.
+        */
+       if (hdev->adv_instance_cnt > prev_instance_cnt)
+               advertising_added(sk, hdev, cp->instance);
 
-       hdev->adv_instance_timeout = timeout;
+       hci_dev_set_flag(hdev, HCI_ADVERTISING_INSTANCE);
 
-       if (timeout)
-               queue_delayed_work(hdev->workqueue,
-                                  &hdev->adv_instance_expire,
-                                  msecs_to_jiffies(timeout * 1000));
+       if (hdev->cur_adv_instance == cp->instance) {
+               /* If the currently advertised instance is being changed then
+                * cancel the current advertising and schedule the next
+                * instance. If there is only one instance then the overridden
+                * advertising data will be visible right away.
+                */
+               cancel_adv_timeout(hdev);
 
-       if (!hci_dev_test_and_set_flag(hdev, HCI_ADVERTISING_INSTANCE))
-               advertising_added(sk, hdev, 1);
+               next_instance = hci_get_next_instance(hdev, cp->instance);
+               if (next_instance)
+                       schedule_instance = next_instance->instance;
+       } else if (!hdev->adv_instance_timeout) {
+               /* Immediately advertise the new instance if no other
+                * instance is currently being advertised.
+                */
+               schedule_instance = cp->instance;
+       }
 
-       /* If the HCI_ADVERTISING flag is set or the device isn't powered then
-        * we have no HCI communication to make. Simply return.
+       /* If the HCI_ADVERTISING flag is set or the device isn't powered or
+        * there is no instance to be advertised then we have no HCI
+        * communication to make. Simply return.
         */
        if (!hdev_is_powered(hdev) ||
-           hci_dev_test_flag(hdev, HCI_ADVERTISING)) {
-               rp.instance = 0x01;
+           hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
+           !schedule_instance) {
+               rp.instance = cp->instance;
                err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
                                        MGMT_STATUS_SUCCESS, &rp, sizeof(rp));
                goto unlock;
@@ -7192,11 +7230,11 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev,
 
        hci_req_init(&req, hdev);
 
-       update_adv_data(&req);
-       update_scan_rsp_data(&req);
-       enable_advertising(&req);
+       err = schedule_adv_instance(&req, schedule_instance, true);
+
+       if (!err)
+               err = hci_req_run(&req, add_advertising_complete);
 
-       err = hci_req_run(&req, add_advertising_complete);
        if (err < 0)
                mgmt_pending_remove(cmd);