|
@@ -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);
|
|
|
|