Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/net/bluetooth/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 187 kB image not shown  

Quelle  hci_sync.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/*
 * BlueZ - Bluetooth protocol stack for Linux
 *
 * Copyright (C) 2021 Intel Corporation
 * Copyright 2023 NXP
 */


#include <linux/property.h>

#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/mgmt.h>

#include "hci_codec.h"
#include "hci_debugfs.h"
#include "smp.h"
#include "eir.h"
#include "msft.h"
#include "aosp.h"
#include "leds.h"

static void hci_cmd_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode,
      struct sk_buff *skb)
{
 bt_dev_dbg(hdev, "result 0x%2.2x", result);

 if (hdev->req_status != HCI_REQ_PEND)
  return;

 hdev->req_result = result;
 hdev->req_status = HCI_REQ_DONE;

 /* Free the request command so it is not used as response */
 kfree_skb(hdev->req_skb);
 hdev->req_skb = NULL;

 if (skb) {
  struct sock *sk = hci_skb_sk(skb);

  /* Drop sk reference if set */
  if (sk)
   sock_put(sk);

  hdev->req_rsp = skb_get(skb);
 }

 wake_up_interruptible(&hdev->req_wait_q);
}

struct sk_buff *hci_cmd_sync_alloc(struct hci_dev *hdev, u16 opcode, u32 plen,
       const void *param, struct sock *sk)
{
 int len = HCI_COMMAND_HDR_SIZE + plen;
 struct hci_command_hdr *hdr;
 struct sk_buff *skb;

 skb = bt_skb_alloc(len, GFP_ATOMIC);
 if (!skb)
  return NULL;

 hdr = skb_put(skb, HCI_COMMAND_HDR_SIZE);
 hdr->opcode = cpu_to_le16(opcode);
 hdr->plen   = plen;

 if (plen)
  skb_put_data(skb, param, plen);

 bt_dev_dbg(hdev, "skb len %d", skb->len);

 hci_skb_pkt_type(skb) = HCI_COMMAND_PKT;
 hci_skb_opcode(skb) = opcode;

 /* Grab a reference if command needs to be associated with a sock (e.g.
 * likely mgmt socket that initiated the command).
 */

 if (sk) {
  hci_skb_sk(skb) = sk;
  sock_hold(sk);
 }

 return skb;
}

static void hci_cmd_sync_add(struct hci_request *req, u16 opcode, u32 plen,
        const void *param, u8 event, struct sock *sk)
{
 struct hci_dev *hdev = req->hdev;
 struct sk_buff *skb;

 bt_dev_dbg(hdev, "opcode 0x%4.4x plen %d", opcode, plen);

 /* If an error occurred during request building, there is no point in
 * queueing the HCI command. We can simply return.
 */

 if (req->err)
  return;

 skb = hci_cmd_sync_alloc(hdev, opcode, plen, param, sk);
 if (!skb) {
  bt_dev_err(hdev, "no memory for command (opcode 0x%4.4x)",
      opcode);
  req->err = -ENOMEM;
  return;
 }

 if (skb_queue_empty(&req->cmd_q))
  bt_cb(skb)->hci.req_flags |= HCI_REQ_START;

 hci_skb_event(skb) = event;

 skb_queue_tail(&req->cmd_q, skb);
}

static int hci_req_sync_run(struct hci_request *req)
{
 struct hci_dev *hdev = req->hdev;
 struct sk_buff *skb;
 unsigned long flags;

 bt_dev_dbg(hdev, "length %u", skb_queue_len(&req->cmd_q));

 /* If an error occurred during request building, remove all HCI
 * commands queued on the HCI request queue.
 */

 if (req->err) {
  skb_queue_purge(&req->cmd_q);
  return req->err;
 }

 /* Do not allow empty requests */
 if (skb_queue_empty(&req->cmd_q))
  return -ENODATA;

 skb = skb_peek_tail(&req->cmd_q);
 bt_cb(skb)->hci.req_complete_skb = hci_cmd_sync_complete;
 bt_cb(skb)->hci.req_flags |= HCI_REQ_SKB;

 spin_lock_irqsave(&hdev->cmd_q.lock, flags);
 skb_queue_splice_tail(&req->cmd_q, &hdev->cmd_q);
 spin_unlock_irqrestore(&hdev->cmd_q.lock, flags);

 queue_work(hdev->workqueue, &hdev->cmd_work);

 return 0;
}

static void hci_request_init(struct hci_request *req, struct hci_dev *hdev)
{
 skb_queue_head_init(&req->cmd_q);
 req->hdev = hdev;
 req->err = 0;
}

/* This function requires the caller holds hdev->req_lock. */
struct sk_buff *__hci_cmd_sync_sk(struct hci_dev *hdev, u16 opcode, u32 plen,
      const void *param, u8 event, u32 timeout,
      struct sock *sk)
{
 struct hci_request req;
 struct sk_buff *skb;
 int err = 0;

 bt_dev_dbg(hdev, "Opcode 0x%4.4x", opcode);

 hci_request_init(&req, hdev);

 hci_cmd_sync_add(&req, opcode, plen, param, event, sk);

 hdev->req_status = HCI_REQ_PEND;

 err = hci_req_sync_run(&req);
 if (err < 0)
  return ERR_PTR(err);

 err = wait_event_interruptible_timeout(hdev->req_wait_q,
            hdev->req_status != HCI_REQ_PEND,
            timeout);

 if (err == -ERESTARTSYS)
  return ERR_PTR(-EINTR);

 switch (hdev->req_status) {
 case HCI_REQ_DONE:
  err = -bt_to_errno(hdev->req_result);
  break;

 case HCI_REQ_CANCELED:
  err = -hdev->req_result;
  break;

 default:
  err = -ETIMEDOUT;
  break;
 }

 hdev->req_status = 0;
 hdev->req_result = 0;
 skb = hdev->req_rsp;
 hdev->req_rsp = NULL;

 bt_dev_dbg(hdev, "end: err %d", err);

 if (err < 0) {
  kfree_skb(skb);
  return ERR_PTR(err);
 }

 /* If command return a status event skb will be set to NULL as there are
 * no parameters.
 */

 if (!skb)
  return ERR_PTR(-ENODATA);

 return skb;
}
EXPORT_SYMBOL(__hci_cmd_sync_sk);

/* This function requires the caller holds hdev->req_lock. */
struct sk_buff *__hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen,
          const void *param, u32 timeout)
{
 return __hci_cmd_sync_sk(hdev, opcode, plen, param, 0, timeout, NULL);
}
EXPORT_SYMBOL(__hci_cmd_sync);

/* Send HCI command and wait for command complete event */
struct sk_buff *hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen,
        const void *param, u32 timeout)
{
 struct sk_buff *skb;

 if (!test_bit(HCI_UP, &hdev->flags))
  return ERR_PTR(-ENETDOWN);

 bt_dev_dbg(hdev, "opcode 0x%4.4x plen %d", opcode, plen);

 hci_req_sync_lock(hdev);
 skb = __hci_cmd_sync(hdev, opcode, plen, param, timeout);
 hci_req_sync_unlock(hdev);

 return skb;
}
EXPORT_SYMBOL(hci_cmd_sync);

/* This function requires the caller holds hdev->req_lock. */
struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
      const void *param, u8 event, u32 timeout)
{
 return __hci_cmd_sync_sk(hdev, opcode, plen, param, event, timeout,
     NULL);
}
EXPORT_SYMBOL(__hci_cmd_sync_ev);

/* This function requires the caller holds hdev->req_lock. */
int __hci_cmd_sync_status_sk(struct hci_dev *hdev, u16 opcode, u32 plen,
        const void *param, u8 event, u32 timeout,
        struct sock *sk)
{
 struct sk_buff *skb;
 u8 status;

 skb = __hci_cmd_sync_sk(hdev, opcode, plen, param, event, timeout, sk);

 /* If command return a status event, skb will be set to -ENODATA */
 if (skb == ERR_PTR(-ENODATA))
  return 0;

 if (IS_ERR(skb)) {
  if (!event)
   bt_dev_err(hdev, "Opcode 0x%4.4x failed: %ld", opcode,
       PTR_ERR(skb));
  return PTR_ERR(skb);
 }

 status = skb->data[0];

 kfree_skb(skb);

 return status;
}
EXPORT_SYMBOL(__hci_cmd_sync_status_sk);

int __hci_cmd_sync_status(struct hci_dev *hdev, u16 opcode, u32 plen,
     const void *param, u32 timeout)
{
 return __hci_cmd_sync_status_sk(hdev, opcode, plen, param, 0, timeout,
     NULL);
}
EXPORT_SYMBOL(__hci_cmd_sync_status);

int hci_cmd_sync_status(struct hci_dev *hdev, u16 opcode, u32 plen,
   const void *param, u32 timeout)
{
 int err;

 hci_req_sync_lock(hdev);
 err = __hci_cmd_sync_status(hdev, opcode, plen, param, timeout);
 hci_req_sync_unlock(hdev);

 return err;
}
EXPORT_SYMBOL(hci_cmd_sync_status);

static void hci_cmd_sync_work(struct work_struct *work)
{
 struct hci_dev *hdev = container_of(work, struct hci_dev, cmd_sync_work);

 bt_dev_dbg(hdev, "");

 /* Dequeue all entries and run them */
 while (1) {
  struct hci_cmd_sync_work_entry *entry;

  mutex_lock(&hdev->cmd_sync_work_lock);
  entry = list_first_entry_or_null(&hdev->cmd_sync_work_list,
       struct hci_cmd_sync_work_entry,
       list);
  if (entry)
   list_del(&entry->list);
  mutex_unlock(&hdev->cmd_sync_work_lock);

  if (!entry)
   break;

  bt_dev_dbg(hdev, "entry %p", entry);

  if (entry->func) {
   int err;

   hci_req_sync_lock(hdev);
   err = entry->func(hdev, entry->data);
   if (entry->destroy)
    entry->destroy(hdev, entry->data, err);
   hci_req_sync_unlock(hdev);
  }

  kfree(entry);
 }
}

static void hci_cmd_sync_cancel_work(struct work_struct *work)
{
 struct hci_dev *hdev = container_of(work, struct hci_dev, cmd_sync_cancel_work);

 cancel_delayed_work_sync(&hdev->cmd_timer);
 cancel_delayed_work_sync(&hdev->ncmd_timer);
 atomic_set(&hdev->cmd_cnt, 1);

 wake_up_interruptible(&hdev->req_wait_q);
}

static int hci_scan_disable_sync(struct hci_dev *hdev);
static int scan_disable_sync(struct hci_dev *hdev, void *data)
{
 return hci_scan_disable_sync(hdev);
}

static int interleaved_inquiry_sync(struct hci_dev *hdev, void *data)
{
 return hci_inquiry_sync(hdev, DISCOV_INTERLEAVED_INQUIRY_LEN, 0);
}

static void le_scan_disable(struct work_struct *work)
{
 struct hci_dev *hdev = container_of(work, struct hci_dev,
         le_scan_disable.work);
 int status;

 bt_dev_dbg(hdev, "");
 hci_dev_lock(hdev);

 if (!hci_dev_test_flag(hdev, HCI_LE_SCAN))
  goto _return;

 status = hci_cmd_sync_queue(hdev, scan_disable_sync, NULL, NULL);
 if (status) {
  bt_dev_err(hdev, "failed to disable LE scan: %d", status);
  goto _return;
 }

 /* If we were running LE only scan, change discovery state. If
 * we were running both LE and BR/EDR inquiry simultaneously,
 * and BR/EDR inquiry is already finished, stop discovery,
 * otherwise BR/EDR inquiry will stop discovery when finished.
 * If we will resolve remote device name, do not change
 * discovery state.
 */


 if (hdev->discovery.type == DISCOV_TYPE_LE)
  goto discov_stopped;

 if (hdev->discovery.type != DISCOV_TYPE_INTERLEAVED)
  goto _return;

 if (hci_test_quirk(hdev, HCI_QUIRK_SIMULTANEOUS_DISCOVERY)) {
  if (!test_bit(HCI_INQUIRY, &hdev->flags) &&
      hdev->discovery.state != DISCOVERY_RESOLVING)
   goto discov_stopped;

  goto _return;
 }

 status = hci_cmd_sync_queue(hdev, interleaved_inquiry_sync, NULL, NULL);
 if (status) {
  bt_dev_err(hdev, "inquiry failed: status %d", status);
  goto discov_stopped;
 }

 goto _return;

discov_stopped:
 hci_discovery_set_state(hdev, DISCOVERY_STOPPED);

_return:
 hci_dev_unlock(hdev);
}

static int hci_le_set_scan_enable_sync(struct hci_dev *hdev, u8 val,
           u8 filter_dup);

static int reenable_adv_sync(struct hci_dev *hdev, void *data)
{
 bt_dev_dbg(hdev, "");

 if (!hci_dev_test_flag(hdev, HCI_ADVERTISING) &&
     list_empty(&hdev->adv_instances))
  return 0;

 if (hdev->cur_adv_instance) {
  return hci_schedule_adv_instance_sync(hdev,
            hdev->cur_adv_instance,
            true);
 } else {
  if (ext_adv_capable(hdev)) {
   hci_start_ext_adv_sync(hdev, 0x00);
  } else {
   hci_update_adv_data_sync(hdev, 0x00);
   hci_update_scan_rsp_data_sync(hdev, 0x00);
   hci_enable_advertising_sync(hdev);
  }
 }

 return 0;
}

static void reenable_adv(struct work_struct *work)
{
 struct hci_dev *hdev = container_of(work, struct hci_dev,
         reenable_adv_work);
 int status;

 bt_dev_dbg(hdev, "");

 hci_dev_lock(hdev);

 status = hci_cmd_sync_queue(hdev, reenable_adv_sync, NULL, NULL);
 if (status)
  bt_dev_err(hdev, "failed to reenable ADV: %d", status);

 hci_dev_unlock(hdev);
}

static void cancel_adv_timeout(struct hci_dev *hdev)
{
 if (hdev->adv_instance_timeout) {
  hdev->adv_instance_timeout = 0;
  cancel_delayed_work(&hdev->adv_instance_expire);
 }
}

/* 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.
 */

int hci_clear_adv_instance_sync(struct hci_dev *hdev, struct sock *sk,
    u8 instance, bool force)
{
 struct adv_info *adv_instance, *n, *next_instance = NULL;
 int err;
 u8 rem_inst;

 /* Cancel any timeout concerning the removed instance(s). */
 if (!instance || hdev->cur_adv_instance == instance)
  cancel_adv_timeout(hdev);

 /* 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);

 if (instance == 0x00) {
  list_for_each_entry_safe(adv_instance, n, &hdev->adv_instances,
      list) {
   if (!(force || adv_instance->timeout))
    continue;

   rem_inst = adv_instance->instance;
   err = hci_remove_adv_instance(hdev, rem_inst);
   if (!err)
    mgmt_advertising_removed(sk, hdev, rem_inst);
  }
 } 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)
    mgmt_advertising_removed(sk, hdev, instance);
  }
 }

 if (!hdev_is_powered(hdev) || hci_dev_test_flag(hdev, HCI_ADVERTISING))
  return 0;

 if (next_instance && !ext_adv_capable(hdev))
  return hci_schedule_adv_instance_sync(hdev,
            next_instance->instance,
            false);

 return 0;
}

static int adv_timeout_expire_sync(struct hci_dev *hdev, void *data)
{
 u8 instance = *(u8 *)data;

 kfree(data);

 hci_clear_adv_instance_sync(hdev, NULL, instance, false);

 if (list_empty(&hdev->adv_instances))
  return hci_disable_advertising_sync(hdev);

 return 0;
}

static void adv_timeout_expire(struct work_struct *work)
{
 u8 *inst_ptr;
 struct hci_dev *hdev = container_of(work, struct hci_dev,
         adv_instance_expire.work);

 bt_dev_dbg(hdev, "");

 hci_dev_lock(hdev);

 hdev->adv_instance_timeout = 0;

 if (hdev->cur_adv_instance == 0x00)
  goto unlock;

 inst_ptr = kmalloc(1, GFP_KERNEL);
 if (!inst_ptr)
  goto unlock;

 *inst_ptr = hdev->cur_adv_instance;
 hci_cmd_sync_queue(hdev, adv_timeout_expire_sync, inst_ptr, NULL);

unlock:
 hci_dev_unlock(hdev);
}

static bool is_interleave_scanning(struct hci_dev *hdev)
{
 return hdev->interleave_scan_state != INTERLEAVE_SCAN_NONE;
}

static int hci_passive_scan_sync(struct hci_dev *hdev);

static void interleave_scan_work(struct work_struct *work)
{
 struct hci_dev *hdev = container_of(work, struct hci_dev,
         interleave_scan.work);
 unsigned long timeout;

 if (hdev->interleave_scan_state == INTERLEAVE_SCAN_ALLOWLIST) {
  timeout = msecs_to_jiffies(hdev->advmon_allowlist_duration);
 } else if (hdev->interleave_scan_state == INTERLEAVE_SCAN_NO_FILTER) {
  timeout = msecs_to_jiffies(hdev->advmon_no_filter_duration);
 } else {
  bt_dev_err(hdev, "unexpected error");
  return;
 }

 hci_passive_scan_sync(hdev);

 hci_dev_lock(hdev);

 switch (hdev->interleave_scan_state) {
 case INTERLEAVE_SCAN_ALLOWLIST:
  bt_dev_dbg(hdev, "next state: allowlist");
  hdev->interleave_scan_state = INTERLEAVE_SCAN_NO_FILTER;
  break;
 case INTERLEAVE_SCAN_NO_FILTER:
  bt_dev_dbg(hdev, "next state: no filter");
  hdev->interleave_scan_state = INTERLEAVE_SCAN_ALLOWLIST;
  break;
 case INTERLEAVE_SCAN_NONE:
  bt_dev_err(hdev, "unexpected error");
 }

 hci_dev_unlock(hdev);

 /* Don't continue interleaving if it was canceled */
 if (is_interleave_scanning(hdev))
  queue_delayed_work(hdev->req_workqueue,
       &hdev->interleave_scan, timeout);
}

void hci_cmd_sync_init(struct hci_dev *hdev)
{
 INIT_WORK(&hdev->cmd_sync_work, hci_cmd_sync_work);
 INIT_LIST_HEAD(&hdev->cmd_sync_work_list);
 mutex_init(&hdev->cmd_sync_work_lock);
 mutex_init(&hdev->unregister_lock);

 INIT_WORK(&hdev->cmd_sync_cancel_work, hci_cmd_sync_cancel_work);
 INIT_WORK(&hdev->reenable_adv_work, reenable_adv);
 INIT_DELAYED_WORK(&hdev->le_scan_disable, le_scan_disable);
 INIT_DELAYED_WORK(&hdev->adv_instance_expire, adv_timeout_expire);
 INIT_DELAYED_WORK(&hdev->interleave_scan, interleave_scan_work);
}

static void _hci_cmd_sync_cancel_entry(struct hci_dev *hdev,
           struct hci_cmd_sync_work_entry *entry,
           int err)
{
 if (entry->destroy)
  entry->destroy(hdev, entry->data, err);

 list_del(&entry->list);
 kfree(entry);
}

void hci_cmd_sync_clear(struct hci_dev *hdev)
{
 struct hci_cmd_sync_work_entry *entry, *tmp;

 cancel_work_sync(&hdev->cmd_sync_work);
 cancel_work_sync(&hdev->reenable_adv_work);

 mutex_lock(&hdev->cmd_sync_work_lock);
 list_for_each_entry_safe(entry, tmp, &hdev->cmd_sync_work_list, list)
  _hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED);
 mutex_unlock(&hdev->cmd_sync_work_lock);
}

void hci_cmd_sync_cancel(struct hci_dev *hdev, int err)
{
 bt_dev_dbg(hdev, "err 0x%2.2x", err);

 if (hdev->req_status == HCI_REQ_PEND) {
  hdev->req_result = err;
  hdev->req_status = HCI_REQ_CANCELED;

  queue_work(hdev->workqueue, &hdev->cmd_sync_cancel_work);
 }
}
EXPORT_SYMBOL(hci_cmd_sync_cancel);

/* Cancel ongoing command request synchronously:
 *
 * - Set result and mark status to HCI_REQ_CANCELED
 * - Wakeup command sync thread
 */

void hci_cmd_sync_cancel_sync(struct hci_dev *hdev, int err)
{
 bt_dev_dbg(hdev, "err 0x%2.2x", err);

 if (hdev->req_status == HCI_REQ_PEND) {
  /* req_result is __u32 so error must be positive to be properly
 * propagated.
 */

  hdev->req_result = err < 0 ? -err : err;
  hdev->req_status = HCI_REQ_CANCELED;

  wake_up_interruptible(&hdev->req_wait_q);
 }
}
EXPORT_SYMBOL(hci_cmd_sync_cancel_sync);

/* Submit HCI command to be run in as cmd_sync_work:
 *
 * - hdev must _not_ be unregistered
 */

int hci_cmd_sync_submit(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
   void *data, hci_cmd_sync_work_destroy_t destroy)
{
 struct hci_cmd_sync_work_entry *entry;
 int err = 0;

 mutex_lock(&hdev->unregister_lock);
 if (hci_dev_test_flag(hdev, HCI_UNREGISTER)) {
  err = -ENODEV;
  goto unlock;
 }

 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
 if (!entry) {
  err = -ENOMEM;
  goto unlock;
 }
 entry->func = func;
 entry->data = data;
 entry->destroy = destroy;

 mutex_lock(&hdev->cmd_sync_work_lock);
 list_add_tail(&entry->list, &hdev->cmd_sync_work_list);
 mutex_unlock(&hdev->cmd_sync_work_lock);

 queue_work(hdev->req_workqueue, &hdev->cmd_sync_work);

unlock:
 mutex_unlock(&hdev->unregister_lock);
 return err;
}
EXPORT_SYMBOL(hci_cmd_sync_submit);

/* Queue HCI command:
 *
 * - hdev must be running
 */

int hci_cmd_sync_queue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
         void *data, hci_cmd_sync_work_destroy_t destroy)
{
 /* Only queue command if hdev is running which means it had been opened
 * and is either on init phase or is already up.
 */

 if (!test_bit(HCI_RUNNING, &hdev->flags))
  return -ENETDOWN;

 return hci_cmd_sync_submit(hdev, func, data, destroy);
}
EXPORT_SYMBOL(hci_cmd_sync_queue);

static struct hci_cmd_sync_work_entry *
_hci_cmd_sync_lookup_entry(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
      void *data, hci_cmd_sync_work_destroy_t destroy)
{
 struct hci_cmd_sync_work_entry *entry, *tmp;

 list_for_each_entry_safe(entry, tmp, &hdev->cmd_sync_work_list, list) {
  if (func && entry->func != func)
   continue;

  if (data && entry->data != data)
   continue;

  if (destroy && entry->destroy != destroy)
   continue;

  return entry;
 }

 return NULL;
}

/* Queue HCI command entry once:
 *
 * - Lookup if an entry already exist and only if it doesn't creates a new entry
 *   and queue it.
 */

int hci_cmd_sync_queue_once(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
       void *data, hci_cmd_sync_work_destroy_t destroy)
{
 if (hci_cmd_sync_lookup_entry(hdev, func, data, destroy))
  return 0;

 return hci_cmd_sync_queue(hdev, func, data, destroy);
}
EXPORT_SYMBOL(hci_cmd_sync_queue_once);

/* Run HCI command:
 *
 * - hdev must be running
 * - if on cmd_sync_work then run immediately otherwise queue
 */

int hci_cmd_sync_run(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
       void *data, hci_cmd_sync_work_destroy_t destroy)
{
 /* Only queue command if hdev is running which means it had been opened
 * and is either on init phase or is already up.
 */

 if (!test_bit(HCI_RUNNING, &hdev->flags))
  return -ENETDOWN;

 /* If on cmd_sync_work then run immediately otherwise queue */
 if (current_work() == &hdev->cmd_sync_work)
  return func(hdev, data);

 return hci_cmd_sync_submit(hdev, func, data, destroy);
}
EXPORT_SYMBOL(hci_cmd_sync_run);

/* Run HCI command entry once:
 *
 * - Lookup if an entry already exist and only if it doesn't creates a new entry
 *   and run it.
 * - if on cmd_sync_work then run immediately otherwise queue
 */

int hci_cmd_sync_run_once(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
     void *data, hci_cmd_sync_work_destroy_t destroy)
{
 if (hci_cmd_sync_lookup_entry(hdev, func, data, destroy))
  return 0;

 return hci_cmd_sync_run(hdev, func, data, destroy);
}
EXPORT_SYMBOL(hci_cmd_sync_run_once);

/* Lookup HCI command entry:
 *
 * - Return first entry that matches by function callback or data or
 *   destroy callback.
 */

struct hci_cmd_sync_work_entry *
hci_cmd_sync_lookup_entry(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
     void *data, hci_cmd_sync_work_destroy_t destroy)
{
 struct hci_cmd_sync_work_entry *entry;

 mutex_lock(&hdev->cmd_sync_work_lock);
 entry = _hci_cmd_sync_lookup_entry(hdev, func, data, destroy);
 mutex_unlock(&hdev->cmd_sync_work_lock);

 return entry;
}
EXPORT_SYMBOL(hci_cmd_sync_lookup_entry);

/* Cancel HCI command entry */
void hci_cmd_sync_cancel_entry(struct hci_dev *hdev,
          struct hci_cmd_sync_work_entry *entry)
{
 mutex_lock(&hdev->cmd_sync_work_lock);
 _hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED);
 mutex_unlock(&hdev->cmd_sync_work_lock);
}
EXPORT_SYMBOL(hci_cmd_sync_cancel_entry);

/* Dequeue one HCI command entry:
 *
 * - Lookup and cancel first entry that matches.
 */

bool hci_cmd_sync_dequeue_once(struct hci_dev *hdev,
          hci_cmd_sync_work_func_t func,
          void *data, hci_cmd_sync_work_destroy_t destroy)
{
 struct hci_cmd_sync_work_entry *entry;

 mutex_lock(&hdev->cmd_sync_work_lock);

 entry = _hci_cmd_sync_lookup_entry(hdev, func, data, destroy);
 if (!entry) {
  mutex_unlock(&hdev->cmd_sync_work_lock);
  return false;
 }

 _hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED);

 mutex_unlock(&hdev->cmd_sync_work_lock);

 return true;
}
EXPORT_SYMBOL(hci_cmd_sync_dequeue_once);

/* Dequeue HCI command entry:
 *
 * - Lookup and cancel any entry that matches by function callback or data or
 *   destroy callback.
 */

bool hci_cmd_sync_dequeue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
     void *data, hci_cmd_sync_work_destroy_t destroy)
{
 struct hci_cmd_sync_work_entry *entry;
 bool ret = false;

 mutex_lock(&hdev->cmd_sync_work_lock);
 while ((entry = _hci_cmd_sync_lookup_entry(hdev, func, data,
         destroy))) {
  _hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED);
  ret = true;
 }
 mutex_unlock(&hdev->cmd_sync_work_lock);

 return ret;
}
EXPORT_SYMBOL(hci_cmd_sync_dequeue);

int hci_update_eir_sync(struct hci_dev *hdev)
{
 struct hci_cp_write_eir cp;

 bt_dev_dbg(hdev, "");

 if (!hdev_is_powered(hdev))
  return 0;

 if (!lmp_ext_inq_capable(hdev))
  return 0;

 if (!hci_dev_test_flag(hdev, HCI_SSP_ENABLED))
  return 0;

 if (hci_dev_test_flag(hdev, HCI_SERVICE_CACHE))
  return 0;

 memset(&cp, 0, sizeof(cp));

 eir_create(hdev, cp.data);

 if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0)
  return 0;

 memcpy(hdev->eir, cp.data, sizeof(cp.data));

 return __hci_cmd_sync_status(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp,
         HCI_CMD_TIMEOUT);
}

static u8 get_service_classes(struct hci_dev *hdev)
{
 struct bt_uuid *uuid;
 u8 val = 0;

 list_for_each_entry(uuid, &hdev->uuids, list)
  val |= uuid->svc_hint;

 return val;
}

int hci_update_class_sync(struct hci_dev *hdev)
{
 u8 cod[3];

 bt_dev_dbg(hdev, "");

 if (!hdev_is_powered(hdev))
  return 0;

 if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED))
  return 0;

 if (hci_dev_test_flag(hdev, HCI_SERVICE_CACHE))
  return 0;

 cod[0] = hdev->minor_class;
 cod[1] = hdev->major_class;
 cod[2] = get_service_classes(hdev);

 if (hci_dev_test_flag(hdev, HCI_LIMITED_DISCOVERABLE))
  cod[1] |= 0x20;

 if (memcmp(cod, hdev->dev_class, 3) == 0)
  return 0;

 return __hci_cmd_sync_status(hdev, HCI_OP_WRITE_CLASS_OF_DEV,
         sizeof(cod), cod, HCI_CMD_TIMEOUT);
}

static bool is_advertising_allowed(struct hci_dev *hdev, bool connectable)
{
 /* If there is no connection we are OK to advertise. */
 if (hci_conn_num(hdev, LE_LINK) == 0)
  return true;

 /* Check le_states if there is any connection in peripheral role. */
 if (hdev->conn_hash.le_num_peripheral > 0) {
  /* Peripheral connection state and non connectable mode
 * bit 20.
 */

  if (!connectable && !(hdev->le_states[2] & 0x10))
   return false;

  /* Peripheral connection state and connectable mode bit 38
 * and scannable bit 21.
 */

  if (connectable && (!(hdev->le_states[4] & 0x40) ||
        !(hdev->le_states[2] & 0x20)))
   return false;
 }

 /* Check le_states if there is any connection in central role. */
 if (hci_conn_num(hdev, LE_LINK) != hdev->conn_hash.le_num_peripheral) {
  /* Central connection state and non connectable mode bit 18. */
  if (!connectable && !(hdev->le_states[2] & 0x02))
   return false;

  /* Central connection state and connectable mode bit 35 and
 * scannable 19.
 */

  if (connectable && (!(hdev->le_states[4] & 0x08) ||
        !(hdev->le_states[2] & 0x08)))
   return false;
 }

 return true;
}

static bool adv_use_rpa(struct hci_dev *hdev, uint32_t flags)
{
 /* If privacy is not enabled don't use RPA */
 if (!hci_dev_test_flag(hdev, HCI_PRIVACY))
  return false;

 /* If basic privacy mode is enabled use RPA */
 if (!hci_dev_test_flag(hdev, HCI_LIMITED_PRIVACY))
  return true;

 /* If limited privacy mode is enabled don't use RPA if we're
 * both discoverable and bondable.
 */

 if ((flags & MGMT_ADV_FLAG_DISCOV) &&
     hci_dev_test_flag(hdev, HCI_BONDABLE))
  return false;

 /* We're neither bondable nor discoverable in the limited
 * privacy mode, therefore use RPA.
 */

 return true;
}

static int hci_set_random_addr_sync(struct hci_dev *hdev, bdaddr_t *rpa)
{
 /* If a random_addr has been set we're advertising or initiating an LE
 * connection we can't go ahead and change the random address at this
 * time. This is because the eventual initiator address used for the
 * subsequently created connection will be undefined (some
 * controllers use the new address and others the one we had
 * when the operation started).
 *
 * In this kind of scenario skip the update and let the random
 * address be updated at the next cycle.
 */

 if (bacmp(&hdev->random_addr, BDADDR_ANY) &&
     (hci_dev_test_flag(hdev, HCI_LE_ADV) ||
     hci_lookup_le_connect(hdev))) {
  bt_dev_dbg(hdev, "Deferring random address update");
  hci_dev_set_flag(hdev, HCI_RPA_EXPIRED);
  return 0;
 }

 return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_RANDOM_ADDR,
         6, rpa, HCI_CMD_TIMEOUT);
}

int hci_update_random_address_sync(struct hci_dev *hdev, bool require_privacy,
       bool rpa, u8 *own_addr_type)
{
 int err;

 /* If privacy is enabled use a resolvable private address. If
 * current RPA has expired or there is something else than
 * the current RPA in use, then generate a new one.
 */

 if (rpa) {
  /* If Controller supports LL Privacy use own address type is
 * 0x03
 */

  if (ll_privacy_capable(hdev))
   *own_addr_type = ADDR_LE_DEV_RANDOM_RESOLVED;
  else
   *own_addr_type = ADDR_LE_DEV_RANDOM;

  /* Check if RPA is valid */
  if (rpa_valid(hdev))
   return 0;

  err = smp_generate_rpa(hdev, hdev->irk, &hdev->rpa);
  if (err < 0) {
   bt_dev_err(hdev, "failed to generate new RPA");
   return err;
  }

  err = hci_set_random_addr_sync(hdev, &hdev->rpa);
  if (err)
   return err;

  return 0;
 }

 /* In case of required privacy without resolvable private address,
 * use an non-resolvable private address. This is useful for active
 * scanning and non-connectable advertising.
 */

 if (require_privacy) {
  bdaddr_t nrpa;

  while (true) {
   /* The non-resolvable private address is generated
 * from random six bytes with the two most significant
 * bits cleared.
 */

   get_random_bytes(&nrpa, 6);
   nrpa.b[5] &= 0x3f;

   /* The non-resolvable private address shall not be
 * equal to the public address.
 */

   if (bacmp(&hdev->bdaddr, &nrpa))
    break;
  }

  *own_addr_type = ADDR_LE_DEV_RANDOM;

  return hci_set_random_addr_sync(hdev, &nrpa);
 }

 /* If forcing static address is in use or there is no public
 * address use the static address as random address (but skip
 * the HCI command if the current random address is already the
 * static one.
 *
 * In case BR/EDR has been disabled on a dual-mode controller
 * and a static address has been configured, then use that
 * address instead of the public BR/EDR address.
 */

 if (hci_dev_test_flag(hdev, HCI_FORCE_STATIC_ADDR) ||
     !bacmp(&hdev->bdaddr, BDADDR_ANY) ||
     (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED) &&
      bacmp(&hdev->static_addr, BDADDR_ANY))) {
  *own_addr_type = ADDR_LE_DEV_RANDOM;
  if (bacmp(&hdev->static_addr, &hdev->random_addr))
   return hci_set_random_addr_sync(hdev,
       &hdev->static_addr);
  return 0;
 }

 /* Neither privacy nor static address is being used so use a
 * public address.
 */

 *own_addr_type = ADDR_LE_DEV_PUBLIC;

 return 0;
}

static int hci_disable_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance)
{
 struct hci_cp_le_set_ext_adv_enable *cp;
 struct hci_cp_ext_adv_set *set;
 u8 data[sizeof(*cp) + sizeof(*set) * 1];
 u8 size;
 struct adv_info *adv = NULL;

 /* If request specifies an instance that doesn't exist, fail */
 if (instance > 0) {
  adv = hci_find_adv_instance(hdev, instance);
  if (!adv)
   return -EINVAL;

  /* If not enabled there is nothing to do */
  if (!adv->enabled)
   return 0;
 }

 memset(data, 0, sizeof(data));

 cp = (void *)data;
 set = (void *)cp->data;

 /* Instance 0x00 indicates all advertising instances will be disabled */
 cp->num_of_sets = !!instance;
 cp->enable = 0x00;

 set->handle = adv ? adv->handle : instance;

 size = sizeof(*cp) + sizeof(*set) * cp->num_of_sets;

 return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_ADV_ENABLE,
         size, data, HCI_CMD_TIMEOUT);
}

static int hci_set_adv_set_random_addr_sync(struct hci_dev *hdev, u8 instance,
         bdaddr_t *random_addr)
{
 struct hci_cp_le_set_adv_set_rand_addr cp;
 int err;

 if (!instance) {
  /* Instance 0x00 doesn't have an adv_info, instead it uses
 * hdev->random_addr to track its address so whenever it needs
 * to be updated this also set the random address since
 * hdev->random_addr is shared with scan state machine.
 */

  err = hci_set_random_addr_sync(hdev, random_addr);
  if (err)
   return err;
 }

 memset(&cp, 0, sizeof(cp));

 cp.handle = instance;
 bacpy(&cp.bdaddr, random_addr);

 return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_ADV_SET_RAND_ADDR,
         sizeof(cp), &cp, HCI_CMD_TIMEOUT);
}

static int
hci_set_ext_adv_params_sync(struct hci_dev *hdev, struct adv_info *adv,
       const struct hci_cp_le_set_ext_adv_params *cp,
       struct hci_rp_le_set_ext_adv_params *rp)
{
 struct sk_buff *skb;

 skb = __hci_cmd_sync(hdev, HCI_OP_LE_SET_EXT_ADV_PARAMS, sizeof(*cp),
        cp, HCI_CMD_TIMEOUT);

 /* If command return a status event, skb will be set to -ENODATA */
 if (skb == ERR_PTR(-ENODATA))
  return 0;

 if (IS_ERR(skb)) {
  bt_dev_err(hdev, "Opcode 0x%4.4x failed: %ld",
      HCI_OP_LE_SET_EXT_ADV_PARAMS, PTR_ERR(skb));
  return PTR_ERR(skb);
 }

 if (skb->len != sizeof(*rp)) {
  bt_dev_err(hdev, "Invalid response length for 0x%4.4x: %u",
      HCI_OP_LE_SET_EXT_ADV_PARAMS, skb->len);
  kfree_skb(skb);
  return -EIO;
 }

 memcpy(rp, skb->data, sizeof(*rp));
 kfree_skb(skb);

 if (!rp->status) {
  hdev->adv_addr_type = cp->own_addr_type;
  if (!cp->handle) {
   /* Store in hdev for instance 0 */
   hdev->adv_tx_power = rp->tx_power;
  } else if (adv) {
   adv->tx_power = rp->tx_power;
  }
 }

 return rp->status;
}

static int hci_set_ext_adv_data_sync(struct hci_dev *hdev, u8 instance)
{
 DEFINE_FLEX(struct hci_cp_le_set_ext_adv_data, pdu, data, length,
      HCI_MAX_EXT_AD_LENGTH);
 u8 len;
 struct adv_info *adv = NULL;
 int err;

 if (instance) {
  adv = hci_find_adv_instance(hdev, instance);
  if (!adv || !adv->adv_data_changed)
   return 0;
 }

 len = eir_create_adv_data(hdev, instance, pdu->data,
      HCI_MAX_EXT_AD_LENGTH);

 pdu->length = len;
 pdu->handle = adv ? adv->handle : instance;
 pdu->operation = LE_SET_ADV_DATA_OP_COMPLETE;
 pdu->frag_pref = LE_SET_ADV_DATA_NO_FRAG;

 err = __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_ADV_DATA,
        struct_size(pdu, data, len), pdu,
        HCI_CMD_TIMEOUT);
 if (err)
  return err;

 /* Update data if the command succeed */
 if (adv) {
  adv->adv_data_changed = false;
 } else {
  memcpy(hdev->adv_data, pdu->data, len);
  hdev->adv_data_len = len;
 }

 return 0;
}

static int hci_set_adv_data_sync(struct hci_dev *hdev, u8 instance)
{
 struct hci_cp_le_set_adv_data cp;
 u8 len;

 memset(&cp, 0, sizeof(cp));

 len = eir_create_adv_data(hdev, instance, cp.data, sizeof(cp.data));

 /* There's nothing to do if the data hasn't changed */
 if (hdev->adv_data_len == len &&
     memcmp(cp.data, hdev->adv_data, len) == 0)
  return 0;

 memcpy(hdev->adv_data, cp.data, sizeof(cp.data));
 hdev->adv_data_len = len;

 cp.length = len;

 return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_ADV_DATA,
         sizeof(cp), &cp, HCI_CMD_TIMEOUT);
}

int hci_update_adv_data_sync(struct hci_dev *hdev, u8 instance)
{
 if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED))
  return 0;

 if (ext_adv_capable(hdev))
  return hci_set_ext_adv_data_sync(hdev, instance);

 return hci_set_adv_data_sync(hdev, instance);
}

int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance)
{
 struct hci_cp_le_set_ext_adv_params cp;
 struct hci_rp_le_set_ext_adv_params rp;
 bool connectable, require_privacy;
 u32 flags;
 bdaddr_t random_addr;
 u8 own_addr_type;
 int err;
 struct adv_info *adv;
 bool secondary_adv;

 if (instance > 0) {
  adv = hci_find_adv_instance(hdev, instance);
  if (!adv)
   return -EINVAL;
 } else {
  adv = NULL;
 }

 /* Updating parameters of an active instance will return a
 * Command Disallowed error, so we must first disable the
 * instance if it is active.
 */

 if (adv) {
  err = hci_disable_ext_adv_instance_sync(hdev, instance);
  if (err)
   return err;
 }

 flags = hci_adv_instance_flags(hdev, instance);

 /* If the "connectable" instance flag was not set, then choose between
 * ADV_IND and ADV_NONCONN_IND based on the global connectable setting.
 */

 connectable = (flags & MGMT_ADV_FLAG_CONNECTABLE) ||
        mgmt_get_connectable(hdev);

 if (!is_advertising_allowed(hdev, connectable))
  return -EPERM;

 /* Set require_privacy to true only when non-connectable
 * advertising is used and it is not periodic.
 * In that case it is fine to use a non-resolvable private address.
 */

 require_privacy = !connectable && !(adv && adv->periodic);

 err = hci_get_random_address(hdev, require_privacy,
         adv_use_rpa(hdev, flags), adv,
         &own_addr_type, &random_addr);
 if (err < 0)
  return err;

 memset(&cp, 0, sizeof(cp));

 if (adv) {
  hci_cpu_to_le24(adv->min_interval, cp.min_interval);
  hci_cpu_to_le24(adv->max_interval, cp.max_interval);
  cp.tx_power = adv->tx_power;
  cp.sid = adv->sid;
 } else {
  hci_cpu_to_le24(hdev->le_adv_min_interval, cp.min_interval);
  hci_cpu_to_le24(hdev->le_adv_max_interval, cp.max_interval);
  cp.tx_power = HCI_ADV_TX_POWER_NO_PREFERENCE;
  cp.sid = 0x00;
 }

 secondary_adv = (flags & MGMT_ADV_FLAG_SEC_MASK);

 if (connectable) {
  if (secondary_adv)
   cp.evt_properties = cpu_to_le16(LE_EXT_ADV_CONN_IND);
  else
   cp.evt_properties = cpu_to_le16(LE_LEGACY_ADV_IND);
 } else if (hci_adv_instance_is_scannable(hdev, instance) ||
     (flags & MGMT_ADV_PARAM_SCAN_RSP)) {
  if (secondary_adv)
   cp.evt_properties = cpu_to_le16(LE_EXT_ADV_SCAN_IND);
  else
   cp.evt_properties = cpu_to_le16(LE_LEGACY_ADV_SCAN_IND);
 } else {
  if (secondary_adv)
   cp.evt_properties = cpu_to_le16(LE_EXT_ADV_NON_CONN_IND);
  else
   cp.evt_properties = cpu_to_le16(LE_LEGACY_NONCONN_IND);
 }

 /* If Own_Address_Type equals 0x02 or 0x03, the Peer_Address parameter
 * contains the peer’s Identity Address and the Peer_Address_Type
 * parameter contains the peer’s Identity Type (i.e., 0x00 or 0x01).
 * These parameters are used to locate the corresponding local IRK in
 * the resolving list; this IRK is used to generate their own address
 * used in the advertisement.
 */

 if (own_addr_type == ADDR_LE_DEV_RANDOM_RESOLVED)
  hci_copy_identity_address(hdev, &cp.peer_addr,
       &cp.peer_addr_type);

 cp.own_addr_type = own_addr_type;
 cp.channel_map = hdev->le_adv_channel_map;
 cp.handle = adv ? adv->handle : instance;

 if (flags & MGMT_ADV_FLAG_SEC_2M) {
  cp.primary_phy = HCI_ADV_PHY_1M;
  cp.secondary_phy = HCI_ADV_PHY_2M;
 } else if (flags & MGMT_ADV_FLAG_SEC_CODED) {
  cp.primary_phy = HCI_ADV_PHY_CODED;
  cp.secondary_phy = HCI_ADV_PHY_CODED;
 } else {
  /* In all other cases use 1M */
  cp.primary_phy = HCI_ADV_PHY_1M;
  cp.secondary_phy = HCI_ADV_PHY_1M;
 }

 err = hci_set_ext_adv_params_sync(hdev, adv, &cp, &rp);
 if (err)
  return err;

 /* Update adv data as tx power is known now */
 err = hci_set_ext_adv_data_sync(hdev, cp.handle);
 if (err)
  return err;

 if ((own_addr_type == ADDR_LE_DEV_RANDOM ||
      own_addr_type == ADDR_LE_DEV_RANDOM_RESOLVED) &&
     bacmp(&random_addr, BDADDR_ANY)) {
  /* Check if random address need to be updated */
  if (adv) {
   if (!bacmp(&random_addr, &adv->random_addr))
    return 0;
  } else {
   if (!bacmp(&random_addr, &hdev->random_addr))
    return 0;
  }

  return hci_set_adv_set_random_addr_sync(hdev, instance,
       &random_addr);
 }

 return 0;
}

static int hci_set_ext_scan_rsp_data_sync(struct hci_dev *hdev, u8 instance)
{
 DEFINE_FLEX(struct hci_cp_le_set_ext_scan_rsp_data, pdu, data, length,
      HCI_MAX_EXT_AD_LENGTH);
 u8 len;
 struct adv_info *adv = NULL;
 int err;

 if (instance) {
  adv = hci_find_adv_instance(hdev, instance);
  if (!adv || !adv->scan_rsp_changed)
   return 0;
 }

 len = eir_create_scan_rsp(hdev, instance, pdu->data);

 pdu->handle = adv ? adv->handle : instance;
 pdu->length = len;
 pdu->operation = LE_SET_ADV_DATA_OP_COMPLETE;
 pdu->frag_pref = LE_SET_ADV_DATA_NO_FRAG;

 err = __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_SCAN_RSP_DATA,
        struct_size(pdu, data, len), pdu,
        HCI_CMD_TIMEOUT);
 if (err)
  return err;

 if (adv) {
  adv->scan_rsp_changed = false;
 } else {
  memcpy(hdev->scan_rsp_data, pdu->data, len);
  hdev->scan_rsp_data_len = len;
 }

 return 0;
}

static int __hci_set_scan_rsp_data_sync(struct hci_dev *hdev, u8 instance)
{
 struct hci_cp_le_set_scan_rsp_data cp;
 u8 len;

 memset(&cp, 0, sizeof(cp));

 len = eir_create_scan_rsp(hdev, instance, cp.data);

 if (hdev->scan_rsp_data_len == len &&
     !memcmp(cp.data, hdev->scan_rsp_data, len))
  return 0;

 memcpy(hdev->scan_rsp_data, cp.data, sizeof(cp.data));
 hdev->scan_rsp_data_len = len;

 cp.length = len;

 return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_SCAN_RSP_DATA,
         sizeof(cp), &cp, HCI_CMD_TIMEOUT);
}

int hci_update_scan_rsp_data_sync(struct hci_dev *hdev, u8 instance)
{
 if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED))
  return 0;

 if (ext_adv_capable(hdev))
  return hci_set_ext_scan_rsp_data_sync(hdev, instance);

 return __hci_set_scan_rsp_data_sync(hdev, instance);
}

int hci_enable_ext_advertising_sync(struct hci_dev *hdev, u8 instance)
{
 struct hci_cp_le_set_ext_adv_enable *cp;
 struct hci_cp_ext_adv_set *set;
 u8 data[sizeof(*cp) + sizeof(*set) * 1];
 struct adv_info *adv;

 if (instance > 0) {
  adv = hci_find_adv_instance(hdev, instance);
  if (!adv)
   return -EINVAL;
  /* If already enabled there is nothing to do */
  if (adv->enabled)
   return 0;
 } else {
  adv = NULL;
 }

 cp = (void *)data;
 set = (void *)cp->data;

 memset(cp, 0, sizeof(*cp));

 cp->enable = 0x01;
 cp->num_of_sets = 0x01;

 memset(set, 0, sizeof(*set));

 set->handle = adv ? adv->handle : instance;

 /* Set duration per instance since controller is responsible for
 * scheduling it.
 */

 if (adv && adv->timeout) {
  u16 duration = adv->timeout * MSEC_PER_SEC;

  /* Time = N * 10 ms */
  set->duration = cpu_to_le16(duration / 10);
 }

 return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_ADV_ENABLE,
         sizeof(*cp) +
         sizeof(*set) * cp->num_of_sets,
         data, HCI_CMD_TIMEOUT);
}

int hci_start_ext_adv_sync(struct hci_dev *hdev, u8 instance)
{
 int err;

 err = hci_setup_ext_adv_instance_sync(hdev, instance);
 if (err)
  return err;

 err = hci_set_ext_scan_rsp_data_sync(hdev, instance);
 if (err)
  return err;

 return hci_enable_ext_advertising_sync(hdev, instance);
}

int hci_disable_per_advertising_sync(struct hci_dev *hdev, u8 instance)
{
 struct hci_cp_le_set_per_adv_enable cp;
 struct adv_info *adv = NULL;

 /* If periodic advertising already disabled there is nothing to do. */
 adv = hci_find_adv_instance(hdev, instance);
 if (!adv || !adv->periodic_enabled)
  return 0;

 memset(&cp, 0, sizeof(cp));

 cp.enable = 0x00;
 cp.handle = instance;

 return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_PER_ADV_ENABLE,
         sizeof(cp), &cp, HCI_CMD_TIMEOUT);
}

static int hci_set_per_adv_params_sync(struct hci_dev *hdev, u8 instance,
           u16 min_interval, u16 max_interval)
{
 struct hci_cp_le_set_per_adv_params cp;

 memset(&cp, 0, sizeof(cp));

 if (!min_interval)
  min_interval = DISCOV_LE_PER_ADV_INT_MIN;

 if (!max_interval)
  max_interval = DISCOV_LE_PER_ADV_INT_MAX;

 cp.handle = instance;
 cp.min_interval = cpu_to_le16(min_interval);
 cp.max_interval = cpu_to_le16(max_interval);
 cp.periodic_properties = 0x0000;

 return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_PER_ADV_PARAMS,
         sizeof(cp), &cp, HCI_CMD_TIMEOUT);
}

static int hci_set_per_adv_data_sync(struct hci_dev *hdev, u8 instance)
{
 DEFINE_FLEX(struct hci_cp_le_set_per_adv_data, pdu, data, length,
      HCI_MAX_PER_AD_LENGTH);
 u8 len;
 struct adv_info *adv = NULL;

 if (instance) {
  adv = hci_find_adv_instance(hdev, instance);
  if (!adv || !adv->periodic)
   return 0;
 }

 len = eir_create_per_adv_data(hdev, instance, pdu->data);

 pdu->length = len;
 pdu->handle = adv ? adv->handle : instance;
 pdu->operation = LE_SET_ADV_DATA_OP_COMPLETE;

 return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_PER_ADV_DATA,
         struct_size(pdu, data, len), pdu,
         HCI_CMD_TIMEOUT);
}

static int hci_enable_per_advertising_sync(struct hci_dev *hdev, u8 instance)
{
 struct hci_cp_le_set_per_adv_enable cp;
 struct adv_info *adv = NULL;

 /* If periodic advertising already enabled there is nothing to do. */
 adv = hci_find_adv_instance(hdev, instance);
 if (adv && adv->periodic_enabled)
  return 0;

 memset(&cp, 0, sizeof(cp));

 cp.enable = 0x01;
 cp.handle = instance;

 return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_PER_ADV_ENABLE,
         sizeof(cp), &cp, HCI_CMD_TIMEOUT);
}

/* Checks if periodic advertising data contains a Basic Announcement and if it
 * does generates a Broadcast ID and add Broadcast Announcement.
 */

static int hci_adv_bcast_annoucement(struct hci_dev *hdev, struct adv_info *adv)
{
 u8 bid[3];
 u8 ad[HCI_MAX_EXT_AD_LENGTH];
 u8 len;

 /* Skip if NULL adv as instance 0x00 is used for general purpose
 * advertising so it cannot used for the likes of Broadcast Announcement
 * as it can be overwritten at any point.
 */

 if (!adv)
  return 0;

 /* Check if PA data doesn't contains a Basic Audio Announcement then
 * there is nothing to do.
 */

 if (!eir_get_service_data(adv->per_adv_data, adv->per_adv_data_len,
      0x1851, NULL))
  return 0;

 /* Check if advertising data already has a Broadcast Announcement since
 * the process may want to control the Broadcast ID directly and in that
 * case the kernel shall no interfere.
 */

 if (eir_get_service_data(adv->adv_data, adv->adv_data_len, 0x1852,
     NULL))
  return 0;

 /* Generate Broadcast ID */
 get_random_bytes(bid, sizeof(bid));
 len = eir_append_service_data(ad, 0, 0x1852, bid, sizeof(bid));
 memcpy(ad + len, adv->adv_data, adv->adv_data_len);
 hci_set_adv_instance_data(hdev, adv->instance, len + adv->adv_data_len,
      ad, 0, NULL);

 return hci_update_adv_data_sync(hdev, adv->instance);
}

int hci_start_per_adv_sync(struct hci_dev *hdev, u8 instance, u8 sid,
      u8 data_len, u8 *data, u32 flags, u16 min_interval,
      u16 max_interval, u16 sync_interval)
{
 struct adv_info *adv = NULL;
 int err;
 bool added = false;

 hci_disable_per_advertising_sync(hdev, instance);

 if (instance) {
  adv = hci_find_adv_instance(hdev, instance);
  if (adv) {
   if (sid != HCI_SID_INVALID && adv->sid != sid) {
    /* If the SID don't match attempt to find by
 * SID.
 */

    adv = hci_find_adv_sid(hdev, sid);
    if (!adv) {
     bt_dev_err(hdev,
         "Unable to find adv_info");
     return -EINVAL;
    }
   }

   /* Turn it into periodic advertising */
   adv->periodic = true;
   adv->per_adv_data_len = data_len;
   if (data)
    memcpy(adv->per_adv_data, data, data_len);
   adv->flags = flags;
  } else if (!adv) {
   /* Create an instance if that could not be found */
   adv = hci_add_per_instance(hdev, instance, sid, flags,
         data_len, data,
         sync_interval,
         sync_interval);
   if (IS_ERR(adv))
    return PTR_ERR(adv);
   adv->pending = false;
   added = true;
  }
 }

 /* Start advertising */
 err = hci_start_ext_adv_sync(hdev, instance);
 if (err < 0)
  goto fail;

 err = hci_adv_bcast_annoucement(hdev, adv);
 if (err < 0)
  goto fail;

 err = hci_set_per_adv_params_sync(hdev, instance, min_interval,
       max_interval);
 if (err < 0)
  goto fail;

 err = hci_set_per_adv_data_sync(hdev, instance);
 if (err < 0)
  goto fail;

 err = hci_enable_per_advertising_sync(hdev, instance);
 if (err < 0)
  goto fail;

 return 0;

fail:
 if (added)
  hci_remove_adv_instance(hdev, instance);

 return err;
}

static int hci_start_adv_sync(struct hci_dev *hdev, u8 instance)
{
 int err;

 if (ext_adv_capable(hdev))
  return hci_start_ext_adv_sync(hdev, instance);

 err = hci_update_adv_data_sync(hdev, instance);
 if (err)
  return err;

 err = hci_update_scan_rsp_data_sync(hdev, instance);
 if (err)
  return err;

 return hci_enable_advertising_sync(hdev);
}

int hci_enable_advertising_sync(struct hci_dev *hdev)
{
 struct adv_info *adv_instance;
 struct hci_cp_le_set_adv_param cp;
 u8 own_addr_type, enable = 0x01;
 bool connectable;
 u16 adv_min_interval, adv_max_interval;
 u32 flags;
 u8 status;

 if (ext_adv_capable(hdev))
  return hci_enable_ext_advertising_sync(hdev,
             hdev->cur_adv_instance);

 flags = hci_adv_instance_flags(hdev, hdev->cur_adv_instance);
 adv_instance = hci_find_adv_instance(hdev, hdev->cur_adv_instance);

 /* If the "connectable" instance flag was not set, then choose between
 * ADV_IND and ADV_NONCONN_IND based on the global connectable setting.
 */

 connectable = (flags & MGMT_ADV_FLAG_CONNECTABLE) ||
        mgmt_get_connectable(hdev);

 if (!is_advertising_allowed(hdev, connectable))
  return -EINVAL;

 status = hci_disable_advertising_sync(hdev);
 if (status)
  return status;

 /* Clear the HCI_LE_ADV bit temporarily so that the
 * hci_update_random_address knows that it's safe to go ahead
 * and write a new random address. The flag will be set back on
 * as soon as the SET_ADV_ENABLE HCI command completes.
 */

 hci_dev_clear_flag(hdev, HCI_LE_ADV);

 /* Set require_privacy to true only when non-connectable
 * advertising is used. In that case it is fine to use a
 * non-resolvable private address.
 */

 status = hci_update_random_address_sync(hdev, !connectable,
      adv_use_rpa(hdev, flags),
      &own_addr_type);
 if (status)
  return status;

 memset(&cp, 0, sizeof(cp));

 if (adv_instance) {
  adv_min_interval = adv_instance->min_interval;
  adv_max_interval = adv_instance->max_interval;
 } else {
  adv_min_interval = hdev->le_adv_min_interval;
  adv_max_interval = hdev->le_adv_max_interval;
 }

 if (connectable) {
  cp.type = LE_ADV_IND;
 } else {
  if (hci_adv_instance_is_scannable(hdev, hdev->cur_adv_instance))
   cp.type = LE_ADV_SCAN_IND;
  else
   cp.type = LE_ADV_NONCONN_IND;

  if (!hci_dev_test_flag(hdev, HCI_DISCOVERABLE) ||
      hci_dev_test_flag(hdev, HCI_LIMITED_DISCOVERABLE)) {
   adv_min_interval = DISCOV_LE_FAST_ADV_INT_MIN;
   adv_max_interval = DISCOV_LE_FAST_ADV_INT_MAX;
  }
 }

 cp.min_interval = cpu_to_le16(adv_min_interval);
 cp.max_interval = cpu_to_le16(adv_max_interval);
 cp.own_address_type = own_addr_type;
 cp.channel_map = hdev->le_adv_channel_map;

 status = __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_ADV_PARAM,
           sizeof(cp), &cp, HCI_CMD_TIMEOUT);
 if (status)
  return status;

 return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_ADV_ENABLE,
         sizeof(enable), &enable, HCI_CMD_TIMEOUT);
}

static int enable_advertising_sync(struct hci_dev *hdev, void *data)
{
 return hci_enable_advertising_sync(hdev);
}

int hci_enable_advertising(struct hci_dev *hdev)
{
 if (!hci_dev_test_flag(hdev, HCI_ADVERTISING) &&
     list_empty(&hdev->adv_instances))
  return 0;

 return hci_cmd_sync_queue(hdev, enable_advertising_sync, NULL, NULL);
}

int hci_remove_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance,
         struct sock *sk)
{
 int err;

 if (!ext_adv_capable(hdev))
  return 0;

 err = hci_disable_ext_adv_instance_sync(hdev, instance);
 if (err)
  return err;

 /* If request specifies an instance that doesn't exist, fail */
 if (instance > 0 && !hci_find_adv_instance(hdev, instance))
  return -EINVAL;

 return __hci_cmd_sync_status_sk(hdev, HCI_OP_LE_REMOVE_ADV_SET,
     sizeof(instance), &instance, 0,
     HCI_CMD_TIMEOUT, sk);
}

int hci_le_terminate_big_sync(struct hci_dev *hdev, u8 handle, u8 reason)
{
 struct hci_cp_le_term_big cp;

 memset(&cp, 0, sizeof(cp));
 cp.handle = handle;
 cp.reason = reason;

 return __hci_cmd_sync_status(hdev, HCI_OP_LE_TERM_BIG,
         sizeof(cp), &cp, HCI_CMD_TIMEOUT);
}

int hci_schedule_adv_instance_sync(struct hci_dev *hdev, u8 instance,
       bool force)
{
 struct adv_info *adv = NULL;
 u16 timeout;

 if (hci_dev_test_flag(hdev, HCI_ADVERTISING) && !ext_adv_capable(hdev))
  return -EPERM;

 if (hdev->adv_instance_timeout)
  return -EBUSY;

 adv = hci_find_adv_instance(hdev, instance);
 if (!adv)
  return -ENOENT;

 /* A zero timeout means unlimited advertising. As long as there is
 * only one instance, duration should be ignored. We still set a timeout
 * in case further instances are being added later on.
 *
 * If the remaining lifetime of the instance is more than the duration
 * then the timeout corresponds to the duration, otherwise it will be
 * reduced to the remaining instance lifetime.
 */

 if (adv->timeout == 0 || adv->duration <= adv->remaining_time)
  timeout = adv->duration;
 else
  timeout = adv->remaining_time;

 /* The remaining time is being reduced unless the instance is being
 * advertised without time limit.
 */

 if (adv->timeout)
  adv->remaining_time = adv->remaining_time - timeout;

 /* Only use work for scheduling instances with legacy advertising */
 if (!ext_adv_capable(hdev)) {
  hdev->adv_instance_timeout = timeout;
  queue_delayed_work(hdev->req_workqueue,
       &hdev->adv_instance_expire,
       secs_to_jiffies(timeout));
 }

 /* If we're just re-scheduling the same instance again then do not
 * execute any HCI commands. This happens when a single instance is
 * being advertised.
 */

 if (!force && hdev->cur_adv_instance == instance &&
     hci_dev_test_flag(hdev, HCI_LE_ADV))
  return 0;

 hdev->cur_adv_instance = instance;

 return hci_start_adv_sync(hdev, instance);
}

static int hci_clear_adv_sets_sync(struct hci_dev *hdev, struct sock *sk)
{
 int err;

 if (!ext_adv_capable(hdev))
  return 0;

 /* Disable instance 0x00 to disable all instances */
 err = hci_disable_ext_adv_instance_sync(hdev, 0x00);
 if (err)
  return err;

 return __hci_cmd_sync_status_sk(hdev, HCI_OP_LE_CLEAR_ADV_SETS,
     0, NULL, 0, HCI_CMD_TIMEOUT, sk);
}

static int hci_clear_adv_sync(struct hci_dev *hdev, struct sock *sk, bool force)
{
 struct adv_info *adv, *n;

 if (ext_adv_capable(hdev))
  /* Remove all existing sets */
  return hci_clear_adv_sets_sync(hdev, sk);

 /* This is safe as long as there is no command send while the lock is
 * held.
 */

 hci_dev_lock(hdev);

 /* Cleanup non-ext instances */
 list_for_each_entry_safe(adv, n, &hdev->adv_instances, list) {
  u8 instance = adv->instance;
  int err;

  if (!(force || adv->timeout))
   continue;

  err = hci_remove_adv_instance(hdev, instance);
  if (!err)
   mgmt_advertising_removed(sk, hdev, instance);
 }

 hci_dev_unlock(hdev);

 return 0;
}

static int hci_remove_adv_sync(struct hci_dev *hdev, u8 instance,
          struct sock *sk)
{
 int err;

 /* If we use extended advertising, instance has to be removed first. */
 if (ext_adv_capable(hdev))
  return hci_remove_ext_adv_instance_sync(hdev, instance, sk);

 /* This is safe as long as there is no command send while the lock is
 * held.
 */

 hci_dev_lock(hdev);

 err = hci_remove_adv_instance(hdev, instance);
 if (!err)
  mgmt_advertising_removed(sk, hdev, instance);

 hci_dev_unlock(hdev);

 return err;
}

/* 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.
 */

int hci_remove_advertising_sync(struct hci_dev *hdev, struct sock *sk,
    u8 instance, bool force)
{
 struct adv_info *next = NULL;
 int err;

 /* Cancel any timeout concerning the removed instance(s). */
 if (!instance || hdev->cur_adv_instance == instance)
  cancel_adv_timeout(hdev);

 /* 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 (hdev->cur_adv_instance == instance)
  next = hci_get_next_instance(hdev, instance);

 if (!instance) {
  err = hci_clear_adv_sync(hdev, sk, force);
  if (err)
   return err;
 } else {
  struct adv_info *adv = hci_find_adv_instance(hdev, instance);

  if (force || (adv && adv->timeout && !adv->remaining_time)) {
   /* Don't advertise a removed instance. */
   if (next && next->instance == instance)
    next = NULL;

   err = hci_remove_adv_sync(hdev, instance, sk);
   if (err)
    return err;
  }
 }

 if (!hdev_is_powered(hdev) || hci_dev_test_flag(hdev, HCI_ADVERTISING))
  return 0;

 if (next && !ext_adv_capable(hdev))
  hci_schedule_adv_instance_sync(hdev, next->instance, false);

 return 0;
}

int hci_read_rssi_sync(struct hci_dev *hdev, __le16 handle)
{
 struct hci_cp_read_rssi cp;

 cp.handle = handle;
 return __hci_cmd_sync_status(hdev, HCI_OP_READ_RSSI,
     sizeof(cp), &cp, HCI_CMD_TIMEOUT);
}

int hci_read_clock_sync(struct hci_dev *hdev, struct hci_cp_read_clock *cp)
{
 return __hci_cmd_sync_status(hdev, HCI_OP_READ_CLOCK,
     sizeof(*cp), cp, HCI_CMD_TIMEOUT);
}

int hci_read_tx_power_sync(struct hci_dev *hdev, __le16 handle, u8 type)
{
 struct hci_cp_read_tx_power cp;

 cp.handle = handle;
 cp.type = type;
 return __hci_cmd_sync_status(hdev, HCI_OP_READ_TX_POWER,
     sizeof(cp), &cp, HCI_CMD_TIMEOUT);
}

int hci_disable_advertising_sync(struct hci_dev *hdev)
{
 u8 enable = 0x00;

 /* If controller is not advertising we are done. */
 if (!hci_dev_test_flag(hdev, HCI_LE_ADV))
  return 0;

 if (ext_adv_capable(hdev))
  return hci_disable_ext_adv_instance_sync(hdev, 0x00);

 return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_ADV_ENABLE,
         sizeof(enable), &enable, HCI_CMD_TIMEOUT);
}

static int hci_le_set_ext_scan_enable_sync(struct hci_dev *hdev, u8 val,
        u8 filter_dup)
{
 struct hci_cp_le_set_ext_scan_enable cp;

 memset(&cp, 0, sizeof(cp));
 cp.enable = val;

 if (hci_dev_test_flag(hdev, HCI_MESH))
  cp.filter_dup = LE_SCAN_FILTER_DUP_DISABLE;
 else
  cp.filter_dup = filter_dup;

 return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_SCAN_ENABLE,
         sizeof(cp), &cp, HCI_CMD_TIMEOUT);
}

static int hci_le_set_scan_enable_sync(struct hci_dev *hdev, u8 val,
           u8 filter_dup)
{
 struct hci_cp_le_set_scan_enable cp;

 if (use_ext_scan(hdev))
  return hci_le_set_ext_scan_enable_sync(hdev, val, filter_dup);

 memset(&cp, 0, sizeof(cp));
 cp.enable = val;

 if (val && hci_dev_test_flag(hdev, HCI_MESH))
  cp.filter_dup = LE_SCAN_FILTER_DUP_DISABLE;
 else
  cp.filter_dup = filter_dup;

 return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_SCAN_ENABLE,
         sizeof(cp), &cp, HCI_CMD_TIMEOUT);
}

static int hci_le_set_addr_resolution_enable_sync(struct hci_dev *hdev, u8 val)
{
 if (!ll_privacy_capable(hdev))
  return 0;

 /* If controller is not/already resolving we are done. */
 if (val == hci_dev_test_flag(hdev, HCI_LL_RPA_RESOLUTION))
  return 0;

 return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_ADDR_RESOLV_ENABLE,
         sizeof(val), &val, HCI_CMD_TIMEOUT);
}

static int hci_scan_disable_sync(struct hci_dev *hdev)
{
 int err;

 /* If controller is not scanning we are done. */
 if (!hci_dev_test_flag(hdev, HCI_LE_SCAN))
  return 0;

 if (hdev->scanning_paused) {
  bt_dev_dbg(hdev, "Scanning is paused for suspend");
  return 0;
 }

 err = hci_le_set_scan_enable_sync(hdev, LE_SCAN_DISABLE, 0x00);
 if (err) {
  bt_dev_err(hdev, "Unable to disable scanning: %d", err);
  return err;
 }

 return err;
}

static bool scan_use_rpa(struct hci_dev *hdev)
{
 return hci_dev_test_flag(hdev, HCI_PRIVACY);
}

static void hci_start_interleave_scan(struct hci_dev *hdev)
{
 hdev->interleave_scan_state = INTERLEAVE_SCAN_NO_FILTER;
 queue_delayed_work(hdev->req_workqueue,
      &hdev->interleave_scan, 0);
}

static void cancel_interleave_scan(struct hci_dev *hdev)
{
 bt_dev_dbg(hdev, "cancelling interleave scan");

 cancel_delayed_work_sync(&hdev->interleave_scan);

 hdev->interleave_scan_state = INTERLEAVE_SCAN_NONE;
}

/* Return true if interleave_scan wasn't started until exiting this function,
 * otherwise, return false
 */

static bool hci_update_interleaved_scan_sync(struct hci_dev *hdev)
{
 /* Do interleaved scan only if all of the following are true:
 * - There is at least one ADV monitor
 * - At least one pending LE connection or one device to be scanned for
 * - Monitor offloading is not supported
 * If so, we should alternate between allowlist scan and one without
 * any filters to save power.
 */

 bool use_interleaving = hci_is_adv_monitoring(hdev) &&
    !(list_empty(&hdev->pend_le_conns) &&
      list_empty(&hdev->pend_le_reports)) &&
    hci_get_adv_monitor_offload_ext(hdev) ==
        HCI_ADV_MONITOR_EXT_NONE;
 bool is_interleaving = is_interleave_scanning(hdev);

 if (use_interleaving && !is_interleaving) {
  hci_start_interleave_scan(hdev);
  bt_dev_dbg(hdev, "starting interleave scan");
  return true;
 }

 if (!use_interleaving && is_interleaving)
  cancel_interleave_scan(hdev);

 return false;
}

/* Removes connection to resolve list if needed.*/
static int hci_le_del_resolve_list_sync(struct hci_dev *hdev,
     bdaddr_t *bdaddr, u8 bdaddr_type)
{
 struct hci_cp_le_del_from_resolv_list cp;
 struct bdaddr_list_with_irk *entry;

 if (!ll_privacy_capable(hdev))
  return 0;

 /* Check if the IRK has been programmed */
 entry = hci_bdaddr_list_lookup_with_irk(&hdev->le_resolv_list, bdaddr,
      bdaddr_type);
 if (!entry)
  return 0;

 cp.bdaddr_type = bdaddr_type;
 bacpy(&cp.bdaddr, bdaddr);

 return __hci_cmd_sync_status(hdev, HCI_OP_LE_DEL_FROM_RESOLV_LIST,
         sizeof(cp), &cp, HCI_CMD_TIMEOUT);
}

static int hci_le_del_accept_list_sync(struct hci_dev *hdev,
           bdaddr_t *bdaddr, u8 bdaddr_type)
{
 struct hci_cp_le_del_from_accept_list cp;
 int err;

 /* Check if device is on accept list before removing it */
 if (!hci_bdaddr_list_lookup(&hdev->le_accept_list, bdaddr, bdaddr_type))
  return 0;

 cp.bdaddr_type = bdaddr_type;
 bacpy(&cp.bdaddr, bdaddr);

 /* Ignore errors when removing from resolving list as that is likely
 * that the device was never added.
 */

 hci_le_del_resolve_list_sync(hdev, &cp.bdaddr, cp.bdaddr_type);

 err = __hci_cmd_sync_status(hdev, HCI_OP_LE_DEL_FROM_ACCEPT_LIST,
        sizeof(cp), &cp, HCI_CMD_TIMEOUT);
 if (err) {
  bt_dev_err(hdev, "Unable to remove from allow list: %d", err);
  return err;
 }

 bt_dev_dbg(hdev, "Remove %pMR (0x%x) from allow list", &cp.bdaddr,
     cp.bdaddr_type);

 return 0;
}

struct conn_params {
 bdaddr_t addr;
 u8 addr_type;
 hci_conn_flags_t flags;
 u8 privacy_mode;
};

/* Adds connection to resolve list if needed.
 * Setting params to NULL programs local hdev->irk
 */

static int hci_le_add_resolve_list_sync(struct hci_dev *hdev,
     struct conn_params *params)
{
 struct hci_cp_le_add_to_resolv_list cp;
 struct smp_irk *irk;
 struct bdaddr_list_with_irk *entry;
 struct hci_conn_params *p;

 if (!ll_privacy_capable(hdev))
  return 0;

 /* Attempt to program local identity address, type and irk if params is
 * NULL.
 */

 if (!params) {
  if (!hci_dev_test_flag(hdev, HCI_PRIVACY))
   return 0;

  hci_copy_identity_address(hdev, &cp.bdaddr, &cp.bdaddr_type);
  memcpy(cp.peer_irk, hdev->irk, 16);
  goto done;
 } else if (!(params->flags & HCI_CONN_FLAG_ADDRESS_RESOLUTION))
  return 0;

 irk = hci_find_irk_by_addr(hdev, ¶ms->addr, params->addr_type);
 if (!irk)
  return 0;

 /* Check if the IK has _not_ been programmed yet. */
 entry = hci_bdaddr_list_lookup_with_irk(&hdev->le_resolv_list,
      ¶ms->addr,
      params->addr_type);
 if (entry)
  return 0;

 cp.bdaddr_type = params->addr_type;
 bacpy(&cp.bdaddr, ¶ms->addr);
 memcpy(cp.peer_irk, irk->val, 16);

 /* Default privacy mode is always Network */
 params->privacy_mode = HCI_NETWORK_PRIVACY;

 rcu_read_lock();
 p = hci_pend_le_action_lookup(&hdev->pend_le_conns,
          ¶ms->addr, params->addr_type);
 if (!p)
  p = hci_pend_le_action_lookup(&hdev->pend_le_reports,
           ¶ms->addr, params->addr_type);
 if (p)
  WRITE_ONCE(p->privacy_mode, HCI_NETWORK_PRIVACY);
 rcu_read_unlock();

done:
 if (hci_dev_test_flag(hdev, HCI_PRIVACY))
  memcpy(cp.local_irk, hdev->irk, 16);
 else
  memset(cp.local_irk, 0, 16);

 return __hci_cmd_sync_status(hdev, HCI_OP_LE_ADD_TO_RESOLV_LIST,
         sizeof(cp), &cp, HCI_CMD_TIMEOUT);
}

/* Set Device Privacy Mode. */
static int hci_le_set_privacy_mode_sync(struct hci_dev *hdev,
     struct conn_params *params)
{
 struct hci_cp_le_set_privacy_mode cp;
 struct smp_irk *irk;

 if (!ll_privacy_capable(hdev) ||
     !(params->flags & HCI_CONN_FLAG_ADDRESS_RESOLUTION))
  return 0;

 /* If device privacy mode has already been set there is nothing to do */
 if (params->privacy_mode == HCI_DEVICE_PRIVACY)
  return 0;

 /* Check if HCI_CONN_FLAG_DEVICE_PRIVACY has been set as it also
 * indicates that LL Privacy has been enabled and
 * HCI_OP_LE_SET_PRIVACY_MODE is supported.
 */

 if (!(params->flags & HCI_CONN_FLAG_DEVICE_PRIVACY))
  return 0;

 irk = hci_find_irk_by_addr(hdev, ¶ms->addr, params->addr_type);
 if (!irk)
  return 0;

 memset(&cp, 0, sizeof(cp));
 cp.bdaddr_type = irk->addr_type;
 bacpy(&cp.bdaddr, &irk->bdaddr);
 cp.mode = HCI_DEVICE_PRIVACY;

 /* Note: params->privacy_mode is not updated since it is a copy */

 return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_PRIVACY_MODE,
         sizeof(cp), &cp, HCI_CMD_TIMEOUT);
}

/* Adds connection to allow list if needed, if the device uses RPA (has IRK)
 * this attempts to program the device in the resolving list as well and
 * properly set the privacy mode.
 */

static int hci_le_add_accept_list_sync(struct hci_dev *hdev,
           struct conn_params *params,
           u8 *num_entries)
{
 struct hci_cp_le_add_to_accept_list cp;
 int err;

 /* During suspend, only wakeable devices can be in acceptlist */
 if (hdev->suspended &&
     !(params->flags & HCI_CONN_FLAG_REMOTE_WAKEUP)) {
  hci_le_del_accept_list_sync(hdev, ¶ms->addr,
         params->addr_type);
  return 0;
 }

 /* Select filter policy to accept all advertising */
 if (*num_entries >= hdev->le_accept_list_size)
  return -ENOSPC;

 /* Attempt to program the device in the resolving list first to avoid
 * having to rollback in case it fails since the resolving list is
 * dynamic it can probably be smaller than the accept list.
 */

 err = hci_le_add_resolve_list_sync(hdev, params);
 if (err) {
  bt_dev_err(hdev, "Unable to add to resolve list: %d", err);
  return err;
 }

 /* Set Privacy Mode */
 err = hci_le_set_privacy_mode_sync(hdev, params);
 if (err) {
  bt_dev_err(hdev, "Unable to set privacy mode: %d", err);
  return err;
 }

 /* Check if already in accept list */
 if (hci_bdaddr_list_lookup(&hdev->le_accept_list, ¶ms->addr,
       params->addr_type))
  return 0;

 *num_entries += 1;
 cp.bdaddr_type = params->addr_type;
 bacpy(&cp.bdaddr, ¶ms->addr);

 err = __hci_cmd_sync_status(hdev, HCI_OP_LE_ADD_TO_ACCEPT_LIST,
        sizeof(cp), &cp, HCI_CMD_TIMEOUT);
 if (err) {
  bt_dev_err(hdev, "Unable to add to allow list: %d", err);
  /* Rollback the device from the resolving list */
  hci_le_del_resolve_list_sync(hdev, &cp.bdaddr, cp.bdaddr_type);
  return err;
 }

 bt_dev_dbg(hdev, "Add %pMR (0x%x) to allow list", &cp.bdaddr,
     cp.bdaddr_type);

 return 0;
}

--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=96 H=97 G=96

¤ Dauer der Verarbeitung: 0.21 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.