/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "syscfg/syscfg.h"
#include "os/os.h"
#include "os/os_cputime.h"
#include "nimble/ble.h"
#include "nimble/nimble_opt.h"
#include "nimble/hci_common.h"
#include "nimble/ble_hci_trans.h"
#include "controller/ble_phy.h"
#include "controller/ble_hw.h"
#include "controller/ble_ll.h"
#include "controller/ble_ll_sched.h"
#include "controller/ble_ll_adv.h"
#include "controller/ble_ll_scan.h"
#include "controller/ble_ll_hci.h"
#include "controller/ble_ll_whitelist.h"
#include "controller/ble_ll_resolv.h"
#include "controller/ble_ll_xcvr.h"
#include "controller/ble_ll_trace.h"
#include "ble_ll_conn_priv.h"

/*
 * XXX:
 * 1) I think I can guarantee that we dont process things out of order if
 * I send an event when a scan request is sent. The scan_rsp_pending flag
 * code might be made simpler.
 *
 * 2) Interleave sending scan requests to different advertisers? I guess I need
 * a list of advertisers to which I sent a scan request and have yet to
 * receive a scan response from? Implement this.
 */

/* Dont allow more than 255 of these entries */
#if MYNEWT_VAL(BLE_LL_NUM_SCAN_DUP_ADVS) > 255
    #error "Cannot have more than 255 duplicate entries!"
#endif
#if MYNEWT_VAL(BLE_LL_NUM_SCAN_RSP_ADVS) > 255
    #error "Cannot have more than 255 scan response entries!"
#endif

#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
static const uint8_t ble_ll_valid_scan_phy_mask = (BLE_HCI_LE_PHY_1M_PREF_MASK
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY)
                                | BLE_HCI_LE_PHY_CODED_PREF_MASK
#endif
                              );
#endif

/* The scanning parameters set by host */
static struct ble_ll_scan_params g_ble_ll_scan_params[BLE_LL_SCAN_PHY_NUMBER];

/* The scanning state machine global object */
static struct ble_ll_scan_sm g_ble_ll_scan_sm;

#define BLE_LL_EXT_ADV_ADVA_BIT         (0)
#define BLE_LL_EXT_ADV_TARGETA_BIT      (1)
#define BLE_LL_EXT_ADV_RFU_BIT          (2)
#define BLE_LL_EXT_ADV_DATA_INFO_BIT    (3)
#define BLE_LL_EXT_ADV_AUX_PTR_BIT      (4)
#define BLE_LL_EXT_ADV_SYNC_INFO_BIT    (5)
#define BLE_LL_EXT_ADV_TX_POWER_BIT     (6)

#define BLE_LL_EXT_ADV_ADVA_SIZE        (6)
#define BLE_LL_EXT_ADV_TARGETA_SIZE     (6)
#define BLE_LL_EXT_ADV_DATA_INFO_SIZE   (2)
#define BLE_LL_EXT_ADV_AUX_PTR_SIZE     (3)
#define BLE_LL_EXT_ADV_SYNC_INFO_SIZE   (18)
#define BLE_LL_EXT_ADV_TX_POWER_SIZE    (1)

struct ble_ll_ext_adv_hdr
{
    uint8_t mode;
    uint8_t hdr_len;
    uint8_t hdr[0];
};

struct ble_ll_ext_adv_report {
    /* We support one report per event for now */
    uint8_t event_meta; /* BLE_HCI_EVCODE_LE_META */
    uint8_t event_len;
    uint8_t subevt;
    uint8_t num_reports;

    uint16_t evt_type;
    uint8_t addr_type;
    uint8_t addr[6];
    uint8_t prim_phy;
    uint8_t sec_phy;
    uint8_t sid;
    uint8_t tx_power;
    int8_t rssi;
    uint16_t per_adv_itvl;
    uint8_t dir_addr_type;
    uint8_t dir_addr[6];
    uint8_t adv_data_len;
    uint8_t adv_data[0];
} __attribute__((packed));

/*
 * Structure used to store advertisers. This is used to limit sending scan
 * requests to the same advertiser and also to filter duplicate events sent
 * to the host.
 */
struct ble_ll_scan_advertisers
{
    uint16_t            sc_adv_flags;
    uint16_t            adi;
    struct ble_dev_addr adv_addr;
};

#define BLE_LL_SC_ADV_F_RANDOM_ADDR     (0x01)
#define BLE_LL_SC_ADV_F_SCAN_RSP_RXD    (0x02)
#define BLE_LL_SC_ADV_F_DIRECT_RPT_SENT (0x04)
#define BLE_LL_SC_ADV_F_ADV_RPT_SENT    (0x08)
#define BLE_LL_SC_ADV_F_SCAN_RSP_SENT   (0x10)

/* Contains list of advertisers that we have heard scan responses from */
static uint8_t g_ble_ll_scan_num_rsp_advs;
struct ble_ll_scan_advertisers
g_ble_ll_scan_rsp_advs[MYNEWT_VAL(BLE_LL_NUM_SCAN_RSP_ADVS)];

/* Used to filter duplicate advertising events to host */
static uint8_t g_ble_ll_scan_num_dup_advs;
struct ble_ll_scan_advertisers
g_ble_ll_scan_dup_advs[MYNEWT_VAL(BLE_LL_NUM_SCAN_DUP_ADVS)];

#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
static os_membuf_t ext_adv_mem[ OS_MEMPOOL_SIZE(
                    MYNEWT_VAL(BLE_LL_EXT_ADV_AUX_PTR_CNT),
                    sizeof (struct ble_ll_aux_data))
];

static struct os_mempool ext_adv_pool;

static int ble_ll_scan_start(struct ble_ll_scan_sm *scansm,
                             struct ble_ll_sched_item *sch);

static int
ble_ll_aux_scan_cb(struct ble_ll_sched_item *sch)
{
    struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm;
    uint8_t lls = ble_ll_state_get();
    uint32_t wfr_usec;

    STATS_INC(ble_ll_stats, aux_sched_cb);

    /* In case scan has been disabled or there is other aux ptr in progress
     * just drop the scheduled item
     */
    if (!scansm->scan_enabled || scansm->cur_aux_data) {
        ble_ll_scan_aux_data_unref(sch->cb_arg);
        goto done;
    }

    /* Check if there is no aux connect sent. If so drop the sched item */
    if (lls == BLE_LL_STATE_INITIATING && ble_ll_conn_init_pending_aux_conn_rsp()) {
        ble_ll_scan_aux_data_unref(sch->cb_arg);
        goto done;
    }

    /* This function is called only when scanner is running. This can happen
     *  in 3 states:
     * BLE_LL_STATE_SCANNING
     * BLE_LL_STATE_INITIATING
     * BLE_LL_STATE_STANDBY
     */
    if (lls != BLE_LL_STATE_STANDBY) {
        ble_phy_disable();
        ble_ll_wfr_disable();
        ble_ll_state_set(BLE_LL_STATE_STANDBY);
    }

    /* When doing RX for AUX pkt, cur_aux_data keeps valid aux data */
    scansm->cur_aux_data = sch->cb_arg;
    BLE_LL_ASSERT(scansm->cur_aux_data != NULL);
    scansm->cur_aux_data->scanning = 1;

    if (ble_ll_scan_start(scansm, sch)) {
        ble_ll_scan_aux_data_unref(scansm->cur_aux_data);
        scansm->cur_aux_data = NULL;
        ble_ll_scan_chk_resume();
        goto done;
    }

    STATS_INC(ble_ll_stats, aux_fired_for_read);

    wfr_usec = scansm->cur_aux_data->offset_units ? 300 : 30;
    ble_phy_wfr_enable(BLE_PHY_WFR_ENABLE_RX, 0, wfr_usec);

done:

    return BLE_LL_SCHED_STATE_DONE;
}

static int
ble_ll_scan_ext_adv_init(struct ble_ll_aux_data **aux_data)
{
    struct ble_ll_aux_data *e;

    e = os_memblock_get(&ext_adv_pool);
    if (!e) {
        return -1;
    }

    memset(e, 0, sizeof(*e));
    e->sch.sched_cb = ble_ll_aux_scan_cb;
    e->sch.cb_arg = e;
    e->sch.sched_type = BLE_LL_SCHED_TYPE_AUX_SCAN;
    e->ref_cnt = 1;
    ble_ll_trace_u32x2(BLE_LL_TRACE_ID_AUX_REF, (uint32_t)e, e->ref_cnt);

    *aux_data = e;
    STATS_INC(ble_ll_stats, aux_allocated);

    return 0;
}
#endif

/* See Vol 6 Part B Section 4.4.3.2. Active scanning backoff */
static void
ble_ll_scan_req_backoff(struct ble_ll_scan_sm *scansm, int success)
{
    scansm->scan_rsp_pending = 0;
    if (success) {
        scansm->scan_rsp_cons_fails = 0;
        ++scansm->scan_rsp_cons_ok;
        if (scansm->scan_rsp_cons_ok == 2) {
            scansm->scan_rsp_cons_ok = 0;
            if (scansm->upper_limit > 1) {
                scansm->upper_limit >>= 1;
            }
        }
        STATS_INC(ble_ll_stats, scan_req_txg);
    } else {
        scansm->scan_rsp_cons_ok = 0;
        ++scansm->scan_rsp_cons_fails;
        if (scansm->scan_rsp_cons_fails == 2) {
            scansm->scan_rsp_cons_fails = 0;
            if (scansm->upper_limit < 256) {
                scansm->upper_limit <<= 1;
            }
        }
        STATS_INC(ble_ll_stats, scan_req_txf);
    }

    scansm->backoff_count = rand() & (scansm->upper_limit - 1);
    ++scansm->backoff_count;
    BLE_LL_ASSERT(scansm->backoff_count <= 256);
}

#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1)
static void
ble_ll_scan_refresh_nrpa(struct ble_ll_scan_sm *scansm)
{
    ble_npl_time_t now;

    now = ble_npl_time_get();
    if ((ble_npl_stime_t)(now - scansm->scan_nrpa_timer) >= 0) {
        /* Generate new NRPA */
        ble_ll_rand_data_get(scansm->scan_nrpa, BLE_DEV_ADDR_LEN);
        scansm->scan_nrpa[5] &= ~0xc0;

        /* We'll use the same timeout as for RPA rotation */
        scansm->scan_nrpa_timer = now + ble_ll_resolv_get_rpa_tmo();
    }
}
#endif

/**
 * ble ll scan req pdu make
 *
 * Construct a SCAN_REQ PDU.
 *
 * @param scansm Pointer to scanning state machine
 * @param adv_addr Pointer to device address of advertiser
 * @param addr_type 0 if public; non-zero if random. This is the addr type of
 *                  the advertiser; not our "own address type"
 */
static void
ble_ll_scan_req_pdu_make(struct ble_ll_scan_sm *scansm, uint8_t *adv_addr,
                         uint8_t adv_addr_type)
{
    uint8_t     *dptr;
    uint8_t     pdu_type;
    uint8_t     *scana;
    struct os_mbuf *m;
#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1)
    uint8_t rpa[BLE_DEV_ADDR_LEN];
    struct ble_ll_resolv_entry *rl;
#endif

    /* Construct first PDU header byte */
    pdu_type = BLE_ADV_PDU_TYPE_SCAN_REQ;
    if (adv_addr_type) {
        pdu_type |= BLE_ADV_PDU_HDR_RXADD_RAND;
    }

    /* Get the advertising PDU */
    m = scansm->scan_req_pdu;
    BLE_LL_ASSERT(m != NULL);

    /* Get pointer to our device address */
    if ((scansm->own_addr_type & 1) == 0) {
        scana = g_dev_addr;
    } else {
        pdu_type |= BLE_ADV_PDU_HDR_TXADD_RAND;
        scana = g_random_addr;
    }

#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1)
    if (scansm->own_addr_type > BLE_HCI_ADV_OWN_ADDR_RANDOM) {
        rl = NULL;
        if (ble_ll_is_rpa(adv_addr, adv_addr_type)) {
            if (scansm->scan_rpa_index >= 0) {
                /* Generate a RPA to use for scana */
                rl = &g_ble_ll_resolv_list[scansm->scan_rpa_index];
            }
        } else {
            if (ble_ll_resolv_enabled()) {
                rl = ble_ll_resolv_list_find(adv_addr, adv_addr_type);
            }
        }

        /*
         * If advertising device is on our resolving list, we use RPA generated
         * using Local IRK from resolving list entry as ScanA. In other case,
         * we use NRPA as ScanA as allowed by spec to prevent our device from
         * being tracked when doing an active scan (see Core 5.0, Vol 6, Part B,
         * section 6.3).
         */
        if (rl) {
            ble_ll_resolv_get_priv_addr(rl, 1, rpa);
            scana = rpa;
        } else {
            ble_ll_scan_refresh_nrpa(scansm);
            scana = scansm->scan_nrpa;
        }

        pdu_type |= BLE_ADV_PDU_HDR_TXADD_RAND;
    }
#endif

    ble_ll_mbuf_init(m, BLE_SCAN_REQ_LEN, pdu_type);

    /* Construct the scan request */
    dptr = m->om_data;
    memcpy(dptr, scana, BLE_DEV_ADDR_LEN);
    memcpy(dptr + BLE_DEV_ADDR_LEN, adv_addr, BLE_DEV_ADDR_LEN);
}

/**
 * Checks to see if an advertiser is on the duplicate address list.
 *
 * @param addr Pointer to address
 * @param txadd TxAdd bit. 0: public; random otherwise
 *
 * @return uint8_t 0: not on list; any other value is
 */
static struct ble_ll_scan_advertisers *
ble_ll_scan_find_dup_adv(uint8_t *addr, uint8_t txadd, uint8_t ext_adv, uint16_t adi)
{
    uint8_t num_advs;
    struct ble_ll_scan_advertisers *adv;

    /* Do we have an address match? Must match address type */
    adv = &g_ble_ll_scan_dup_advs[0];
    num_advs = g_ble_ll_scan_num_dup_advs;
    while (num_advs) {
        if (!memcmp(&adv->adv_addr, addr, BLE_DEV_ADDR_LEN)) {
            /* Address type must match */
            if (txadd) {
                if ((adv->sc_adv_flags & BLE_LL_SC_ADV_F_RANDOM_ADDR) == 0) {
                    goto next_dup_adv;
                }
            } else {
                if (adv->sc_adv_flags & BLE_LL_SC_ADV_F_RANDOM_ADDR) {
                    goto next_dup_adv;
                }
            }

            if (ext_adv && (adv->adi != adi)) {
                goto next_dup_adv;
            }

            return adv;
        }

next_dup_adv:
        ++adv;
        --num_advs;
    }

    return NULL;
}

#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
static struct ble_ll_ext_adv_report *
ble_ll_scan_init_ext_adv_report(struct ble_ll_ext_adv_report *copy_from)
{
    struct ble_ll_ext_adv_report *evt;

    evt = (struct ble_ll_ext_adv_report *) ble_hci_trans_buf_alloc(
                                                    BLE_HCI_TRANS_BUF_EVT_LO);
    if (!evt) {
        return NULL;
    }

    if (copy_from) {
        memcpy(evt, copy_from, sizeof(*evt));
    } else {
        memset(evt, 0, sizeof(*evt));

        evt->event_meta = BLE_HCI_EVCODE_LE_META;
        evt->subevt = BLE_HCI_LE_SUBEV_EXT_ADV_RPT;
        /* We support only one report per event now */
        evt->num_reports = 1;
        /* Init TX Power with "Not available" which is 127 */
        evt->tx_power = 127;
        /* Init RSSI with "Not available" which is 127 */
        evt->rssi = 127;
        /* Init SID with "Not available" which is 0xFF */
        evt->sid = 0xFF;
        /* Init address type with "anonymous" which is 0xFF */
        evt->addr_type = 0xFF;
    }

    return evt;
}

static void
ble_ll_scan_send_truncated_if_chained(struct ble_ll_aux_data *aux_data)
{
    struct ble_ll_ext_adv_report *evt;

    if (!ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_EXT_ADV_RPT)) {
        goto done;
    }

    BLE_LL_ASSERT(aux_data);

    if (!BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_AUX_CHAIN_BIT)) {
        /* if not chained, there is nothing to do here */
        goto done;
    }

    if (aux_data->evt) {
        evt = aux_data->evt;
        aux_data->evt = NULL;
    } else {
        evt = ble_ll_scan_init_ext_adv_report(NULL);
        if (!evt) {
            goto done;
        }
    }

    evt->event_len = sizeof(*evt) - BLE_HCI_EVENT_HDR_LEN;
    evt->evt_type = aux_data->evt_type;
    evt->evt_type |= (BLE_HCI_ADV_DATA_STATUS_TRUNCATED);
    BLE_LL_AUX_SET_FLAG(aux_data, BLE_LL_AUX_TRUNCATED_SENT);

    if (BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_AUX_HAS_ADDRA)) {
        memcpy(evt->addr, aux_data->addr, 6);
        evt->addr_type = aux_data->addr_type;
    }

    if (BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_AUX_HAS_DIR_ADDRA)) {
        memcpy(evt->dir_addr, aux_data->dir_addr, 6);
        evt->dir_addr_type = aux_data->dir_addr_type;
    }

    evt->sid = (aux_data->adi >> 12);
    ble_ll_hci_event_send((uint8_t *)evt);

done:
    ble_ll_scan_aux_data_unref(aux_data);
}

static int
ble_ll_scan_get_adi(struct ble_ll_aux_data *aux_data, uint16_t *adi)
{
    if (!aux_data) {
        return -1;
    }

    if (!BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_AUX_HAS_ADI)) {
        return -1;
    }

    *adi = aux_data->adi;

    return 0;
}
#endif

void
ble_ll_scan_end_adv_evt(struct ble_ll_aux_data *aux_data)
{
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
    /*
     * Check if tuncated has been sent
     *
     * Normally reference counter here should be 2. 1 for outstanding
     * complete event and one for ongoing scanning. If there is only 1
     * that means, advertising event is already completed
     * (truncated was sent to the host) and we just need to drop last reference.
     * Otherwise we should try to send truncated event to the host.
     */
    if (!BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_AUX_TRUNCATED_SENT)) {
        ble_ll_scan_send_truncated_if_chained(aux_data);
    }

    if (ble_ll_scan_aux_data_unref(aux_data) > 0) {
        BLE_LL_ASSERT(0);
    }

#endif
}
/**
 * Do scan machine clean up on PHY disabled
 *
 */
void
ble_ll_scan_clean_cur_aux_data(void)
{
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
    struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm;

    /* If scanner was reading aux ptr, we need to clean it up */
    if (scansm && scansm->cur_aux_data) {
        ble_ll_scan_end_adv_evt(scansm->cur_aux_data);
        scansm->cur_aux_data = NULL;
    }
#endif
}

/**
 * Check if a packet is a duplicate advertising packet.
 *
 * @param pdu_type
 * @param rxbuf
 *
 * @return int 0: not a duplicate. 1:duplicate
 */
int
ble_ll_scan_is_dup_adv(struct ble_mbuf_hdr *ble_hdr, uint8_t pdu_type,
                       uint8_t txadd, uint8_t *addr)
{
    struct ble_ll_scan_advertisers *adv;
    uint8_t ext_adv = 0;
    uint16_t adi = 0;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
    struct ble_ll_aux_data *aux_data = ble_hdr->rxinfo.user_data;

    if (pdu_type == BLE_ADV_PDU_TYPE_ADV_EXT_IND) {
        if (ble_ll_scan_get_adi(aux_data, &adi) < 0) {
            return 0;
        }

        ext_adv = 1;
    }

#endif

    adv = ble_ll_scan_find_dup_adv(addr, txadd, ext_adv, adi);
    if (adv) {
        /* Check appropriate flag (based on type of PDU) */
        if (pdu_type == BLE_ADV_PDU_TYPE_ADV_DIRECT_IND) {
            if (adv->sc_adv_flags & BLE_LL_SC_ADV_F_DIRECT_RPT_SENT) {
                return 1;
            }
        } else if (pdu_type == BLE_ADV_PDU_TYPE_SCAN_RSP) {
            if (adv->sc_adv_flags & BLE_LL_SC_ADV_F_SCAN_RSP_SENT) {
                return 1;
            }
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
        } else if (pdu_type == BLE_ADV_PDU_TYPE_ADV_EXT_IND) {
            if (BLE_MBUF_HDR_SCAN_RSP_RCV(ble_hdr) ||
                            (aux_data->evt_type & BLE_HCI_ADV_SCAN_RSP_MASK)) {
                if (adv->sc_adv_flags & BLE_LL_SC_ADV_F_SCAN_RSP_SENT) {
                    return 1;
                }
            } else {
                return 1;
            }
#endif
        } else {
            if (adv->sc_adv_flags & BLE_LL_SC_ADV_F_ADV_RPT_SENT) {
                return 1;
            }
        }
    }

    return 0;
}

/**
 * Add an advertiser the list of duplicate advertisers. An address gets added to
 * the list of duplicate addresses when the controller sends an advertising
 * report to the host.
 *
 * @param addr   Pointer to advertisers address or identity address
 * @param Txadd. TxAdd bit (0 public, random otherwise)
 * @param subev  Type of advertising report sent (direct or normal).
 * @param evtype Advertising event type
 * @param adi    Advertising Data Information
 */
void
ble_ll_scan_add_dup_adv(uint8_t *addr, uint8_t txadd, uint8_t subev,
                        uint8_t evtype, uint8_t ext_adv, uint16_t adi)
{
    uint8_t num_advs;
    struct ble_ll_scan_advertisers *adv;

    /* Check to see if on list. */
    adv = ble_ll_scan_find_dup_adv(addr, txadd, ext_adv, adi);
    if (!adv) {
        /* XXX: for now, if we dont have room, just leave */
        num_advs = g_ble_ll_scan_num_dup_advs;
        if (num_advs == MYNEWT_VAL(BLE_LL_NUM_SCAN_DUP_ADVS)) {
            return;
        }

        /* Add the advertiser to the array */
        adv = &g_ble_ll_scan_dup_advs[num_advs];
        memcpy(&adv->adv_addr, addr, BLE_DEV_ADDR_LEN);
        ++g_ble_ll_scan_num_dup_advs;

        adv->sc_adv_flags = 0;
        if (txadd) {
            adv->sc_adv_flags |= BLE_LL_SC_ADV_F_RANDOM_ADDR;
        }
        adv->adi = adi;
    }

    if (subev == BLE_HCI_LE_SUBEV_DIRECT_ADV_RPT) {
        adv->sc_adv_flags |= BLE_LL_SC_ADV_F_DIRECT_RPT_SENT;
#if MYNEWT_VAL(BLE_EXT_ADV)
    } else if (subev == BLE_HCI_LE_SUBEV_EXT_ADV_RPT) {
        if (evtype & BLE_HCI_ADV_SCAN_RSP_MASK) {
            adv->sc_adv_flags |= BLE_LL_SC_ADV_F_SCAN_RSP_SENT;
        } else {
            adv->sc_adv_flags |= BLE_LL_SC_ADV_F_ADV_RPT_SENT;
        }
#endif
    } else {
        if (evtype == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) {
            adv->sc_adv_flags |= BLE_LL_SC_ADV_F_SCAN_RSP_SENT;
        } else {
            adv->sc_adv_flags |= BLE_LL_SC_ADV_F_ADV_RPT_SENT;
        }
    }
}

/**
 * Checks to see if we have received a scan response from this advertiser.
 *
 * @param adv_addr Address of advertiser
 * @param txadd TxAdd bit (0: public; random otherwise)
 *
 * @return int 0: have not received a scan response; 1 otherwise.
 */
static int
ble_ll_scan_have_rxd_scan_rsp(uint8_t *addr, uint8_t txadd,
                              uint8_t ext_adv, uint16_t adi)
{
    uint8_t num_advs;
    struct ble_ll_scan_advertisers *adv;

    /* Do we have an address match? Must match address type */
    adv = &g_ble_ll_scan_rsp_advs[0];
    num_advs = g_ble_ll_scan_num_rsp_advs;
    while (num_advs) {
        if (!memcmp(&adv->adv_addr, addr, BLE_DEV_ADDR_LEN)) {
            /* Address type must match */
            if (txadd) {
                if (adv->sc_adv_flags & BLE_LL_SC_ADV_F_RANDOM_ADDR) {
                    if (ext_adv) {
                        if (adi == adv->adi) {
                            return 1;
                        }
                        goto next;
                    }
                    return 1;
                }
            } else {
                if ((adv->sc_adv_flags & BLE_LL_SC_ADV_F_RANDOM_ADDR) == 0) {
                    if (ext_adv) {
                        if (adi == adv->adi) {
                            return 1;
                        }
                        goto next;
                    }
                    return 1;
                }
            }
        }
next:
        ++adv;
        --num_advs;
    }

    return 0;
}

static void
ble_ll_scan_add_scan_rsp_adv(uint8_t *addr, uint8_t txadd,
                             uint8_t ext_adv, uint16_t adi)
{
    uint8_t num_advs;
    struct ble_ll_scan_advertisers *adv;

    /* XXX: for now, if we dont have room, just leave */
    num_advs = g_ble_ll_scan_num_rsp_advs;
    if (num_advs == MYNEWT_VAL(BLE_LL_NUM_SCAN_RSP_ADVS)) {
        return;
    }

    /* Check if address is already on the list */
    if (ble_ll_scan_have_rxd_scan_rsp(addr, txadd, ext_adv, adi)) {
        return;
    }

    /* Add the advertiser to the array */
    adv = &g_ble_ll_scan_rsp_advs[num_advs];
    memcpy(&adv->adv_addr, addr, BLE_DEV_ADDR_LEN);
    adv->sc_adv_flags = BLE_LL_SC_ADV_F_SCAN_RSP_RXD;
    if (txadd) {
        adv->sc_adv_flags |= BLE_LL_SC_ADV_F_RANDOM_ADDR;
    }
    adv->adi = adi;
    ++g_ble_ll_scan_num_rsp_advs;

    return;
}

#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
static int
ble_ll_hci_send_legacy_ext_adv_report(uint8_t evtype,
                                      uint8_t addr_type, uint8_t *addr,
                                      uint8_t rssi,
                                      uint8_t adv_data_len, struct os_mbuf *adv_data,
                                      uint8_t *inita, uint8_t inita_type)
{
    struct ble_ll_ext_adv_report *evt;

    if (!ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_EXT_ADV_RPT)) {
        return -1;
    }

    /* Drop packet if it does not fit into event buffer */
    if ((sizeof(*evt) + adv_data_len) + 1 > MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE)) {
        STATS_INC(ble_ll_stats, adv_evt_dropped);
        return -1;
    }

    evt = ble_ll_scan_init_ext_adv_report(NULL);
    if (!evt) {
        return 0;
    }

    switch (evtype) {
    case BLE_HCI_ADV_RPT_EVTYPE_ADV_IND:
        evt->evt_type = BLE_HCI_LEGACY_ADV_EVTYPE_ADV_IND;
        break;
    case BLE_HCI_ADV_RPT_EVTYPE_DIR_IND:
        evt->evt_type = BLE_HCI_LEGACY_ADV_EVTYPE_ADV_DIRECT_IND;
        break;
    case BLE_HCI_ADV_RPT_EVTYPE_NONCONN_IND:
        evt->evt_type = BLE_HCI_LEGACY_ADV_EVTYPE_ADV_NONCON_IND;
        break;
    case BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP:
        evt->evt_type = BLE_HCI_LEGACY_ADV_EVTYPE_SCAN_RSP_ADV_IND;
         break;
    case BLE_HCI_ADV_RPT_EVTYPE_SCAN_IND:
        evt->evt_type = BLE_HCI_LEGACY_ADV_EVTYPE_ADV_SCAN_IND;
        break;
    default:
        BLE_LL_ASSERT(0);
        break;
    }

    evt->addr_type = addr_type;
    memcpy(evt->addr, addr, BLE_DEV_ADDR_LEN);

    evt->event_len = sizeof(*evt) - BLE_HCI_EVENT_HDR_LEN;

    if (inita) {
        evt->dir_addr_type = inita_type;
        memcpy(evt->dir_addr, inita, BLE_DEV_ADDR_LEN);
    } else if (adv_data_len <= (MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE) - sizeof(*evt))) {
        evt->adv_data_len = adv_data_len;
        os_mbuf_copydata(adv_data, 0, adv_data_len, evt->adv_data);
        evt->event_len += adv_data_len;
    }

    evt->rssi = rssi;
    evt->prim_phy = BLE_PHY_1M;

    return ble_ll_hci_event_send((uint8_t *) evt);
}
#endif

static int
ble_ll_hci_send_adv_report(uint8_t subev, uint8_t evtype,uint8_t event_len,
                           uint8_t addr_type, uint8_t *addr, uint8_t rssi,
                           uint8_t adv_data_len, struct os_mbuf *adv_data,
                           uint8_t *inita, uint8_t inita_type)
{
    uint8_t *evbuf;
    uint8_t *tmp;

    if (!ble_ll_hci_is_le_event_enabled(subev)) {
        return -1;
    }

    /* Drop packet if it does not fit into event buffer */
    if (event_len + 1 > MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE)) {
        STATS_INC(ble_ll_stats, adv_evt_dropped);
        return -1;
    }

    evbuf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_LO);
    if (!evbuf) {
        return -1;
    }

    evbuf[0] = BLE_HCI_EVCODE_LE_META;
    evbuf[1] = event_len;
    evbuf[2] = subev;
    evbuf[3] = 1;       /* number of reports */
    evbuf[4] = evtype;
    evbuf[5] = addr_type;
    memcpy(&evbuf[6], addr, BLE_DEV_ADDR_LEN);

    tmp = &evbuf[12];
    if (subev == BLE_HCI_LE_SUBEV_DIRECT_ADV_RPT) {
        BLE_LL_ASSERT(inita);
        tmp[0] = inita_type;
        memcpy(tmp + 1, inita, BLE_DEV_ADDR_LEN);
        tmp += BLE_DEV_ADDR_LEN + 1;
    } else if (subev == BLE_HCI_LE_SUBEV_ADV_RPT) {
        tmp[0] = adv_data_len;
        os_mbuf_copydata(adv_data, 0, adv_data_len, tmp + 1);
        tmp += adv_data_len + 1;
    } else {
        BLE_LL_ASSERT(0);
        return -1;
    }

    tmp[0] = rssi;

    return ble_ll_hci_event_send(evbuf);
}
/**
 * Send an advertising report to the host.
 *
 * NOTE: while we are allowed to send multiple devices in one report, we
 * will just send for one for now.
 *
 * @param pdu_type
 * @param txadd
 * @param rxbuf
 * @param hdr
 * @param scansm
 */
static void
ble_ll_scan_send_adv_report(uint8_t pdu_type, uint8_t *adva, uint8_t adva_type,
                            uint8_t *inita, uint8_t inita_type,
                            struct os_mbuf *om,
                            struct ble_mbuf_hdr *hdr,
                            struct ble_ll_scan_sm *scansm)
{
    int rc;
    uint8_t *rxbuf = om->om_data;
    uint8_t evtype;
    uint8_t subev;
    uint8_t adv_data_len;
    uint8_t event_len;

    if (pdu_type == BLE_ADV_PDU_TYPE_ADV_DIRECT_IND) {
        if (ble_ll_is_rpa(inita, inita_type)) {
            /* For resolvable we send separate event */
            subev = BLE_HCI_LE_SUBEV_DIRECT_ADV_RPT;
            event_len = BLE_HCI_LE_ADV_DIRECT_RPT_LEN;
        } else {
            subev = BLE_HCI_LE_SUBEV_ADV_RPT;
            event_len = BLE_HCI_LE_ADV_RPT_MIN_LEN;
        }
        evtype = BLE_HCI_ADV_RPT_EVTYPE_DIR_IND;

        adv_data_len = 0;
    } else {
        subev = BLE_HCI_LE_SUBEV_ADV_RPT;
        if (pdu_type == BLE_ADV_PDU_TYPE_ADV_IND) {
            evtype = BLE_HCI_ADV_RPT_EVTYPE_ADV_IND;
        } else if (pdu_type == BLE_ADV_PDU_TYPE_ADV_SCAN_IND) {
            evtype = BLE_HCI_ADV_RPT_EVTYPE_SCAN_IND;
        } else if (pdu_type == BLE_ADV_PDU_TYPE_ADV_NONCONN_IND) {
            evtype = BLE_HCI_ADV_RPT_EVTYPE_NONCONN_IND;
        } else {
            evtype = BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP;
        }
        adv_data_len = rxbuf[1];
        adv_data_len -= BLE_DEV_ADDR_LEN;
        event_len = BLE_HCI_LE_ADV_RPT_MIN_LEN + adv_data_len;
        os_mbuf_adj(om, BLE_LL_PDU_HDR_LEN + BLE_DEV_ADDR_LEN);
    }

#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
    /* If RPA has been used, make sure we use correct address types
     * in the advertising report.
     */
    if (BLE_MBUF_HDR_RESOLVED(hdr)) {
        adva_type += 2;
    }
    if (BLE_MBUF_HDR_INITA_RESOLVED(hdr)) {
        inita_type += 2;
    }
#endif

#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
    if (scansm->ext_scanning) {
        rc = ble_ll_hci_send_legacy_ext_adv_report(evtype,
                                                   adva_type, adva,
                                                   hdr->rxinfo.rssi,
                                                   adv_data_len, om,
                                                   inita, inita_type);
    } else {
        rc = ble_ll_hci_send_adv_report(subev, evtype, event_len,
                                        adva_type, adva,
                                        hdr->rxinfo.rssi,
                                        adv_data_len, om,
                                        inita, inita_type);
    }
#else
    rc = ble_ll_hci_send_adv_report(subev, evtype, event_len,
                                    adva_type, adva,
                                    hdr->rxinfo.rssi,
                                    adv_data_len, om,
                                    inita, inita_type);
#endif
    if (!rc) {
        /* If filtering, add it to list of duplicate addresses */
        if (scansm->scan_filt_dups) {
            ble_ll_scan_add_dup_adv(adva, adva_type, subev, evtype, 0, 0);
        }
    }
}

/**
 * Checks the scanner filter policy to determine if we should allow or discard
 * the received PDU.
 *
 * NOTE: connect requests and scan requests are not passed here
 *
 * @param pdu_type
 * @param adv_addr
 * @param adv_addr_type
 * @param init_addr
 * @param init_addr_type
 * @param flags
 *
 * @return int 0: pdu allowed by filter policy. 1: pdu not allowed
 */
static int
ble_ll_scan_chk_filter_policy(uint8_t pdu_type, uint8_t *adv_addr,
                              uint8_t adv_addr_type, uint8_t *init_addr,
                              uint8_t init_addr_type, uint8_t devmatch)
{
    int use_whitelist;
    int chk_inita;
    struct ble_ll_scan_params *params =
                        &g_ble_ll_scan_sm.phy_data[g_ble_ll_scan_sm.cur_phy];

    use_whitelist = 0;
    chk_inita = 0;

#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
    if (pdu_type == BLE_ADV_PDU_TYPE_ADV_EXT_IND && adv_addr == NULL) {
        /* Note: adv_addr can be NULL (but don't have to) for ext adv. If NULL
         * that means it is beacon and skip filter policy for now */
        return 0;
    }
#endif

    switch (params->scan_filt_policy) {
    case BLE_HCI_SCAN_FILT_NO_WL:
        break;
    case BLE_HCI_SCAN_FILT_USE_WL:
        use_whitelist = 1;
        break;
    case BLE_HCI_SCAN_FILT_NO_WL_INITA:
        chk_inita = 1;
        break;
    case BLE_HCI_SCAN_FILT_USE_WL_INITA:
        chk_inita = 1;
        use_whitelist = 1;
        break;
    default:
        BLE_LL_ASSERT(0);
        break;
    }

    /* If we are using the whitelist, check that first */
    if (use_whitelist && (pdu_type != BLE_ADV_PDU_TYPE_SCAN_RSP)) {
        /* If device does not match let us skip this PDU.
         * If device matches, lets check for InitA further in the code
         */
        if (!devmatch) {
            return 1;
        }
    }

    /* If this is a directed advertisement, init_addr is not NULL.
     * Check that it is for us */
    if (init_addr) {
        /* Is this for us? If not, is it resolvable */
        if (!ble_ll_is_our_devaddr(init_addr, init_addr_type)) {
            if (!chk_inita || !ble_ll_is_rpa(adv_addr, adv_addr_type)) {
                return 1;
            }
        }
    }

    return 0;
}

static void
ble_ll_get_chan_to_scan(struct ble_ll_scan_sm *scansm, uint8_t *chan,
                        int *phy)
{
    struct ble_ll_scan_params *scanphy = &scansm->phy_data[scansm->cur_phy];
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
    struct ble_ll_aux_data *aux_data = scansm->cur_aux_data;

    if (!scansm->ext_scanning || !aux_data || !aux_data->scanning) {
        *chan = scanphy->scan_chan;
        *phy = scanphy->phy;
        return;
    }

    *chan = aux_data->chan;
    *phy = aux_data->aux_phy;
#else
    *chan = scanphy->scan_chan;
    *phy = scanphy->phy;
#endif
}
/**
 * Called to enable the receiver for scanning.
 *
 * Context: Link Layer task
 *
 * @param sch
 *
 * @return int
 */
static int
ble_ll_scan_start(struct ble_ll_scan_sm *scansm, struct ble_ll_sched_item *sch)
{
    int rc;
    struct ble_ll_scan_params *scanphy = &scansm->phy_data[scansm->cur_phy];
    uint8_t scan_chan;
#if (BLE_LL_BT5_PHY_SUPPORTED == 1)
    uint8_t phy_mode;
#endif
    int phy;

    ble_ll_get_chan_to_scan(scansm, &scan_chan, &phy);

    /* XXX: right now scheduled item is only present if we schedule for aux
     *      scan just make sanity check that we have proper combination of
     *      sch and resulting scan_chan
     */
    BLE_LL_ASSERT(!sch || scan_chan < BLE_PHY_ADV_CHAN_START);
    BLE_LL_ASSERT(sch || scan_chan >= BLE_PHY_ADV_CHAN_START);

    /* Set channel */
    rc = ble_phy_setchan(scan_chan, BLE_ACCESS_ADDR_ADV, BLE_LL_CRCINIT_ADV);
    BLE_LL_ASSERT(rc == 0);

    /*
     * Set transmit end callback to NULL in case we transmit a scan request.
     * There is a callback for the connect request.
     */
    ble_phy_set_txend_cb(NULL, NULL);

#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) == 1)
    ble_phy_encrypt_disable();
#endif

#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1)
    if (ble_ll_resolv_enabled()) {
        ble_phy_resolv_list_enable();
    } else {
        ble_phy_resolv_list_disable();
    }
#endif

#if (BLE_LL_BT5_PHY_SUPPORTED == 1)
    phy_mode = ble_ll_phy_to_phy_mode(phy, BLE_HCI_LE_PHY_CODED_ANY);
    ble_phy_mode_set(phy_mode, phy_mode);
#endif

    /* XXX: probably need to make sure hfxo is running too */
    /* XXX: can make this better; want to just start asap. */
    if (sch) {
        rc = ble_phy_rx_set_start_time(sch->start_time +
                                       g_ble_ll_sched_offset_ticks,
                                       sch->remainder);
    } else {
        rc = ble_phy_rx_set_start_time(os_cputime_get32() +
                                       g_ble_ll_sched_offset_ticks, 0);
    }
    if (!rc || rc == BLE_PHY_ERR_RX_LATE) {
        /* If we are late here, it is still OK because we keep scanning.
         * Clear error
         */
        rc = 0;

        /* Enable/disable whitelisting */
        if (scanphy->scan_filt_policy & 1) {
            ble_ll_whitelist_enable();
        } else {
            ble_ll_whitelist_disable();
        }

        /* Set link layer state to scanning */
        if (scanphy->scan_type == BLE_SCAN_TYPE_INITIATE) {
            ble_ll_state_set(BLE_LL_STATE_INITIATING);
        } else {
            ble_ll_state_set(BLE_LL_STATE_SCANNING);
        }
    }

    /* If there is a still a scan response pending, we have failed! */
    if (scansm->scan_rsp_pending) {
        ble_ll_scan_req_backoff(scansm, 0);
    }

    return rc;
}

#ifdef BLE_XCVR_RFCLK
static void
ble_ll_scan_rfclk_chk_stop(void)
{
    int stop;
    int32_t time_till_next;
    os_sr_t sr;
    uint32_t next_time;

    stop = 0;
    OS_ENTER_CRITICAL(sr);
    if (ble_ll_sched_next_time(&next_time)) {
        /*
         * If the time until the next event is too close, dont bother to turn
         * off the clock
         */
        time_till_next = (int32_t)(next_time - os_cputime_get32());
        if (time_till_next > g_ble_ll_data.ll_xtal_ticks) {
            stop = 1;
        }
    } else {
        stop = 1;
    }
    if (stop) {
        ble_ll_xcvr_rfclk_disable();
    }
    OS_EXIT_CRITICAL(sr);
}
#endif

static uint8_t
ble_ll_scan_get_next_adv_prim_chan(uint8_t chan)
{
    ++chan;
    if (chan == BLE_PHY_NUM_CHANS) {
        chan = BLE_PHY_ADV_CHAN_START;
    }

    return chan;
}

static uint32_t
ble_ll_scan_get_current_scan_win(struct ble_ll_scan_sm *scansm, uint32_t cputime)
{
    uint32_t itvl;
    struct ble_ll_scan_params *scanphy = &scansm->phy_data[scansm->cur_phy];

    /* Well, in case we missed to schedule scan, lets move to next stan interval.
     */
    itvl = os_cputime_usecs_to_ticks(scanphy->scan_itvl * BLE_HCI_SCAN_ITVL);
    while ((int32_t)(cputime - scanphy->scan_win_start_time) >= itvl) {
        scanphy->scan_win_start_time += itvl;

        /* If we missed scan window, make sure we update scan channel */
        scanphy->scan_chan =
                ble_ll_scan_get_next_adv_prim_chan(scanphy->scan_chan);
    }

    return scanphy->scan_win_start_time;
}
/**
 * Called to determine if we are inside or outside the scan window. If we
 * are inside the scan window it means that the device should be receiving
 * on the scan channel.
 *
 * Context: Link Layer
 *
 * @param scansm
 *
 * @return int 0: inside scan window 1: outside scan window
 */
static int
ble_ll_scan_window_chk(struct ble_ll_scan_sm *scansm, uint32_t cputime)
{
    uint32_t win;
    uint32_t dt;
    uint32_t win_start;
    struct ble_ll_scan_params *scanphy = &scansm->phy_data[scansm->cur_phy];

    win_start = ble_ll_scan_get_current_scan_win(scansm, cputime);

    if (scanphy->scan_window != scanphy->scan_itvl) {
        win = os_cputime_usecs_to_ticks(scanphy->scan_window * BLE_HCI_SCAN_ITVL);
        dt = cputime - win_start;
        if (dt >= win) {
#ifdef BLE_XCVR_RFCLK
            if (dt < (scanphy->scan_itvl - g_ble_ll_data.ll_xtal_ticks)) {
                ble_ll_scan_rfclk_chk_stop();
            }
#endif
            return 1;
        }
    }

    return 0;
}
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
static void
ble_ll_scan_aux_data_free(struct ble_ll_aux_data *aux_scan)
{
    if (aux_scan) {
        if (aux_scan->evt) {
            ble_hci_trans_buf_free((uint8_t *)aux_scan->evt);
            aux_scan->evt = NULL;
        }
        os_memblock_put(&ext_adv_pool, aux_scan);
        STATS_INC(ble_ll_stats, aux_freed);
    }
}

void
ble_ll_scan_aux_data_ref(struct ble_ll_aux_data *aux_data)
{
    os_sr_t sr;

    if (!aux_data) {
        return;
    }

    OS_ENTER_CRITICAL(sr);
    aux_data->ref_cnt++;
    ble_ll_trace_u32x2(BLE_LL_TRACE_ID_AUX_REF, (uint32_t) aux_data, aux_data->ref_cnt);

    OS_EXIT_CRITICAL(sr);
}

int
ble_ll_scan_aux_data_unref(struct ble_ll_aux_data *aux_data)
{
    os_sr_t sr;
    int ret;

    if (!aux_data) {
        return 0;
    }

    OS_ENTER_CRITICAL(sr);
    aux_data->ref_cnt--;
    ret = aux_data->ref_cnt;
    ble_ll_trace_u32x2(BLE_LL_TRACE_ID_AUX_UNREF, (uint32_t) aux_data, aux_data->ref_cnt);

    if (aux_data->ref_cnt == 0) {
        ble_ll_scan_aux_data_free(aux_data);
    }

    OS_EXIT_CRITICAL(sr);

    return ret;
}

static void
ble_ll_scan_sched_remove(struct ble_ll_sched_item *sch)
{
    ble_ll_scan_aux_data_free(sch->cb_arg);
}
#endif
/**
 * Stop the scanning state machine
 */
void
ble_ll_scan_sm_stop(int chk_disable)
{
    os_sr_t sr;
    uint8_t lls;
    struct ble_ll_scan_sm *scansm;

    /* Stop the scanning timer  */
    scansm = &g_ble_ll_scan_sm;
    os_cputime_timer_stop(&scansm->scan_timer);

    /* Disable scanning state machine */
    scansm->scan_enabled = 0;
    scansm->restart_timer_needed = 0;

#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
    if (scansm->ext_scanning) {
        OS_ENTER_CRITICAL(sr);
        ble_ll_scan_clean_cur_aux_data();
        OS_EXIT_CRITICAL(sr);

        ble_ll_sched_rmv_elem_type(BLE_LL_SCHED_TYPE_AUX_SCAN, ble_ll_scan_sched_remove);
        scansm->ext_scanning = 0;
    }
#endif

    /* Count # of times stopped */
    STATS_INC(ble_ll_stats, scan_stops);

    /* Only set state if we are currently in a scan window */
    if (chk_disable) {
        OS_ENTER_CRITICAL(sr);
        lls = ble_ll_state_get();

        if ((lls == BLE_LL_STATE_SCANNING) ||
                        (lls == BLE_LL_STATE_INITIATING && chk_disable == 1)) {
            /* Disable phy */
            ble_phy_disable();

            /* Set LL state to standby */
            ble_ll_state_set(BLE_LL_STATE_STANDBY);

            /* May need to stop the rfclk */
#ifdef BLE_XCVR_RFCLK
            ble_ll_scan_rfclk_chk_stop();
#endif
        }
        OS_EXIT_CRITICAL(sr);
    }
}

static int
ble_ll_scan_sm_start(struct ble_ll_scan_sm *scansm)
{
    /*
     * This is not in the specification. I will reject the command with a
     * command disallowed error if no random address has been sent by the
     * host. All the parameter errors refer to the command parameter
     * (which in this case is just enable or disable) so that is why I chose
     * command disallowed.
     */
    if (scansm->own_addr_type == BLE_HCI_ADV_OWN_ADDR_RANDOM) {
        if (!ble_ll_is_valid_random_addr(g_random_addr)) {
            return BLE_ERR_CMD_DISALLOWED;
        }
    }

    /* Count # of times started */
    STATS_INC(ble_ll_stats, scan_starts);

    /* Set flag telling us that scanning is enabled */
    scansm->scan_enabled = 1;

    /* Set first advertising channel */
    BLE_LL_ASSERT(scansm->cur_phy != PHY_NOT_CONFIGURED);
    scansm->phy_data[scansm->cur_phy].scan_chan = BLE_PHY_ADV_CHAN_START;

    if (scansm->next_phy != PHY_NOT_CONFIGURED &&
            scansm->next_phy != scansm->cur_phy) {
        scansm->phy_data[scansm->next_phy].scan_chan = BLE_PHY_ADV_CHAN_START;
    }

    /* Reset scan request backoff parameters to default */
    scansm->upper_limit = 1;
    scansm->backoff_count = 1;
    scansm->scan_rsp_pending = 0;

    /* Forget filtered advertisers from previous scan. */
    g_ble_ll_scan_num_rsp_advs = 0;
    g_ble_ll_scan_num_dup_advs = 0;

    /* XXX: align to current or next slot???. */
    /* Schedule start time now */
    scansm->phy_data[scansm->cur_phy].scan_win_start_time = os_cputime_get32();

    /* Post scanning event to start off the scanning process */
    ble_ll_event_send(&scansm->scan_sched_ev);

    return BLE_ERR_SUCCESS;
}

#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
static void
ble_ll_scan_switch_phy(struct ble_ll_scan_sm *scansm)
{
    uint8_t tmp;

    if (scansm->next_phy == PHY_NOT_CONFIGURED) {
        return;
    }

    tmp = scansm->next_phy;
    scansm->next_phy = scansm->cur_phy;
    scansm->cur_phy = tmp;

    /* PHY is changing in ble_ll_scan_start() */
}

/**
 * Called to change PHY if needed and set new event time
 *
 * Context: Link Layer task.
 *
 * @param arg
 */
static bool
ble_ll_scan_start_next_phy(struct ble_ll_scan_sm *scansm,
                                 uint32_t *next_event_time)
{
    struct ble_ll_scan_params *cur_phy;
    struct ble_ll_scan_params *next_phy;
    uint32_t now;
    uint32_t win;

    /* Lets check if we want to switch to other PHY */
    if (scansm->cur_phy == scansm->next_phy ||
            scansm->next_phy == PHY_NOT_CONFIGURED) {
        return false;
    }

    cur_phy = &scansm->phy_data[scansm->cur_phy];
    next_phy = &scansm->phy_data[scansm->next_phy];

    /* Store next event for current phy */
    cur_phy->next_event_start = *next_event_time;

    /* Other PHY already wanted to scan. Allow it */
    ble_ll_scan_switch_phy(scansm);

    now = os_cputime_get32();

    /* Start with new channel only if PHY was scanning already */
    if (next_phy->next_event_start != 0) {
        next_phy->scan_chan =
                ble_ll_scan_get_next_adv_prim_chan(next_phy->scan_chan);
    }
    next_phy->scan_win_start_time = now;
    win = os_cputime_usecs_to_ticks(next_phy->scan_window *
                                    BLE_HCI_SCAN_ITVL);

    next_phy->next_event_start = now + win;

    *next_event_time = next_phy->next_event_start;
    return true;
}

static void
ble_ll_aux_scan_rsp_failed(void)
{
    STATS_INC(ble_ll_stats, aux_scan_rsp_err);
    ble_ll_scan_clean_cur_aux_data();
}
#endif

/**
 * Called to process the scanning OS event which was posted to the LL task
 *
 * Context: Link Layer task.
 *
 * @param arg
 */
static void
ble_ll_scan_event_proc(struct ble_npl_event *ev)
{
    os_sr_t sr;
    int inside_window;
    int start_scan;
    uint32_t now;
    uint32_t dt;
    uint32_t win;
    uint32_t win_start;
    uint32_t scan_itvl;
    uint32_t next_event_time;
#ifdef BLE_XCVR_RFCLK
    uint32_t xtal_ticks;
    int xtal_state;
#endif
    struct ble_ll_scan_sm *scansm;
    struct ble_ll_scan_params *scanphy;

    /*
     * Get the scanning state machine. If not enabled (this is possible), just
     * leave and do nothing (just make sure timer is stopped).
     */
    scansm = (struct ble_ll_scan_sm *)ble_npl_event_get_arg(ev);
    scanphy = &scansm->phy_data[scansm->cur_phy];

    OS_ENTER_CRITICAL(sr);
    if (!scansm->scan_enabled) {
        os_cputime_timer_stop(&scansm->scan_timer);
        OS_EXIT_CRITICAL(sr);
        return;
    }

    if (scansm->cur_aux_data) {
        /* Aux scan in progress. Wait */
        STATS_INC(ble_ll_stats, scan_timer_stopped);
        scansm->restart_timer_needed = 1;
        OS_EXIT_CRITICAL(sr);
        return;
    }

    /* Make sure the scan window start time and channel are up to date. */
    now = os_cputime_get32();
    win_start = ble_ll_scan_get_current_scan_win(scansm, now);

    /* Check if we are in scan window */
    dt = now - win_start;

    if (scanphy->scan_window != scanphy->scan_itvl) {
        win = os_cputime_usecs_to_ticks(scanphy->scan_window * BLE_HCI_SCAN_ITVL);
        inside_window = dt < win ? 1 : 0;
    } else {
        win = 0;
        /* In case continous scan lets assume we area always in the window*/
        inside_window = 1;
    }

    /* Determine on/off state based on scan window */
    scan_itvl = os_cputime_usecs_to_ticks(scanphy->scan_itvl *
                                                    BLE_HCI_SCAN_ITVL);

    if (win != 0 && inside_window) {
        next_event_time = win_start + win;
    } else {
        next_event_time = win_start + scan_itvl;
    }

#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
    if (inside_window == 0  &&
                        ble_ll_scan_start_next_phy(scansm, &next_event_time)) {
        /* Check if we should start next phy. If so let's say we are inside
         * the window
         */
        inside_window = 1;
    }
#endif

    /*
     * If we are not in the standby state it means that the scheduled
     * scanning event was overlapped in the schedule. In this case all we do
     * is post the scan schedule end event.
     */
    start_scan = 1;
    switch (ble_ll_state_get()) {
    case BLE_LL_STATE_ADV:
    case BLE_LL_STATE_CONNECTION:
         start_scan = 0;
         break;
    case BLE_LL_STATE_INITIATING:
        /* Must disable PHY since we will move to a new channel */
        ble_phy_disable();
        if (!inside_window) {
            ble_ll_state_set(BLE_LL_STATE_STANDBY);
        }
        /* PHY is disabled - make sure we do not wait for AUX_CONNECT_RSP */
        ble_ll_conn_reset_pending_aux_conn_rsp();
        break;
    case BLE_LL_STATE_SCANNING:
        /* Must disable PHY since we will move to a new channel */
        ble_phy_disable();
        if (!inside_window) {
            ble_ll_state_set(BLE_LL_STATE_STANDBY);
        }
        break;
    case BLE_LL_STATE_STANDBY:
        break;
    default:
        BLE_LL_ASSERT(0);
        break;
    }

    if (start_scan && inside_window) {
#ifdef BLE_XCVR_RFCLK
            xtal_state = ble_ll_xcvr_rfclk_state();
        if (xtal_state != BLE_RFCLK_STATE_SETTLED) {
            if (xtal_state == BLE_RFCLK_STATE_OFF) {
                xtal_ticks = g_ble_ll_data.ll_xtal_ticks;
            } else {
                xtal_ticks = ble_ll_xcvr_rfclk_time_till_settled();
            }

            /*
             * Only bother if we have enough time to receive anything
             * here. The next event time will turn off the clock.
             */
            if (win != 0) {
                if ((win - dt) <= xtal_ticks)  {
                    goto done;
                }
            }

            /*
             * If clock off, start clock. Set next event time to now plus
             * the clock setting time.
             */
            if (xtal_state == BLE_RFCLK_STATE_OFF) {
                ble_ll_xcvr_rfclk_start_now(now);
            }
            next_event_time = now + xtal_ticks;
            goto done;
        }
#endif
        ble_ll_scan_start(scansm, NULL);
        goto done;
    }

#ifdef BLE_XCVR_RFCLK
    if (inside_window == 0) {
        /*
         * We need to wake up before we need to start scanning in order
         * to make sure the rfclock is on. If we are close to being on,
         * enable the rfclock. If not, set wakeup time.
         */
        if (dt >= (scan_itvl - g_ble_ll_data.ll_xtal_ticks)) {
            /* Start the clock if necessary */
            if (start_scan) {
                if (ble_ll_xcvr_rfclk_state() == BLE_RFCLK_STATE_OFF) {
                    ble_ll_xcvr_rfclk_start_now(now);
                    next_event_time = now + g_ble_ll_data.ll_xtal_ticks;
                }
            }
        } else {
            next_event_time -= g_ble_ll_data.ll_xtal_ticks;
            if (start_scan) {
                ble_ll_scan_rfclk_chk_stop();
            }
        }
    }
#endif

done:
    OS_EXIT_CRITICAL(sr);
    os_cputime_timer_start(&scansm->scan_timer, next_event_time);
}

/**
 * ble ll scan rx pdu start
 *
 * Called when a PDU reception has started and the Link Layer is in the
 * scanning state.
 *
 * Context: Interrupt
 *
 * @param pdu_type
 * @param rxflags
 *
 * @return int
 *  0: we will not attempt to reply to this frame
 *  1: we may send a response to this frame.
 */
int
ble_ll_scan_rx_isr_start(uint8_t pdu_type, uint16_t *rxflags)
{
    int rc;
    struct ble_ll_scan_sm *scansm;
    struct ble_ll_scan_params *scanphy;

    rc = 0;
    scansm = &g_ble_ll_scan_sm;
    scanphy = &scansm->phy_data[scansm->cur_phy];

    switch (scanphy->scan_type) {
    case BLE_SCAN_TYPE_ACTIVE:
        /* If adv ind or scan ind, we may send scan request */
        if ((pdu_type == BLE_ADV_PDU_TYPE_ADV_IND) ||
            (pdu_type == BLE_ADV_PDU_TYPE_ADV_SCAN_IND)) {
            rc = 1;
        }

#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
        if ((pdu_type == BLE_ADV_PDU_TYPE_ADV_EXT_IND && scansm->ext_scanning)) {
            *rxflags |= BLE_MBUF_HDR_F_EXT_ADV;
            rc = 1;
        }
#endif

        /*
         * If this is the first PDU after we sent the scan response (as
         * denoted by the scan rsp pending flag), we set a bit in the ble
         * header so the link layer can check to see if the scan request
         * was successful. We do it this way to let the Link Layer do the
         * work for successful scan requests. If failed, we do the work here.
         */
        if (scansm->scan_rsp_pending) {
            if (pdu_type == BLE_ADV_PDU_TYPE_SCAN_RSP) {
                *rxflags |= BLE_MBUF_HDR_F_SCAN_RSP_CHK;
            } else if (pdu_type == BLE_ADV_PDU_TYPE_AUX_SCAN_RSP) {
                *rxflags |= BLE_MBUF_HDR_F_SCAN_RSP_CHK;
            } else {
                ble_ll_scan_req_backoff(scansm, 0);
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
                ble_ll_aux_scan_rsp_failed();
#endif
            }
        }

        if (scansm->cur_aux_data && !scansm->scan_rsp_pending ) {
            STATS_INC(ble_ll_stats, aux_received);
        }

        /* Disable wfr if running */
        ble_ll_wfr_disable();
        break;
    case BLE_SCAN_TYPE_PASSIVE:
    default:
        break;
    }

    return rc;
}

#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
static uint8_t
ble_ll_ext_adv_phy_mode_to_local_phy(uint8_t adv_phy_mode)
{
    switch (adv_phy_mode) {
    case 0x00:
        return BLE_PHY_1M;
    case 0x01:
        return BLE_PHY_2M;
    case 0x02:
        return BLE_PHY_CODED;
    }

    return 0;
}

static int
ble_ll_ext_scan_parse_aux_ptr(struct ble_ll_aux_data *aux_data, uint8_t *buf)
{
    uint32_t aux_ptr_field = get_le32(buf) & 0x00FFFFFF;

    aux_data->chan = (aux_ptr_field) & 0x3F;
    if (aux_data->chan >= BLE_PHY_NUM_DATA_CHANS) {
        return -1;
    }

    /* TODO use CA aux_ptr_field >> 6 */

    aux_data->offset = 30 * ((aux_ptr_field >> 8) & 0x1FFF);

    if ((aux_ptr_field >> 7) & 0x01) {
            aux_data->offset *= 10;
            aux_data->offset_units = 1;
    }

    if (aux_data->offset < BLE_LL_MAFS) {
        return -1;
    }

    aux_data->aux_phy =
            ble_ll_ext_adv_phy_mode_to_local_phy((aux_ptr_field >> 21) & 0x07);
    if (aux_data->aux_phy == 0) {
        return -1;
    }

    return 0;
}

static void
ble_ll_ext_scan_parse_adv_info(struct ble_ll_scan_sm *scansm,
                               struct ble_ll_ext_adv_report *evt, uint8_t *buf)
{
    uint16_t adv_info = get_le16(buf);

    /* TODO Use DID */

    evt->sid = (adv_info >> 12);
}

/**
 * ble_ll_scan_get_aux_data
 *
 * Get aux data pointer. It is new allocated data for beacon or currently
 * processing aux data pointer. Aux data pointer will be attached to ble_hdr.rxinfo.user_data
 *
 * Context: Interrupt
 *
 * @param scansm
 * @param ble_hdr
 * @param rxbuf
 * @param aux_data
 *
 * @return int
 *  0: new allocated aux data
 *  1: current processing aux data
 * -1: error
 */
int
ble_ll_scan_get_aux_data(struct ble_mbuf_hdr *ble_hdr, uint8_t *rxbuf)
{
    uint8_t ext_hdr_len;
    uint8_t pdu_len;
    uint8_t ext_hdr_flags;
    uint8_t *ext_hdr;
    uint8_t has_addr = 0;
    uint8_t has_dir_addr = 0;
    uint8_t has_adi = 0;
    int i;
    struct ble_ll_aux_data *current_aux = ble_hdr->rxinfo.user_data;
    struct ble_ll_aux_data *new_aux = NULL;
    struct ble_ll_aux_data tmp_aux_data = { 0 };
    int rc;

    pdu_len = rxbuf[1];
    if (pdu_len == 0) {
        return -1;
    }

    tmp_aux_data.mode = rxbuf[2] >> 6;

    ext_hdr_len = rxbuf[2] & 0x3F;
    if (ext_hdr_len < BLE_LL_EXT_ADV_AUX_PTR_SIZE && !current_aux) {
        return -1;
    }

    if (ext_hdr_len == 0) {
        if (current_aux) {
            /* This is last element in the chain.
             * Clear incomplete flag
             */
            BLE_LL_AUX_CLEAR_FLAG(current_aux, BLE_LL_AUX_INCOMPLETE_BIT);
            return 1;
        }

        return -1;
    }

    ext_hdr_flags = rxbuf[3];
    ext_hdr = &rxbuf[4];

    i = 0;
    if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_ADVA_BIT)) {
        memcpy(tmp_aux_data.addr, ext_hdr + i, 6);
        tmp_aux_data.addr_type =
                ble_ll_get_addr_type(rxbuf[0] & BLE_ADV_PDU_HDR_TXADD_MASK);
        i += BLE_LL_EXT_ADV_ADVA_SIZE;
        has_addr = 1;
    }

    if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_TARGETA_BIT)) {
        memcpy(tmp_aux_data.dir_addr, ext_hdr + i, 6);
               tmp_aux_data.dir_addr_type =
                        ble_ll_get_addr_type(rxbuf[0] & BLE_ADV_PDU_HDR_RXADD_MASK);
               has_dir_addr = 1;
        i += BLE_LL_EXT_ADV_TARGETA_SIZE;
    }

    if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_RFU_BIT)) {
        i += 1;
    }

    if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_DATA_INFO_BIT)) {
        tmp_aux_data.adi = get_le16(ext_hdr + i);
        i += BLE_LL_EXT_ADV_DATA_INFO_SIZE;
        has_adi = 1;
    }

    if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT)) {

        if (ble_ll_ext_scan_parse_aux_ptr(&tmp_aux_data, ext_hdr + i) < 0) {
            return -1;
        }

        if (current_aux) {
            /* If we are here that means there is chain advertising. */

            /* Lets reuse old aux_data */
            new_aux = current_aux;

            /* TODO Collision; Do smth smart when did does not match */
            if (!(new_aux->evt_type & (BLE_HCI_ADV_SCAN_MASK))
                            && (tmp_aux_data.adi != new_aux->adi)) {
                STATS_INC(ble_ll_stats, aux_chain_err);
                BLE_LL_AUX_SET_FLAG(new_aux, BLE_LL_AUX_INCOMPLETE_ERR_BIT);
            }

            BLE_LL_AUX_SET_FLAG(new_aux, BLE_LL_AUX_CHAIN_BIT);
            BLE_LL_AUX_SET_FLAG(new_aux, BLE_LL_AUX_INCOMPLETE_BIT);
        } else {
            if (ble_ll_scan_ext_adv_init(&new_aux) < 0) {
            /* Out of memory */
            return -1;
            }
        }

        new_aux->aux_phy = tmp_aux_data.aux_phy;

        if (!current_aux) {
            /* Only for first ext adv we want to keep primary phy.*/
            new_aux->aux_primary_phy = ble_hdr->rxinfo.phy;
        }

        new_aux->chan = tmp_aux_data.chan;
        new_aux->offset = tmp_aux_data.offset;
        new_aux->mode = tmp_aux_data.mode;

        /* New aux_data or chaining */
        rc = 0;
    } else {

        /* So ext packet does not have aux ptr. It can be because
         * a) it is empty beacon (no aux ptr at all)
         * b) there is no chaining or chaining has just stopped. In this case we do hava aux_data */

        if (!current_aux) {
            new_aux = NULL;
            return 1;
        }

        /*If there is no new aux ptr, just get current one */
        new_aux = ble_hdr->rxinfo.user_data;

        /* Clear incomplete flag */
        BLE_LL_AUX_CLEAR_FLAG(new_aux, BLE_LL_AUX_INCOMPLETE_BIT);

        /* Current processing aux_ptr */
        rc = 1;
    }

    if (has_adi) {
        new_aux->adi = tmp_aux_data.adi;
        BLE_LL_AUX_SET_FLAG(new_aux, BLE_LL_AUX_HAS_ADI);
    }

    if (has_addr) {
        memcpy(new_aux->addr, tmp_aux_data.addr, 6);
        new_aux->addr_type = tmp_aux_data.addr_type;
        BLE_LL_AUX_SET_FLAG(new_aux, BLE_LL_AUX_HAS_ADDRA);
    }

    if (has_dir_addr) {
        memcpy(new_aux->dir_addr, tmp_aux_data.dir_addr, 6);
        new_aux->dir_addr_type = tmp_aux_data.dir_addr_type;
        BLE_LL_AUX_SET_FLAG(new_aux, BLE_LL_AUX_HAS_DIR_ADDRA);
    }

    ble_hdr->rxinfo.user_data = new_aux;

    return rc;
}
/**
 * Called when a receive ADV_EXT PDU has ended.
 *
 * Context: Interrupt
 *
 * @return int
 *       < 0  Error
 *      >= 0: Success (number of bytes left in PDU)
 *
 */
int
ble_ll_scan_parse_ext_hdr(struct os_mbuf *om,
                          uint8_t *adva, uint8_t adva_type,
                          uint8_t *inita, uint8_t inita_type,
                          struct ble_mbuf_hdr *ble_hdr,
                          struct ble_ll_ext_adv_report *out_evt)
{
    uint8_t pdu_len;
    uint8_t ext_hdr_len;
    uint8_t ext_hdr_flags;
    uint8_t *ext_hdr;
    uint8_t *rxbuf = om->om_data;
    int i = 1;
    struct ble_ll_scan_sm *scansm;
    struct ble_ll_aux_data *aux_data = ble_hdr->rxinfo.user_data;

    if (!out_evt) {
        return -1;
    }

    scansm = &g_ble_ll_scan_sm;

    if (!scansm->ext_scanning) {
       /* Ignore ext adv if host does not want it*/
       return -1;
    }

    pdu_len = rxbuf[1];
    if (pdu_len == 0) {
        return -1;
    }

    out_evt->evt_type = rxbuf[2] >> 6;
    if (out_evt->evt_type > BLE_LL_EXT_ADV_MODE_SCAN) {
        return -1;
    }

    if (BLE_MBUF_HDR_SCAN_RSP_RCV(ble_hdr)) {
        out_evt->evt_type |= BLE_HCI_ADV_SCAN_RSP_MASK;
    }

    ext_hdr_len = rxbuf[2] & 0x3F;
    os_mbuf_adj(om, 3);

    ext_hdr_flags = rxbuf[3];
    ext_hdr = &rxbuf[4];

    if (ext_hdr_len) {
        i = 0;
        if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_ADVA_BIT)) {
            i += BLE_LL_EXT_ADV_ADVA_SIZE;
        }

        if (adva) {
            memcpy(out_evt->addr, adva, 6);
            out_evt->addr_type = adva_type;
        }

        if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_TARGETA_BIT)) {
            i += BLE_LL_EXT_ADV_TARGETA_SIZE;
        }

        if (inita) {
           memcpy(out_evt->dir_addr, inita, 6);
           out_evt->dir_addr_type = inita_type;
           out_evt->evt_type |= BLE_HCI_ADV_DIRECT_MASK;
        }

        if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_RFU_BIT)) {
            /* Just skip it for now*/
            i += 1;
        }

        if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_DATA_INFO_BIT)) {
            ble_ll_ext_scan_parse_adv_info(scansm, out_evt, (ext_hdr + i));
            i += BLE_LL_EXT_ADV_DATA_INFO_SIZE;
        } else if (out_evt->evt_type & BLE_HCI_ADV_SCAN_RSP_MASK) {
            out_evt->sid = (aux_data->adi >> 12);
        }

        /* In this point of time we don't want to care about aux ptr */
        if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT)) {
            i += BLE_LL_EXT_ADV_AUX_PTR_SIZE;
        }

        if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_SYNC_INFO_BIT)) {
            /* TODO Handle periodic adv */
            i += BLE_LL_EXT_ADV_SYNC_INFO_SIZE;
        }

        if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_TX_POWER_BIT)) {
            out_evt->tx_power = *(ext_hdr + i);
            i += BLE_LL_EXT_ADV_TX_POWER_SIZE;
        }

        /* TODO Handle ACAD if needed */
    }

    /* In the event we need information on primary and secondary PHY used during
     * advertising.
     */
    if (!aux_data) {
        out_evt->prim_phy = ble_hdr->rxinfo.phy;
        goto done;
    }

    out_evt->sec_phy = aux_data->aux_phy;
    out_evt->prim_phy = aux_data->aux_primary_phy;

    if (ext_hdr_len) {
        /* Adjust mbuf to contain advertising data only */
        os_mbuf_adj(om, ext_hdr_len);
    }

    /* Let us first keep update event type in aux data.
     * Note that in aux chain and aux scan response packets
     * we do miss original event type, which we need for advertising report.
     */
    aux_data->evt_type |= out_evt->evt_type;
    out_evt->evt_type = aux_data->evt_type;

done:
    return pdu_len - ext_hdr_len - 1;
}

static int
ble_ll_scan_get_addr_from_ext_adv(uint8_t *rxbuf, struct ble_mbuf_hdr *ble_hdr,
                                  uint8_t **addr, uint8_t *addr_type,
                                  uint8_t **inita, uint8_t *inita_type,
                                  int *ext_mode)
{
    uint8_t pdu_len;
    uint8_t ext_hdr_len;
    uint8_t ext_hdr_flags;
    uint8_t *ext_hdr;
    int i;
    struct ble_ll_aux_data *aux_data = ble_hdr->rxinfo.user_data;

    pdu_len = rxbuf[1];
    if (pdu_len == 0) {
        return -1;
    }

    *ext_mode = rxbuf[2] >> 6;
    if (*ext_mode > BLE_LL_EXT_ADV_MODE_SCAN) {
        return -1;
    }

    if (aux_data) {
        /* If address has been provided, we do have it already in aux_data.*/
        if (BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_AUX_HAS_ADDRA)) {
            *addr = aux_data->addr;
            *addr_type = aux_data->addr_type;
        }

        if (!inita) {
            return 0;
        }

        if (BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_AUX_HAS_DIR_ADDRA)) {
            *inita = aux_data->dir_addr;
            *inita_type = aux_data->dir_addr_type;
        }

        return 0;
    }

    /* If this is just becon with no aux data, lets get address from the packet */

    ext_hdr_len = rxbuf[2] & 0x3F;

    ext_hdr_flags = rxbuf[3];
    ext_hdr = &rxbuf[4];

    i = 0;
    if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_ADVA_BIT)) {
        if (ext_hdr_len < BLE_LL_EXT_ADV_ADVA_SIZE) {
            return -1;
        }

        *addr = ext_hdr + i;
        *addr_type =
                ble_ll_get_addr_type(rxbuf[0] & BLE_ADV_PDU_HDR_TXADD_MASK);
        i += BLE_LL_EXT_ADV_ADVA_SIZE;
    }

    if (!inita) {
        return 0;
    }

    if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_TARGETA_BIT)) {
        *inita = ext_hdr + i;
        *inita_type =
                ble_ll_get_addr_type(rxbuf[0] & BLE_ADV_PDU_HDR_RXADD_MASK);
        i += BLE_LL_EXT_ADV_TARGETA_SIZE;
    }

    return 0;
}
#endif

int
ble_ll_scan_adv_decode_addr(uint8_t pdu_type, uint8_t *rxbuf,
                            struct ble_mbuf_hdr *ble_hdr,
                            uint8_t **addr, uint8_t *addr_type,
                            uint8_t **inita, uint8_t *inita_type,
                            int *ext_mode)
{
    if (pdu_type != BLE_ADV_PDU_TYPE_ADV_EXT_IND &&
        pdu_type != BLE_ADV_PDU_TYPE_AUX_CONNECT_RSP) {
        /* Legacy advertising */
        *addr_type = ble_ll_get_addr_type(rxbuf[0] & BLE_ADV_PDU_HDR_TXADD_MASK);
        *addr = rxbuf + BLE_LL_PDU_HDR_LEN;

        if (!inita) {
            return 0;
        }

        if (pdu_type != BLE_ADV_PDU_TYPE_ADV_DIRECT_IND) {
            *inita = NULL;
            *inita_type = 0;
            return 0;
        }

        *inita = rxbuf + BLE_LL_PDU_HDR_LEN + BLE_DEV_ADDR_LEN;
        *inita_type = ble_ll_get_addr_type(rxbuf[0] & BLE_ADV_PDU_HDR_RXADD_MASK);

        return 0;
    }

    /* Extended advertising */
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
    if (ble_ll_scan_get_addr_from_ext_adv(rxbuf, ble_hdr, addr, addr_type,
                                          inita, inita_type, ext_mode)) {
        return -1;
    }
#else
    return -1;
#endif

    return 0;
}

/**
 * Called when a receive PDU has ended.
 *
 * Context: Interrupt
 *
 * @param rxpdu
 *
 * @return int
 *       < 0: Disable the phy after reception.
 *      == 0: Success. Do not disable the PHY.
 *       > 0: Do not disable PHY as that has already been done.
 */
int
ble_ll_scan_rx_isr_end(struct os_mbuf *rxpdu, uint8_t crcok)
{
    int rc;
    int chk_send_req;
    int chk_wl;
    int index;
    int resolved;
    uint8_t pdu_type;
    uint8_t addr_type;
    uint8_t peer_addr_type = 0;
    uint8_t *adv_addr = NULL;
    uint8_t *peer = NULL;
    uint8_t *inita = NULL;
    uint8_t inita_type = 0;
    uint8_t *rxbuf;
    struct ble_mbuf_hdr *ble_hdr;
    struct ble_ll_scan_sm *scansm;
    struct ble_ll_scan_params *scanphy;
    int ext_adv_mode = -1;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
    uint8_t phy_mode;
    uint16_t adi;
#endif
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
    struct ble_ll_resolv_entry *rl = NULL;
#endif

    /* Get scanning state machine */
    scansm = &g_ble_ll_scan_sm;
    scanphy = &scansm->phy_data[scansm->cur_phy];

    /*
     * The reason we do something different here (as opposed to failed CRC) is
     * that the received PDU will not be handed up in this case. So we have
     * to restart scanning and handle a failed scan request. Note that we
     * return 0 in this case because we dont want the phy disabled.
     */
    if (rxpdu == NULL) {
        if (scansm->scan_rsp_pending) {
            ble_ll_scan_req_backoff(scansm, 0);
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
            ble_ll_aux_scan_rsp_failed();
#endif
        }
        ble_phy_restart_rx();
        return 0;
    }

    rc = -1;

    ble_hdr = BLE_MBUF_HDR_PTR(rxpdu);

    /* Get pdu type, pointer to address and address "type"  */
    rxbuf = rxpdu->om_data;
    pdu_type = rxbuf[0] & BLE_ADV_PDU_HDR_TYPE_MASK;

#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
    if (scansm->cur_aux_data) {
        ble_hdr->rxinfo.user_data = scansm->cur_aux_data;
        scansm->cur_aux_data = NULL;
        if (ble_ll_scan_aux_data_unref(ble_hdr->rxinfo.user_data) == 0) {
            ble_hdr->rxinfo.user_data = NULL;
            ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_AUX_INVALID;
            goto scan_rx_isr_exit;
        }

        /* If we were expecting aux/chain and it not arrived,
         * lets just exit here.
         */
        if (pdu_type != BLE_ADV_PDU_TYPE_ADV_EXT_IND) {
            goto scan_rx_isr_exit;
        }
    }
#endif

    /* Just leave if the CRC is not OK. */
    if (!crcok) {
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
        ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_AUX_INVALID;
#endif
        goto scan_rx_isr_exit;
    }

#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
    if (pdu_type == BLE_ADV_PDU_TYPE_ADV_EXT_IND) {
        if (!scansm->ext_scanning) {
            goto scan_rx_isr_exit;
        }
        /* Create new aux data for beacon or get current processing aux ptr */
        rc = ble_ll_scan_get_aux_data(ble_hdr, rxbuf);
        if (rc < 0) {
            /* No memory or broken packet */
            ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_AUX_INVALID;

            goto scan_rx_isr_exit;
        }
        if (rc == 0) {
            /* Let's tell LL to schedule aux */
            ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_AUX_PTR_WAIT;
        }

        rc = -1;
    }
#endif

    /* Lets get addresses from advertising report*/
    if (ble_ll_scan_adv_decode_addr(pdu_type, rxbuf, ble_hdr,
                                    &peer, &peer_addr_type,
                                    &inita, &inita_type, &ext_adv_mode)) {
        goto scan_rx_isr_exit;
    }

    /* Determine if request may be sent and if whitelist needs to be checked */
    chk_send_req = 0;
    switch (pdu_type) {
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
    case BLE_ADV_PDU_TYPE_ADV_EXT_IND:
        if (!peer) {
            /*Wait for AUX ptr */
            goto scan_rx_isr_exit;
        }

        if ((ext_adv_mode == BLE_LL_EXT_ADV_MODE_SCAN) &&
                (scanphy->scan_type == BLE_SCAN_TYPE_ACTIVE)) {
            chk_send_req = 1;
        }
        chk_wl = 1;
    break;
#endif
    case BLE_ADV_PDU_TYPE_ADV_IND:
    case BLE_ADV_PDU_TYPE_ADV_SCAN_IND:
        if (scanphy->scan_type == BLE_SCAN_TYPE_ACTIVE) {
            chk_send_req = 1;
        }
        chk_wl = 1;
        break;
    case BLE_ADV_PDU_TYPE_ADV_NONCONN_IND:
    case BLE_ADV_PDU_TYPE_ADV_DIRECT_IND:
        chk_wl = 1;
        break;
    default:
        chk_wl = 0;
        break;
    }

    /* Since peer might point to different address e.g. resolved one
     * lets do a copy of pointers for scan request
     */
    adv_addr = peer;
    addr_type = peer_addr_type;

    if ((scanphy->scan_filt_policy & 1) == 0) {
        chk_wl = 0;
    }
    resolved = 0;

    index = -1;
#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1)
    if (ble_ll_resolv_enabled()) {
        if (ble_ll_is_rpa(peer, peer_addr_type)) {
            index = ble_hw_resolv_list_match();
            if (index >= 0) {
                ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_RESOLVED;
                peer = g_ble_ll_resolv_list[index].rl_identity_addr;
                peer_addr_type = g_ble_ll_resolv_list[index].rl_addr_type;
                resolved = 1;
                if (ble_ll_is_rpa(inita, inita_type)) {
                    if (!ble_ll_resolv_rpa(inita, g_ble_ll_resolv_list[index].rl_local_irk)) {
                        goto scan_rx_isr_exit;
                    }
                    ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_INITA_RESOLVED;
                }
            } else {
                if (chk_wl) {
                    goto scan_rx_isr_exit;
                }
                /* We don't know peer and InitA is RPA so nothing to do more here */
                if (chk_send_req && inita && ble_ll_is_rpa(inita, inita_type)) {
                    goto scan_rx_isr_exit;
                }
            }
        } else if (chk_send_req && inita && ble_ll_is_rpa(inita, inita_type)) {
            /* If remote is identity address but InitA is RPA, make sure we can resolve it.
             * If not, nothing more to do here
             */
            rl = ble_ll_resolv_list_find(peer, peer_addr_type);
            if (!rl) {
                goto scan_rx_isr_exit;
            }

            if (!ble_ll_resolv_rpa(inita, rl->rl_local_irk)) {
                goto scan_rx_isr_exit;
            }

            ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_INITA_RESOLVED;
        }
    }
#else
    if (chk_send_req && inita && ble_ll_is_rpa(inita, inita_type)) {
        goto scan_rx_isr_exit;
    }
#endif
    scansm->scan_rpa_index = index;

    /* If whitelist enabled, check to see if device is in the white list */
    if (chk_wl && !ble_ll_whitelist_match(peer, peer_addr_type, resolved)) {
        goto scan_rx_isr_exit;
    }
    ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_DEVMATCH;

    /* Should we send a scan request? */
    if (chk_send_req) {
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
        if (pdu_type == BLE_ADV_PDU_TYPE_ADV_EXT_IND) {
            /* Let us check if InitA is our device.
             * Note that InitA RPA is handled above where privacy is handled
             */
            if (inita && !ble_ll_is_rpa(inita, inita_type) &&
                         !ble_ll_is_our_devaddr(inita, inita_type)) {
                goto scan_rx_isr_exit;
            }

            if (ble_ll_scan_get_adi(ble_hdr->rxinfo.user_data, &adi) < 0) {
                /* There is not ADI in scannable packet? This must be some trash,
                 * ignore it
                 */
                goto scan_rx_isr_exit;
            }
            if (ble_ll_scan_have_rxd_scan_rsp(peer, peer_addr_type, 1, adi)) {
                goto scan_rx_isr_exit;
            }
        } else {
            /* Dont send scan request if we have sent one to this advertiser */
            if (ble_ll_scan_have_rxd_scan_rsp(peer, peer_addr_type, 0, 0)) {
                goto scan_rx_isr_exit;
            }
        }
#else
        /* Dont send scan request if we have sent one to this advertiser */
        if (ble_ll_scan_have_rxd_scan_rsp(peer, peer_addr_type, 0, 0)) {
            goto scan_rx_isr_exit;
        }
#endif
        /* Better not be a scan response pending */
        BLE_LL_ASSERT(scansm->scan_rsp_pending == 0);

        /* We want to send a request. See if backoff allows us */
        --scansm->backoff_count;
        if (scansm->backoff_count == 0) {
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
            phy_mode = ble_ll_phy_to_phy_mode(ble_hdr->rxinfo.phy,
                                              BLE_HCI_LE_PHY_CODED_ANY);
            if (ble_ll_sched_scan_req_over_aux_ptr(ble_hdr->rxinfo.channel,
                                                   phy_mode)) {
                goto scan_rx_isr_exit;
            }
#endif
            /* XXX: TODO assume we are on correct phy */
            ble_ll_scan_req_pdu_make(scansm, adv_addr, addr_type);
            rc = ble_phy_tx(ble_ll_tx_mbuf_pducb, scansm->scan_req_pdu,
                            BLE_PHY_TRANSITION_TX_RX);

            if (rc == 0) {
                /* Set "waiting for scan response" flag */
                scansm->scan_rsp_pending = 1;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
                if (ble_hdr->rxinfo.channel <  BLE_PHY_NUM_DATA_CHANS) {
                    /* Let's keep the aux ptr as a reference to scan rsp */
                    scansm->cur_aux_data = ble_hdr->rxinfo.user_data;
                    ble_ll_scan_aux_data_ref(scansm->cur_aux_data);
                    STATS_INC(ble_ll_stats, aux_scan_req_tx);
                }
#endif
            }
        }
    }

scan_rx_isr_exit:
    if (rc) {
        ble_ll_state_set(BLE_LL_STATE_STANDBY);
    }
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
    /* On handover lets increase ref count */
    if (ble_hdr->rxinfo.user_data) {
        ble_ll_scan_aux_data_ref(ble_hdr->rxinfo.user_data);
    }
#endif
    return rc;
}

/**
 * Called to resume scanning. This is called after an advertising event or
 * connection event has ended. It is also called if we receive a packet while
 * in the initiating or scanning state.
 *
 * Context: Link Layer task
 */
void
ble_ll_scan_chk_resume(void)
{
    os_sr_t sr;
    struct ble_ll_scan_sm *scansm;

    scansm = &g_ble_ll_scan_sm;
    if (scansm->scan_enabled) {
        OS_ENTER_CRITICAL(sr);
        if (scansm->restart_timer_needed) {
            scansm->restart_timer_needed = 0;
            ble_ll_event_send(&scansm->scan_sched_ev);
            STATS_INC(ble_ll_stats, scan_timer_restarted);
            OS_EXIT_CRITICAL(sr);
            return;
        }

        if (ble_ll_state_get() == BLE_LL_STATE_STANDBY &&
                    ble_ll_scan_window_chk(scansm, os_cputime_get32()) == 0) {
            /* Turn on the receiver and set state */
            ble_ll_scan_start(scansm, NULL);
        }
        OS_EXIT_CRITICAL(sr);
    }
}

/**
 * Scan timer callback; means that the scan window timeout has been reached
 * and we should perform the appropriate actions.
 *
 * Context: Interrupt (cputimer)
 *
 * @param arg Pointer to scan state machine.
 */
void
ble_ll_scan_timer_cb(void *arg)
{
    struct ble_ll_scan_sm *scansm;

    scansm = (struct ble_ll_scan_sm *)arg;
    ble_ll_event_send(&scansm->scan_sched_ev);
}

/**
 * Called when the wait for response timer expires while in the scanning
 * state.
 *
 * Context: Interrupt.
 */
void
ble_ll_scan_wfr_timer_exp(void)
{
    struct ble_ll_scan_sm *scansm;

    /*
     * If we timed out waiting for a response, the scan response pending
     * flag should be set. Deal with scan backoff. Put device back into rx.
     */
    scansm = &g_ble_ll_scan_sm;
    if (scansm->scan_rsp_pending) {
        ble_ll_scan_req_backoff(scansm, 0);
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
        ble_ll_aux_scan_rsp_failed();
        ble_ll_scan_chk_resume();
#endif
    }

#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
    if (scansm->cur_aux_data) {
        ble_ll_scan_end_adv_evt(scansm->cur_aux_data);
        scansm->cur_aux_data = NULL;
        STATS_INC(ble_ll_stats, aux_missed_adv);
        ble_ll_scan_chk_resume();
    }
#endif

    ble_phy_restart_rx();
}

#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
/*
 * Send extended advertising report
 *
 * @return -1 on error (data truncated or other error)
 *          0 on success (data status is "completed")
 *          1 on success (data status is not "completed")
 */
static int
ble_ll_hci_send_ext_adv_report(uint8_t ptype, uint8_t *adva, uint8_t adva_type,
                               uint8_t *inita, uint8_t inita_type,
                               struct os_mbuf *om,
                               struct ble_mbuf_hdr *hdr)
{
    struct ble_ll_aux_data *aux_data = hdr->rxinfo.user_data;
    struct ble_ll_ext_adv_report *evt;
    struct ble_ll_ext_adv_report *next_evt;
    int offset;
    int datalen;
    int rc;
    bool need_event;
    uint16_t max_event_len;

    if (!ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_EXT_ADV_RPT)) {
        rc = -1;
        goto done;
    }

    /*
     * We keep one allocated event in aux_data to be able to truncate chain
     * properly in case of error. If there is no event in aux_data it means this
     * is the first event for this chain.
     */
    if (aux_data && aux_data->evt) {
        evt = aux_data->evt;
        aux_data->evt = NULL;
    } else {
        evt = ble_ll_scan_init_ext_adv_report(NULL);
        if (!evt) {
            rc = -1;
            goto done;
        }
    }

#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
    /* If RPA has been used, make sure we use correct address types
     * in the advertising report.
     */
    if (BLE_MBUF_HDR_RESOLVED(hdr)) {
        adva_type += 2;
    }
    if (BLE_MBUF_HDR_INITA_RESOLVED(hdr)) {
        inita_type += 2;
    }
#endif

    datalen = ble_ll_scan_parse_ext_hdr(om, adva, adva_type, inita, inita_type, hdr, evt);
    if (datalen < 0) {
        /* XXX what should we do here? send some trimmed event? */
        ble_hci_trans_buf_free((uint8_t *)evt);
        rc = -1;
        goto done;
    }

    /* Max_event_len contains advertising data and BLE_HCI_EVENT_HDR_LEN as this is related
     * to the buffer available for the event. The maximum is 255 + 2
     */
    offset = 0;
    max_event_len = min(UINT8_MAX + BLE_HCI_EVENT_HDR_LEN, BLE_LL_MAX_EVT_LEN);

    do {
        need_event = false;
        next_evt = NULL;

        evt->adv_data_len = min(max_event_len - sizeof(*evt),
                                datalen - offset);
        /* Event len, should not contain event meta code and let itself */
        evt->event_len = (sizeof(*evt) - BLE_HCI_EVENT_HDR_LEN) + evt->adv_data_len;
        evt->rssi = hdr->rxinfo.rssi;

        os_mbuf_copydata(om, offset, evt->adv_data_len, evt->adv_data);
        offset += evt->adv_data_len;


        if (offset < datalen) {
            /* Need another event for next fragment of this PDU */
            need_event = true;
        } else if (aux_data && BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_AUX_INCOMPLETE_ERR_BIT)) {
            need_event = false;
        } else if (aux_data && BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_AUX_INCOMPLETE_BIT)) {
            /* Need another event for next PDU in chain */
            need_event = true;
        }

        /* Assume data status is not "completed" */
        rc = 1;

        if (need_event) {
            /*
             * We will need another event so let's try to allocate one now. If
             * we cannot do this, need to mark event as truncated.
             */
            next_evt = ble_ll_scan_init_ext_adv_report(evt);

            if (next_evt) {
                evt->evt_type |= (BLE_HCI_ADV_DATA_STATUS_INCOMPLETE);
            } else {
                evt->evt_type |= (BLE_HCI_ADV_DATA_STATUS_TRUNCATED);
                BLE_LL_AUX_SET_FLAG(aux_data, BLE_LL_AUX_TRUNCATED_SENT);
                rc = -1;
            }
        } else if (aux_data ) {
            if (BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_AUX_INCOMPLETE_ERR_BIT)) {
                evt->evt_type |= (BLE_HCI_ADV_DATA_STATUS_TRUNCATED);
                rc = -1;
            } else if (BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_AUX_INCOMPLETE_BIT)) {
                evt->evt_type |= (BLE_HCI_ADV_DATA_STATUS_INCOMPLETE);
            } else {
                rc = 0;
            }
        } else {
            rc = 0;
        }

        ble_ll_hci_event_send((uint8_t *)evt);

        evt = next_evt;
    } while ((offset < datalen) && evt);

    BLE_LL_ASSERT(offset <= datalen);

    if (aux_data){
        /* Store any event left for later use */
        aux_data->evt = evt;
    } else {
        /* If it is empty beacon, evt shall be NULL */
        BLE_LL_ASSERT(!evt);
    }

done:
    /* If advertising event is completed or failed, we can drop the reference */
    if (rc <= 0) {
        if (aux_data){
            if ((rc == 0) && (aux_data->evt_type & BLE_HCI_ADV_SCAN_RSP_MASK)) {
                /* Scan response completed successfully */
                ble_ll_scan_add_scan_rsp_adv(aux_data->addr, aux_data->addr_type,
                                             1, aux_data->adi);
            }
            ble_ll_scan_aux_data_unref(aux_data);
        }
    }

    return rc;
}
#endif
/**
 * Process a received PDU while in the scanning state.
 *
 * Context: Link Layer task.
 *
 * @param pdu_type
 * @param rxbuf
 */
void
ble_ll_scan_rx_pkt_in(uint8_t ptype, struct os_mbuf *om, struct ble_mbuf_hdr *hdr)
{
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
    int index;
    struct ble_ll_resolv_entry *rl;
#endif
    uint8_t *rxbuf = om->om_data;
    uint8_t *adv_addr = NULL;
    uint8_t *adva;
    uint8_t *ident_addr;
    uint8_t ident_addr_type;
    uint8_t *init_addr = NULL;
    uint8_t init_addr_type = 0;
    uint8_t txadd = 0;
    uint8_t rxadd;
    uint8_t scan_rsp_chk;
    struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm;
    struct ble_mbuf_hdr *ble_hdr;
    int ext_adv_mode = -1;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
    struct ble_ll_aux_data *aux_data = hdr->rxinfo.user_data;
    int rc;
    uint8_t evt_possibly_truncated = 0;
#endif

    /* Set scan response check flag */
    scan_rsp_chk = BLE_MBUF_HDR_SCAN_RSP_RCV(hdr);

    /* We dont care about scan requests or connect requests */
    if (!BLE_MBUF_HDR_CRC_OK(hdr)) {
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
        if (BLE_MBUF_HDR_AUX_INVALID(hdr)) {
            evt_possibly_truncated = 1;
        }
#endif
        goto scan_continue;
    }

#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
    if (aux_data && ptype != BLE_ADV_PDU_TYPE_ADV_EXT_IND) {
        /* LL was scheduled for aux but received something different.
         * Let's just ignore received event and send truncated for
         * previous report if needed.
         */
        evt_possibly_truncated = 1;
        goto scan_continue;
    }
#endif

    if ((ptype == BLE_ADV_PDU_TYPE_SCAN_REQ) || (ptype == BLE_ADV_PDU_TYPE_CONNECT_REQ)) {
        goto scan_continue;
    }

    if (ble_ll_scan_adv_decode_addr(ptype, rxbuf, hdr,
                                    &adv_addr, &txadd,
                                    &init_addr, &init_addr_type,
                                    &ext_adv_mode)) {
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
        evt_possibly_truncated = 1;
#endif
        goto scan_continue;
    }

    ident_addr = adv_addr;
    ident_addr_type = txadd;

#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1)
    index = scansm->scan_rpa_index;
    if (index >= 0) {
        ident_addr = g_ble_ll_resolv_list[index].rl_identity_addr;
        ident_addr_type = g_ble_ll_resolv_list[index].rl_addr_type;

        if (ble_ll_is_rpa(init_addr, init_addr_type)) {
           /* Let's try resolve InitA. */
           if (ble_ll_resolv_rpa(init_addr, g_ble_ll_resolv_list[index].rl_local_irk)) {
               init_addr = ble_ll_get_our_devaddr(scansm->own_addr_type & 1);
               init_addr_type = scansm->own_addr_type & 1;
           }
       }
    } else if (init_addr && ble_ll_resolv_enabled() && ble_ll_is_rpa(init_addr, init_addr_type)) {
        /* If we are here it means AdvA is identity. Check if initA is RPA */
        rl = ble_ll_resolv_list_find(ident_addr, ident_addr_type);
        if (rl && ble_ll_resolv_rpa(init_addr, rl->rl_local_irk)) {
            init_addr = ble_ll_get_our_devaddr(scansm->own_addr_type & 1);
            init_addr_type = scansm->own_addr_type & 1;
        }
    }
#endif

    /* Check the scanner filter policy */
    if (ble_ll_scan_chk_filter_policy(ptype, adv_addr, txadd, init_addr,
                                      init_addr_type,
                                      BLE_MBUF_HDR_DEVMATCH(hdr))) {
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
        ble_ll_scan_aux_data_unref(aux_data);
#endif
        goto scan_continue;
    }

    /*
     * XXX: The BLE spec is a bit unclear here. What if we get a scan
     * response from an advertiser that we did not send a request to?
     * Do we send an advertising report? Do we add it to list of devices
     * that we have heard a scan response from?
     */
    if (ptype == BLE_ADV_PDU_TYPE_SCAN_RSP) {
        /*
         * If this is a scan response in reply to a request we sent we need
         * to store this advertiser's address so we dont send a request to it.
         */
        if (scansm->scan_rsp_pending && scan_rsp_chk) {
            /*
             * We could also check the timing of the scan reponse; make sure
             * that it is relatively close to the end of the scan request but
             * we wont for now.
             */
            ble_hdr = BLE_MBUF_HDR_PTR(scansm->scan_req_pdu);
            rxadd = ble_hdr->txinfo.hdr_byte & BLE_ADV_PDU_HDR_RXADD_MASK;
            adva = scansm->scan_req_pdu->om_data + BLE_DEV_ADDR_LEN;
            if (((txadd && rxadd) || ((txadd + rxadd) == 0)) &&
                !memcmp(adv_addr, adva, BLE_DEV_ADDR_LEN)) {
                /* We have received a scan response. Add to list */
                ble_ll_scan_add_scan_rsp_adv(ident_addr, ident_addr_type, 0, 0);

                /* Perform scan request backoff procedure */
                ble_ll_scan_req_backoff(scansm, 1);
            }
        } else {
            /* Ignore if this is not ours */
            goto scan_continue;
        }
    }

    /* Filter duplicates */
    if (scansm->scan_filt_dups) {
        if (ble_ll_scan_is_dup_adv(hdr, ptype, ident_addr_type, ident_addr)) {
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
            if (ptype == BLE_ADV_PDU_TYPE_ADV_EXT_IND) {
                ble_ll_scan_aux_data_unref(aux_data);
            }
#endif
            goto scan_continue;
        }
    }

#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
    if (ptype == BLE_ADV_PDU_TYPE_ADV_EXT_IND) {
        if (!scansm->ext_scanning) {
            ble_ll_scan_aux_data_unref(aux_data);
            goto scan_continue;
        }

        if (BLE_MBUF_HDR_AUX_INVALID(hdr)) {
            evt_possibly_truncated = 1;
            goto scan_continue;
        }

        /* If it is ignore it means event is already truncated, just unref aux */
        if (aux_data && BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_AUX_IGNORE_BIT)) {
            ble_ll_scan_aux_data_unref(aux_data);
            goto scan_continue;
        }

        /* Let's see if that packet contains aux ptr*/
        if (BLE_MBUF_HDR_WAIT_AUX(hdr)) {
            BLE_LL_ASSERT(aux_data);
            if (ble_ll_sched_aux_scan(hdr, scansm, hdr->rxinfo.user_data)) {
                /* We are here when could not schedule the aux ptr */
                hdr->rxinfo.flags &= ~BLE_MBUF_HDR_F_AUX_PTR_WAIT;
                /* Mark that chain is trimmed */
                BLE_LL_AUX_SET_FLAG(aux_data, BLE_LL_AUX_INCOMPLETE_ERR_BIT);
                /* Note: aux_data unref will be done when truncated is sent to the host or
                 * below if we failed to schedule for the very first aux packet.
                 */
            } else {
                /* We are here because successfully scheduled for next aux */
                ble_ll_scan_aux_data_ref(aux_data);
            }

            /*
             * If this is ext adv, there is nothing to do here but just leave and wait
             * for aux packet. However, if we was not able to schedule for first aux packet,
             * make sure to unref aux_data here
             */
            if (!BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_AUX_CHAIN_BIT)) {
                if (BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_AUX_INCOMPLETE_ERR_BIT)) {
                    ble_ll_scan_aux_data_unref(aux_data);
                }
                goto scan_continue;
            }

            STATS_INC(ble_ll_stats, aux_chain_cnt);
        }

        /* For the time when sending events up, lets increase ref count */
        ble_ll_scan_aux_data_ref(aux_data);
        rc = ble_ll_hci_send_ext_adv_report(ptype, ident_addr, ident_addr_type,
                                            init_addr, init_addr_type, om, hdr);
        if (rc < 0) {
            /*
             * Data were trimmed so no need to scan this chain anymore. Also
             * make sure we do not send any more events for this chain, just in
             * case we managed to scan something before events were processed.
             */
            if (BLE_MBUF_HDR_WAIT_AUX(hdr)) {
                hdr->rxinfo.flags &= ~BLE_MBUF_HDR_F_AUX_PTR_WAIT;
                if (ble_ll_sched_rmv_elem(&aux_data->sch) == 0) {
                    ble_ll_scan_aux_data_unref(aux_data);
                }
            }
            BLE_LL_AUX_SET_FLAG(aux_data, BLE_LL_AUX_IGNORE_BIT);
        } else if ((rc == 0) && scansm->scan_filt_dups && aux_data && aux_data->adi) {
                ble_ll_scan_add_dup_adv(ident_addr, ident_addr_type,
                                        BLE_HCI_LE_SUBEV_EXT_ADV_RPT,
                                        aux_data->evt_type, 1, aux_data->adi);
        }
        ble_ll_scan_aux_data_unref(aux_data);

        ble_ll_scan_switch_phy(scansm);

        if (scansm->scan_rsp_pending) {
            if (!scan_rsp_chk) {
                /* We are here because we sent SCAN_REQ and wait for SCAN_RSP.
                 * We do not drop reference here by purpose, because
                 * it was already dropped in ble_ll_hci_send_ext_adv_report() as
                 * very first advertising report for scannable report is "completed"
                 */
                return;
            }

            /* XXX: For now let us consider scan response as succeed in the backoff context,
             * after first scan response packet is received.
             * I guess we should marked it succeed after complete scan response is received,
             * and failed when truncated, but then we need to analyze reason of truncation as it
             * also might be issue of the resources on our side
             */
            ble_ll_scan_req_backoff(scansm, 1);
        }

        goto scan_continue;
    }
#endif

    /* Send the advertising report */
    ble_ll_scan_send_adv_report(ptype, ident_addr, ident_addr_type,
                                init_addr, init_addr_type, om, hdr, scansm);

scan_continue:

#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
    if (aux_data) {
        if (evt_possibly_truncated) {
            ble_ll_scan_end_adv_evt(aux_data);
        } else {
            /* This is ref for handover to LL */
            ble_ll_scan_aux_data_unref(aux_data);
        }
    }
#endif
    /*
     * If the scan response check bit is set and we are pending a response,
     * we have failed the scan request (as we would have reset the scan rsp
     * pending flag if we received a valid response
     */
    if (scansm->scan_rsp_pending && scan_rsp_chk) {
        ble_ll_scan_req_backoff(scansm, 0);
    }

    ble_ll_scan_chk_resume();
}

int
ble_ll_scan_set_scan_params(uint8_t *cmd)
{
    uint8_t scan_type;
    uint8_t own_addr_type;
    uint8_t filter_policy;
    uint16_t scan_itvl;
    uint16_t scan_window;
    struct ble_ll_scan_sm *scansm;
    struct ble_ll_scan_params *scanp;

    /* If already enabled, we return an error */
    scansm = &g_ble_ll_scan_sm;
    if (scansm->scan_enabled) {
        return BLE_ERR_CMD_DISALLOWED;
    }

    /* Get the scan interval and window */
    scan_type = cmd[0];
    scan_itvl  = get_le16(cmd + 1);
    scan_window = get_le16(cmd + 3);
    own_addr_type = cmd[5];
    filter_policy = cmd[6];

    /* Check scan type */
    if ((scan_type != BLE_HCI_SCAN_TYPE_PASSIVE) &&
        (scan_type != BLE_HCI_SCAN_TYPE_ACTIVE)) {
        return BLE_ERR_INV_HCI_CMD_PARMS;
    }

    /* Check interval and window */
    if ((scan_itvl < BLE_HCI_SCAN_ITVL_MIN) ||
        (scan_itvl > BLE_HCI_SCAN_ITVL_MAX) ||
        (scan_window < BLE_HCI_SCAN_WINDOW_MIN) ||
        (scan_window > BLE_HCI_SCAN_WINDOW_MAX) ||
        (scan_itvl < scan_window)) {
        return BLE_ERR_INV_HCI_CMD_PARMS;
    }

    /* Check own addr type */
    if (own_addr_type > BLE_HCI_ADV_OWN_ADDR_MAX) {
        return BLE_ERR_INV_HCI_CMD_PARMS;
    }

    /* Check scanner filter policy */
    if (filter_policy > BLE_HCI_SCAN_FILT_MAX) {
        return BLE_ERR_INV_HCI_CMD_PARMS;
    }

    /* Store scan parameters */
    scanp = &g_ble_ll_scan_params[PHY_UNCODED];
    scanp->configured = 1;
    scanp->scan_type = scan_type;
    scanp->scan_itvl = scan_itvl;
    scanp->scan_window = scan_window;
    scanp->scan_filt_policy = filter_policy;
    scanp->own_addr_type = own_addr_type;

#if (BLE_LL_SCAN_PHY_NUMBER == 2)
    g_ble_ll_scan_params[PHY_CODED].configured = 0;
#endif

    return 0;
}

#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
static int
ble_ll_check_scan_params(uint8_t type, uint16_t itvl, uint16_t window)
{
    /* Check scan type */
    if ((type != BLE_HCI_SCAN_TYPE_PASSIVE) &&
        (type != BLE_HCI_SCAN_TYPE_ACTIVE)) {
        return BLE_ERR_INV_HCI_CMD_PARMS;
    }

    /* Check interval and window */
    if ((itvl < BLE_HCI_SCAN_ITVL_MIN) ||
        (itvl > BLE_HCI_SCAN_ITVL_MAX) ||
        (window < BLE_HCI_SCAN_WINDOW_MIN) ||
        (window > BLE_HCI_SCAN_WINDOW_MAX) ||
        (itvl < window)) {
        return BLE_ERR_INV_HCI_CMD_PARMS;
    }

    return 0;
}

int
ble_ll_set_ext_scan_params(uint8_t *cmd, uint8_t cmdlen)
{
    struct ble_ll_scan_params new_params[BLE_LL_SCAN_PHY_NUMBER] = { };
    struct ble_ll_scan_params *uncoded = &new_params[PHY_UNCODED];
    struct ble_ll_scan_params *coded = &new_params[PHY_CODED];
    uint8_t idx;
    int rc;

    if (cmdlen < 3) {
        return BLE_ERR_INV_HCI_CMD_PARMS;
    }

    /* If already enabled, we return an error */
    if (g_ble_ll_scan_sm.scan_enabled) {
        return BLE_ERR_CMD_DISALLOWED;
    }

    /* Check own addr type */
    if (cmd[0] > BLE_HCI_ADV_OWN_ADDR_MAX) {
        return BLE_ERR_INV_HCI_CMD_PARMS;
    }

    coded->own_addr_type = cmd[0];
    uncoded->own_addr_type = cmd[0];

    /* Check scanner filter policy */
    if (cmd[1] > BLE_HCI_SCAN_FILT_MAX) {
        return BLE_ERR_INV_HCI_CMD_PARMS;
    }

    coded->scan_filt_policy = cmd[1];
    uncoded->scan_filt_policy = cmd[1];

    /* Check if no reserved bits in PHYS are set and that at least one valid PHY
     * is set.
     */
    if (!(cmd[2] & ble_ll_valid_scan_phy_mask) ||
                                    (cmd[2] & ~ble_ll_valid_scan_phy_mask)) {
         return BLE_ERR_INV_HCI_CMD_PARMS;
     }

    idx = 3;
    if (cmd[2] & BLE_HCI_LE_PHY_1M_PREF_MASK) {
        if (cmdlen < idx + 5) {
            return BLE_ERR_INV_HCI_CMD_PARMS;
        }

        uncoded->scan_type = cmd[idx];
        idx++;
        uncoded->scan_itvl = get_le16(cmd + idx);
        idx += 2;
        uncoded->scan_window = get_le16(cmd + idx);
        idx += 2;

        rc = ble_ll_check_scan_params(uncoded->scan_type,
                                      uncoded->scan_itvl,
                                      uncoded->scan_window);
        if (rc) {
            return rc;
        }

        /* That means user whats to use this PHY for scanning */
        uncoded->configured = 1;
    }

#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY)
    if (cmd[2] & BLE_HCI_LE_PHY_CODED_PREF_MASK) {
        if (cmdlen < idx + 5) {
            return BLE_ERR_INV_HCI_CMD_PARMS;
        }

        coded->scan_type = cmd[idx];
        idx++;
        coded->scan_itvl = get_le16(cmd + idx);
        idx += 2;
        coded->scan_window = get_le16(cmd + idx);
        idx += 2;

        rc = ble_ll_check_scan_params(coded->scan_type,
                                      coded->scan_itvl,
                                      coded->scan_window);
        if (rc) {
            return rc;
        }

        /* That means user whats to use this PHY for scanning */
        coded->configured = 1;
    }
#endif

    /* If host requests continuous scan for 2 PHYs, we double scan interval
     * and split it for two equal scan windows between 2 PHYs
     */
    if ((coded->configured && uncoded->configured) &&
                ((uncoded->scan_itvl == uncoded->scan_window) ||
                (coded->scan_itvl == coded-> scan_window))) {

                uncoded->scan_itvl *= 2;
                coded-> scan_itvl = uncoded->scan_itvl;
                coded->scan_window = uncoded->scan_window;
    }

    memcpy(g_ble_ll_scan_params, new_params, sizeof(new_params));

    return 0;
}

#endif

#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
static void
ble_ll_scan_duration_period_timers_restart(struct ble_ll_scan_sm *scansm)
{
    uint32_t now;

    now = os_cputime_get32();

    os_cputime_timer_stop(&scansm->duration_timer);
    os_cputime_timer_stop(&scansm->period_timer);

    if (scansm->duration_ticks) {
        os_cputime_timer_start(&scansm->duration_timer,
                                                now + scansm->duration_ticks);

        if (scansm->period_ticks) {
            os_cputime_timer_start(&scansm->period_timer,
                                                    now + scansm->period_ticks);
        }
    }
}

static void
ble_ll_scan_duration_timer_cb(void *arg)
{
    struct ble_ll_scan_sm *scansm;

    scansm = (struct ble_ll_scan_sm *)arg;

    ble_ll_scan_sm_stop(2);

    /* if period is set both timers get started from period cb */
    if (!scansm->period_ticks) {
        ble_ll_hci_ev_send_scan_timeout();
    }
}

static void
ble_ll_scan_period_timer_cb(void *arg)
{
    struct ble_ll_scan_sm *scansm = arg;

    ble_ll_scan_sm_start(scansm);

    /* always start timer regardless of ble_ll_scan_sm_start result
     * if it failed will restart in next period
     */
    ble_ll_scan_duration_period_timers_restart(scansm);
}
#endif

/**
 * ble ll scan set enable
 *
 *  HCI scan set enable command processing function
 *
 *  Context: Link Layer task (HCI Command parser).
 *
 * @param cmd Pointer to command buffer
 *
 * @return int BLE error code.
 */
int
ble_ll_scan_set_enable(uint8_t *cmd, uint8_t ext)
{
    int rc;
    uint8_t filter_dups;
    uint8_t enable;
    struct ble_ll_scan_sm *scansm;
    struct ble_ll_scan_params *scanp;
    struct ble_ll_scan_params *scanphy;
    int i;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
    uint32_t period_ticks = 0;
    uint32_t dur_ticks = 0;
    uint16_t period;
    uint16_t dur;
#endif

    /* Check for valid parameters */
    enable = cmd[0];
    filter_dups = cmd[1];
    if ((filter_dups > 1) || (enable > 1)) {
        return BLE_ERR_INV_HCI_CMD_PARMS;
    }

    scansm = &g_ble_ll_scan_sm;

#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
    /* we can do that here since value will never change until reset */
    scansm->ext_scanning = ext;

    if (ext) {
        dur = get_le16(cmd + 2);

        /* Period parameter is ignored when the Duration parameter is zero */
        if (dur) {
            period = get_le16(cmd + 4);
        } else {
            period = 0;
        }

        /* period is in 1.28 sec units
         * TODO support full range, would require os_cputime milliseconds API
         */
        if (period > 3355) {
            return BLE_ERR_INV_HCI_CMD_PARMS;
        }
        period_ticks = os_cputime_usecs_to_ticks(period * 1280000);

        /* duration is in 10ms units */
        dur_ticks = os_cputime_usecs_to_ticks(dur * 10000);

        if (dur_ticks && period_ticks && (dur_ticks >= period_ticks)) {
            return BLE_ERR_INV_HCI_CMD_PARMS;
        }
    }
#endif

    /* disable*/
    if (!enable) {
        if (scansm->scan_enabled) {
            ble_ll_scan_sm_stop(1);
        }
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
        os_cputime_timer_stop(&scansm->duration_timer);
        os_cputime_timer_stop(&scansm->period_timer);
#endif

        return BLE_ERR_SUCCESS;
    }

    /* if already enable we just need to update parameters */
    if (scansm->scan_enabled) {
        /* Controller does not allow initiating and scanning.*/
        for (i = 0; i < BLE_LL_SCAN_PHY_NUMBER; i++) {
            scanphy = &scansm->phy_data[i];
            if (scanphy->configured &&
                                scanphy->scan_type == BLE_SCAN_TYPE_INITIATE) {
                return BLE_ERR_CMD_DISALLOWED;
            }
        }

        /* update filter policy */
        scansm->scan_filt_dups = filter_dups;

#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
        /* restart timers according to new settings */
        scansm->duration_ticks = dur_ticks;
        scansm->period_ticks = period_ticks;
        ble_ll_scan_duration_period_timers_restart(scansm);
#endif

        return BLE_ERR_SUCCESS;
    }

    /* we can store those upfront regardless of start scan result since scan is
     * disabled now
     */

    scansm->scan_filt_dups = filter_dups;
    scansm->cur_phy = PHY_NOT_CONFIGURED;
    scansm->next_phy = PHY_NOT_CONFIGURED;

    for (i = 0; i < BLE_LL_SCAN_PHY_NUMBER; i++) {
        scanphy = &scansm->phy_data[i];
        scanp = &g_ble_ll_scan_params[i];

        if (!scanp->configured) {
            continue;
        }

        scanphy->configured = scanp->configured;
        scanphy->scan_type = scanp->scan_type;
        scanphy->scan_itvl = scanp->scan_itvl;
        scanphy->scan_window = scanp->scan_window;
        scanphy->scan_filt_policy = scanp->scan_filt_policy;
        scanphy->own_addr_type = scanp->own_addr_type;

        if (scansm->cur_phy == PHY_NOT_CONFIGURED) {
            scansm->cur_phy = i;
            /* Take own_addr_type from the first configured PHY.
             * Note: All configured PHYs shall have the same own_addr_type
             */
            scansm->own_addr_type = scanphy->own_addr_type;
        } else {
            scansm->next_phy = i;
        }
    }

    rc = ble_ll_scan_sm_start(scansm);

#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
    if (rc == BLE_ERR_SUCCESS) {
        scansm->duration_ticks = dur_ticks;
        scansm->period_ticks = period_ticks;
        ble_ll_scan_duration_period_timers_restart(scansm);
    }
#endif

    return rc;
}

/**
 * Checks if controller can change the whitelist. If scanning is enabled and
 * using the whitelist the controller is not allowed to change the whitelist.
 *
 * @return int 0: not allowed to change whitelist; 1: change allowed.
 */
int
ble_ll_scan_can_chg_whitelist(void)
{
    int rc;
    struct ble_ll_scan_sm *scansm;
    struct ble_ll_scan_params *params;

    scansm = &g_ble_ll_scan_sm;
    params = &scansm->phy_data[scansm->cur_phy];
    if (scansm->scan_enabled && (params->scan_filt_policy & 1)) {
        rc = 0;
    } else {
        rc = 1;
    }

    return rc;
}

int
ble_ll_scan_initiator_start(struct hci_create_conn *hcc,
                            struct ble_ll_scan_sm **sm)
{
    struct ble_ll_scan_sm *scansm;
    struct ble_ll_scan_params *scanphy;
    int rc;

    scansm = &g_ble_ll_scan_sm;
    scansm->own_addr_type = hcc->own_addr_type;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
    scansm->ext_scanning = 0;
#endif
    scansm->cur_phy = PHY_UNCODED;
    scansm->next_phy = PHY_NOT_CONFIGURED;

    scanphy = &scansm->phy_data[scansm->cur_phy];
    scanphy->scan_filt_policy = hcc->filter_policy;
    scanphy->scan_itvl = hcc->scan_itvl;
    scanphy->scan_window = hcc->scan_window;
    scanphy->scan_type = BLE_SCAN_TYPE_INITIATE;

    rc = ble_ll_scan_sm_start(scansm);
    if (sm == NULL) {
        return rc;
    }

    if (rc == BLE_ERR_SUCCESS) {
        *sm = scansm;
    } else {
        *sm = NULL;
    }

    return rc;
}

#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
int
ble_ll_scan_ext_initiator_start(struct hci_ext_create_conn *hcc,
                                struct ble_ll_scan_sm **sm)
{
    struct ble_ll_scan_sm *scansm;
    struct ble_ll_scan_params *uncoded;
    struct ble_ll_scan_params *coded;
    struct hci_ext_conn_params *params;
    int rc;

    scansm = &g_ble_ll_scan_sm;
    scansm->own_addr_type = hcc->own_addr_type;
    scansm->cur_phy = PHY_NOT_CONFIGURED;
    scansm->next_phy = PHY_NOT_CONFIGURED;
    scansm->ext_scanning = 1;

    if (hcc->init_phy_mask & BLE_PHY_MASK_1M) {
        params = &hcc->params[0];
        uncoded = &scansm->phy_data[PHY_UNCODED];

        uncoded->scan_itvl = params->scan_itvl;
        uncoded->scan_window = params->scan_window;
        uncoded->scan_type = BLE_SCAN_TYPE_INITIATE;
        uncoded->scan_filt_policy = hcc->filter_policy;
        scansm->cur_phy = PHY_UNCODED;
    }

    if (hcc->init_phy_mask & BLE_PHY_MASK_CODED) {
        params = &hcc->params[2];
        coded = &scansm->phy_data[PHY_CODED];

        coded->scan_itvl = params->scan_itvl;
        coded->scan_window = params->scan_window;
        coded->scan_type = BLE_SCAN_TYPE_INITIATE;
        coded->scan_filt_policy = hcc->filter_policy;
        if (scansm->cur_phy == PHY_NOT_CONFIGURED) {
            scansm->cur_phy = PHY_CODED;
        } else {
            scansm->next_phy = PHY_CODED;
        }
    }

    /* If host request for continuous scan if 2 PHYs are requested, we split
     * time on two
     */
    if ((scansm->next_phy != PHY_NOT_CONFIGURED) &&
                ((uncoded->scan_itvl == uncoded->scan_window) ||
                (coded->scan_itvl == coded-> scan_window))) {

        uncoded->scan_itvl *= 2;
        coded-> scan_itvl = uncoded->scan_itvl;
        coded->scan_window = uncoded->scan_window;
    }

    rc = ble_ll_scan_sm_start(scansm);
    if (sm == NULL) {
        return rc;
    }

    if (rc == BLE_ERR_SUCCESS) {
        *sm = scansm;
    } else {
        *sm = NULL;
    }

    return rc;
}
#endif

/**
 * Checks to see if the scanner is enabled.
 *
 * @return int 0: not enabled; enabled otherwise
 */
int
ble_ll_scan_enabled(void)
{
    return (int)g_ble_ll_scan_sm.scan_enabled;
}

/**
 * Returns the peer resolvable private address of last device connecting to us
 *
 * @return uint8_t*
 */
uint8_t *
ble_ll_scan_get_peer_rpa(void)
{
    struct ble_ll_scan_sm *scansm;

    /* XXX: should this go into IRK list or connection? */
    scansm = &g_ble_ll_scan_sm;
    return scansm->scan_peer_rpa;
}

/**
 * Returns the local resolvable private address currently being using by
 * the scanner/initiator
 *
 * @return uint8_t*
 */
uint8_t *
ble_ll_scan_get_local_rpa(void)
{
    uint8_t *rpa;
    struct ble_ll_scan_sm *scansm;

    scansm = &g_ble_ll_scan_sm;

    /*
     * The RPA we used is in connect request or scan request and is the
     * first address in the packet
     */
    rpa = scansm->scan_req_pdu->om_data;

    return rpa;
}

/**
 * Set the Resolvable Private Address in the scanning (or initiating) state
 * machine.
 *
 * XXX: should this go into IRK list or connection?
 *
 * @param rpa
 */
void
ble_ll_scan_set_peer_rpa(uint8_t *rpa)
{
    struct ble_ll_scan_sm *scansm;

    scansm = &g_ble_ll_scan_sm;
    memcpy(scansm->scan_peer_rpa, rpa, BLE_DEV_ADDR_LEN);
}

/* Returns the PDU allocated by the scanner */
struct os_mbuf *
ble_ll_scan_get_pdu(void)
{
    return g_ble_ll_scan_sm.scan_req_pdu;
}

/* Returns true if whitelist is enabled for scanning */
int
ble_ll_scan_whitelist_enabled(void)
{
    struct ble_ll_scan_params *params;

    params = &g_ble_ll_scan_sm.phy_data[g_ble_ll_scan_sm.cur_phy];
    return params->scan_filt_policy & 1;
}

static void
ble_ll_scan_common_init(void)
{
    struct ble_ll_scan_sm *scansm;
    struct ble_ll_scan_params *scanp;
    int i;

    /* Clear state machine in case re-initialized */
    scansm = &g_ble_ll_scan_sm;
    memset(scansm, 0, sizeof(struct ble_ll_scan_sm));

    /* Clear scan parameters in case re-initialized */
    memset(g_ble_ll_scan_params, 0, sizeof(g_ble_ll_scan_params));

    /* Initialize scanning window end event */
    ble_npl_event_init(&scansm->scan_sched_ev, ble_ll_scan_event_proc, scansm);

    for (i = 0; i < BLE_LL_SCAN_PHY_NUMBER; i++) {
        /* Set all non-zero default parameters */
        scanp = &g_ble_ll_scan_params[i];
        scanp->scan_itvl = BLE_HCI_SCAN_ITVL_DEF;
        scanp->scan_window = BLE_HCI_SCAN_WINDOW_DEF;
    }

    scansm->phy_data[PHY_UNCODED].phy = BLE_PHY_1M;
#if (BLE_LL_SCAN_PHY_NUMBER == 2)
    scansm->phy_data[PHY_CODED].phy = BLE_PHY_CODED;
#endif

#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1)
    /* Make sure we'll generate new NRPA if necessary */
    scansm->scan_nrpa_timer = ble_npl_time_get();
#endif

    /* Initialize scanning timer */
    os_cputime_timer_init(&scansm->scan_timer, ble_ll_scan_timer_cb, scansm);

    /* Initialize extended scan timers */
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
    os_cputime_timer_init(&scansm->duration_timer,
                                        ble_ll_scan_duration_timer_cb, scansm);
    os_cputime_timer_init(&scansm->period_timer, ble_ll_scan_period_timer_cb,
                                                                        scansm);
#endif

    /* Get a scan request mbuf (packet header) and attach to state machine */
    scansm->scan_req_pdu = os_msys_get_pkthdr(BLE_SCAN_LEGACY_MAX_PKT_LEN,
                                              sizeof(struct ble_mbuf_hdr));
    BLE_LL_ASSERT(scansm->scan_req_pdu != NULL);
}

/**
 * Called when the controller receives the reset command. Resets the
 * scanning state machine to its initial state.
 *
 * @return int
 */
void
ble_ll_scan_reset(void)
{
    struct ble_ll_scan_sm *scansm;

    scansm = &g_ble_ll_scan_sm;

    /* If enabled, stop it. */
    if (scansm->scan_enabled) {
        ble_ll_scan_sm_stop(0);
    }

    /* stop extended scan timers */
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
    os_cputime_timer_stop(&scansm->duration_timer);
    os_cputime_timer_stop(&scansm->period_timer);
#endif

    /* Free the scan request pdu */
    os_mbuf_free_chain(scansm->scan_req_pdu);

    /* Reset duplicate advertisers and those from which we rxd a response */
    g_ble_ll_scan_num_rsp_advs = 0;
    memset(&g_ble_ll_scan_rsp_advs[0], 0, sizeof(g_ble_ll_scan_rsp_advs));

    g_ble_ll_scan_num_dup_advs = 0;
    memset(&g_ble_ll_scan_dup_advs[0], 0, sizeof(g_ble_ll_scan_dup_advs));

#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
    /* clear memory pool for AUX scan results */
    os_mempool_clear(&ext_adv_pool);
#endif

    /* Call the common init function again */
    ble_ll_scan_common_init();
}

/**
 * ble ll scan init
 *
 * Initialize a scanner. Must be called before scanning can be started.
 * Expected to be called with a un-initialized scanning state machine.
 */
void
ble_ll_scan_init(void)
{
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
    os_error_t err;

    err = os_mempool_init(&ext_adv_pool,
                          MYNEWT_VAL(BLE_LL_EXT_ADV_AUX_PTR_CNT),
                          sizeof (struct ble_ll_aux_data),
                          ext_adv_mem,
                          "ble_ll_aux_scan_pool");
    BLE_LL_ASSERT(err == 0);
#endif

    ble_ll_scan_common_init();
}
