/**
 * Called when the LL receives a scan request or connection request
 *
 * Context: Called from interrupt context.
 *
 * @param rxbuf
 *
 * @return -1: request not for us or is a connect request.
 *          0: request (scan) is for us and we successfully went from rx to tx.
 *        > 0: PHY error attempting to go from rx to tx.
 */
static int
ble_ll_adv_rx_req(uint8_t pdu_type, struct os_mbuf *rxpdu)
{
    int rc;
    uint8_t chk_whitelist;
    uint8_t txadd;
    uint8_t *rxbuf;
    struct ble_mbuf_hdr *ble_hdr;
    struct ble_ll_adv_sm *advsm;

    rxbuf = rxpdu->om_data;
    if (ble_ll_adv_addr_cmp(rxbuf)) {
        return -1;
    }

    /* Set device match bit if we are whitelisting */
    advsm = &g_ble_ll_adv_sm;
    if (pdu_type == BLE_ADV_PDU_TYPE_SCAN_REQ) {
        chk_whitelist = advsm->adv_filter_policy & 1;
    } else {
        chk_whitelist = advsm->adv_filter_policy & 2;
    }

    /* Set device match bit if we are whitelisting */
    ble_hdr = BLE_MBUF_HDR_PTR(rxpdu);
    if (chk_whitelist) {
        /* Get the scanners address type */
        if (rxbuf[0] & BLE_ADV_PDU_HDR_TXADD_MASK) {
            txadd = BLE_ADDR_TYPE_RANDOM;
        } else {
            txadd = BLE_ADDR_TYPE_PUBLIC;
        }

        /* Check for whitelist match */
        if (!ble_ll_whitelist_match(rxbuf + BLE_LL_PDU_HDR_LEN, txadd)) {
            return -1;
        }
        ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_DEVMATCH;
    }

    /* Setup to transmit the scan response if appropriate */
    rc = -1;
    if (pdu_type == BLE_ADV_PDU_TYPE_SCAN_REQ) {
        ble_phy_set_txend_cb(ble_ll_adv_tx_done, &g_ble_ll_adv_sm);
        rc = ble_phy_tx(advsm->scan_rsp_pdu, BLE_PHY_TRANSITION_NONE);
        if (!rc) {
            ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_SCAN_RSP_TXD;
            STATS_INC(ble_ll_stats, scan_rsp_txg);
        }
    }

    return rc;
}
/**
 * ll rx pkt in
 *
 * Process received packet from PHY.
 *
 * Context: Link layer task
 *
 */
static void
ble_ll_rx_pkt_in(void)
{
    os_sr_t sr;
    uint8_t pdu_type;
    uint8_t *rxbuf;
    struct os_mbuf_pkthdr *pkthdr;
    struct ble_mbuf_hdr *ble_hdr;
    struct os_mbuf *m;

    /* Drain all packets off the queue */
    while (STAILQ_FIRST(&g_ble_ll_data.ll_rx_pkt_q)) {
        /* Get mbuf pointer from packet header pointer */
        pkthdr = STAILQ_FIRST(&g_ble_ll_data.ll_rx_pkt_q);
        m = (struct os_mbuf *)((uint8_t *)pkthdr - sizeof(struct os_mbuf));

        /* Remove from queue */
        OS_ENTER_CRITICAL(sr);
        STAILQ_REMOVE_HEAD(&g_ble_ll_data.ll_rx_pkt_q, omp_next);
        OS_EXIT_CRITICAL(sr);

        /* Note: pdu type wont get used unless this is an advertising pdu */
        ble_hdr = BLE_MBUF_HDR_PTR(m);
        rxbuf = m->om_data;
        pdu_type = rxbuf[0] & BLE_ADV_PDU_HDR_TYPE_MASK;
        ble_ll_count_rx_stats(ble_hdr, pkthdr->omp_len, pdu_type);

        /* Process the data or advertising pdu */
        if (ble_hdr->rxinfo.channel < BLE_PHY_NUM_DATA_CHANS) {
            ble_ll_conn_rx_data_pdu(m, ble_hdr);
        } else {
            /* Process the PDU */
            switch (BLE_MBUF_HDR_RX_STATE(ble_hdr)) {
            case BLE_LL_STATE_ADV:
                ble_ll_adv_rx_pkt_in(pdu_type, rxbuf, ble_hdr);
                break;
            case BLE_LL_STATE_SCANNING:
                ble_ll_scan_rx_pkt_in(pdu_type, rxbuf, ble_hdr);
                break;
            case BLE_LL_STATE_INITIATING:
                ble_ll_init_rx_pkt_in(rxbuf, ble_hdr);
                break;
            default:
                /* Any other state should never occur */
                STATS_INC(ble_ll_stats, bad_ll_state);
                break;
            }

            /* Free the packet buffer */
            os_mbuf_free_chain(m);
        }
    }
}
/**
 * Called to initialize a mbuf used by the controller
 *
 * NOTE: this is only used when the mbuf is created by the controller;
 * it should not be used for data packets (ACL data packets) that come from
 * the host. This routine assumes that the entire pdu length can fit in
 * one mbuf contiguously.
 *
 * @param m
 * @param pdulen
 * @param hdr
 */
void
ble_ll_mbuf_init(struct os_mbuf *m, uint8_t pdulen, uint8_t hdr)
{
    struct ble_mbuf_hdr *ble_hdr;

    /* Set mbuf length and packet length */
    m->om_len = pdulen;
    OS_MBUF_PKTHDR(m)->omp_len = pdulen;

    /* Set BLE transmit header */
    ble_hdr = BLE_MBUF_HDR_PTR(m);
    ble_hdr->txinfo.flags = 0;
    ble_hdr->txinfo.offset = 0;
    ble_hdr->txinfo.pyld_len = pdulen;
    ble_hdr->txinfo.hdr_byte = hdr;
}
/**
 * Called when the LL receives a scan request or connection request
 *
 * Context: Called from interrupt context.
 *
 * @param rxbuf
 *
 * @return -1: request not for us or is a connect request.
 *          0: request (scan) is for us and we successfully went from rx to tx.
 *        > 0: PHY error attempting to go from rx to tx.
 */
static int
ble_ll_adv_rx_req(uint8_t pdu_type, struct os_mbuf *rxpdu)
{
    int rc;
    int resolved;
    uint8_t chk_wl;
    uint8_t txadd;
    uint8_t peer_addr_type;
    uint8_t *rxbuf;
    uint8_t *adva;
    uint8_t *peer;
    struct ble_mbuf_hdr *ble_hdr;
    struct ble_ll_adv_sm *advsm;
    struct os_mbuf *scan_rsp;

    /* See if adva in the request (scan or connect) matches what we sent */
    advsm = &g_ble_ll_adv_sm;
    rxbuf = rxpdu->om_data;
    adva = rxbuf + BLE_LL_PDU_HDR_LEN + BLE_DEV_ADDR_LEN;
    if (memcmp(advsm->adva, adva, BLE_DEV_ADDR_LEN)) {
        return -1;
    }

    /* Set device match bit if we are whitelisting */
    if (pdu_type == BLE_ADV_PDU_TYPE_SCAN_REQ) {
        chk_wl = advsm->adv_filter_policy & 1;
    } else {
        chk_wl = advsm->adv_filter_policy & 2;
    }

    /* Get the peer address type */
    if (rxbuf[0] & BLE_ADV_PDU_HDR_TXADD_MASK) {
        txadd = BLE_ADDR_TYPE_RANDOM;
    } else {
        txadd = BLE_ADDR_TYPE_PUBLIC;
    }

    ble_hdr = BLE_MBUF_HDR_PTR(rxpdu);
    peer = rxbuf + BLE_LL_PDU_HDR_LEN;
    peer_addr_type = txadd;
    resolved = 0;

#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1)
    if (ble_ll_is_rpa(peer, txadd) && ble_ll_resolv_enabled()) {
        advsm->adv_rpa_index = ble_hw_resolv_list_match();
        if (advsm->adv_rpa_index >= 0) {
            ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_RESOLVED;
            if (chk_wl) {
                peer = g_ble_ll_resolv_list[advsm->adv_rpa_index].rl_identity_addr;
                peer_addr_type = g_ble_ll_resolv_list[advsm->adv_rpa_index].rl_addr_type;
                resolved = 1;
            }
        } else {
            if (chk_wl) {
                return -1;
            }
        }
    }
#endif

    /* Set device match bit if we are whitelisting */
    if (chk_wl && !ble_ll_whitelist_match(peer, peer_addr_type, resolved)) {
        return -1;
    }

    /*
     * We set the device match bit to tell the upper layer that we will
     * accept the request
     */
    ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_DEVMATCH;

    /* Setup to transmit the scan response if appropriate */
    rc = -1;
    if (pdu_type == BLE_ADV_PDU_TYPE_SCAN_REQ) {
        scan_rsp = ble_ll_adv_scan_rsp_pdu_make(advsm);
        if (scan_rsp) {
            ble_phy_set_txend_cb(ble_ll_adv_tx_done, &g_ble_ll_adv_sm);
            rc = ble_phy_tx(scan_rsp, BLE_PHY_TRANSITION_NONE);
            if (!rc) {
                ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_SCAN_RSP_TXD;
                STATS_INC(ble_ll_stats, scan_rsp_txg);
            }
            os_mbuf_free_chain(scan_rsp);
        }
    }

    return rc;
}
int
ble_phy_tx(struct os_mbuf *txpdu, uint8_t end_trans)
{
    int rc;
    uint8_t *dptr;
    uint8_t *pktptr;
    uint8_t payload_len;
    uint32_t state;
    uint32_t shortcuts;
    struct ble_mbuf_hdr *ble_hdr;

    /* Better have a pdu! */
    assert(txpdu != NULL);

    /*
     * This check is to make sure that the radio is not in a state where
     * it is moving to disabled state. If so, let it get there.
     */
    nrf_wait_disabled();

    ble_hdr = BLE_MBUF_HDR_PTR(txpdu);
    payload_len = ble_hdr->txinfo.pyld_len;

#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) == 1)
    if (g_ble_phy_data.phy_encrypted) {
        dptr = (uint8_t *)&g_ble_phy_enc_buf[0];
        ++dptr;
        pktptr = (uint8_t *)&g_ble_phy_tx_buf[0];
        NRF_CCM->SHORTS = 1;
        NRF_CCM->INPTR = (uint32_t)dptr;
        NRF_CCM->OUTPTR = (uint32_t)pktptr;
        NRF_CCM->SCRATCHPTR = (uint32_t)&g_nrf_encrypt_scratchpad[0];
        NRF_CCM->EVENTS_ERROR = 0;
        NRF_CCM->MODE = CCM_MODE_LENGTH_Msk;
        NRF_CCM->CNFPTR = (uint32_t)&g_nrf_ccm_data;
        NRF_PPI->CHENCLR = PPI_CHEN_CH25_Msk | PPI_CHEN_CH23_Msk;
        NRF_PPI->CHENSET = PPI_CHEN_CH24_Msk;
    } else {
#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1)
        NRF_PPI->CHENCLR = PPI_CHEN_CH23_Msk;
        NRF_AAR->IRKPTR = (uint32_t)&g_nrf_irk_list[0];
#endif
        dptr = (uint8_t *)&g_ble_phy_tx_buf[0];
        ++dptr;
        pktptr = dptr;
    }
#else
#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1)
    NRF_PPI->CHENCLR = PPI_CHEN_CH23_Msk;
#endif
    dptr = (uint8_t *)&g_ble_phy_tx_buf[0];
    ++dptr;
    pktptr = dptr;
#endif

    /* RAM representation has S0, LENGTH and S1 fields. (3 bytes) */
    dptr[0] = ble_hdr->txinfo.hdr_byte;
    dptr[1] = payload_len;
    dptr[2] = 0;
    dptr += 3;
    NRF_RADIO->PACKETPTR = (uint32_t)pktptr;

    /* Clear the ready, end and disabled events */
    NRF_RADIO->EVENTS_READY = 0;
    NRF_RADIO->EVENTS_END = 0;
    NRF_RADIO->EVENTS_DISABLED = 0;

    /* Enable shortcuts for transmit start/end. */
    shortcuts = RADIO_SHORTS_END_DISABLE_Msk | RADIO_SHORTS_READY_START_Msk;
    if (end_trans == BLE_PHY_TRANSITION_TX_RX) {
        shortcuts |= RADIO_SHORTS_DISABLED_RXEN_Msk;
    }
    NRF_RADIO->SHORTS = shortcuts;
    NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk;

    /* Set transmitted payload length */
    g_ble_phy_data.phy_tx_pyld_len = payload_len;

    /* Set the PHY transition */
    g_ble_phy_data.phy_transition = end_trans;

    /* If we already started transmitting, abort it! */
    state = NRF_RADIO->STATE;
    if (state != RADIO_STATE_STATE_Tx) {
        /* Copy data from mbuf into transmit buffer */
        os_mbuf_copydata(txpdu, ble_hdr->txinfo.offset, payload_len, dptr);

        /* Set phy state to transmitting and count packet statistics */
        g_ble_phy_data.phy_state = BLE_PHY_STATE_TX;
        STATS_INC(ble_phy_stats, tx_good);
        STATS_INCN(ble_phy_stats, tx_bytes, payload_len + BLE_LL_PDU_HDR_LEN);
        rc = BLE_ERR_SUCCESS;
    } else {
        ble_phy_disable();
        STATS_INC(ble_phy_stats, tx_late);
        rc = BLE_PHY_ERR_RADIO_STATE;
    }

    return rc;
}
/**
 * Copies the data from the phy receive buffer into a mbuf chain.
 *
 * @param dptr Pointer to receive buffer
 * @param rxpdu Pointer to already allocated mbuf chain
 *
 * NOTE: the packet header already has the total mbuf length in it. The
 * lengths of the individual mbufs are not set prior to calling.
 *
 */
void
ble_phy_rxpdu_copy(uint8_t *dptr, struct os_mbuf *rxpdu)
{
    uint16_t rem_bytes;
    uint16_t mb_bytes;
    uint16_t copylen;
    uint32_t *dst;
    uint32_t *src;
    struct os_mbuf *m;
    struct ble_mbuf_hdr *ble_hdr;
    struct os_mbuf_pkthdr *pkthdr;

    /* Better be aligned */
    assert(((uint32_t)dptr & 3) == 0);

    pkthdr = OS_MBUF_PKTHDR(rxpdu);
    rem_bytes = pkthdr->omp_len;

    /* Fill in the mbuf pkthdr first. */
    dst = (uint32_t *)(rxpdu->om_data);
    src = (uint32_t *)dptr;

    mb_bytes = (rxpdu->om_omp->omp_databuf_len - rxpdu->om_pkthdr_len - 4);
    copylen = min(mb_bytes, rem_bytes);
    copylen &= 0xFFFC;
    rem_bytes -= copylen;
    mb_bytes -= copylen;
    rxpdu->om_len = copylen;
    while (copylen > 0) {
        *dst = *src;
        ++dst;
        ++src;
        copylen -= 4;
    }

    /* Copy remaining bytes */
    m = rxpdu;
    while (rem_bytes > 0) {
        /* If there are enough bytes in the mbuf, copy them and leave */
        if (rem_bytes <= mb_bytes) {
            memcpy(m->om_data + m->om_len, src, rem_bytes);
            m->om_len += rem_bytes;
            break;
        }

        m = SLIST_NEXT(m, om_next);
        assert(m != NULL);

        mb_bytes = m->om_omp->omp_databuf_len;
        copylen = min(mb_bytes, rem_bytes);
        copylen &= 0xFFFC;
        rem_bytes -= copylen;
        mb_bytes -= copylen;
        m->om_len = copylen;
        dst = (uint32_t *)m->om_data;
        while (copylen > 0) {
            *dst = *src;
            ++dst;
            ++src;
            copylen -= 4;
        }
    }

    /* Copy ble header */
    ble_hdr = BLE_MBUF_HDR_PTR(rxpdu);
    memcpy(ble_hdr, &g_ble_phy_data.rxhdr, sizeof(struct ble_mbuf_hdr));
}
int
ble_phy_tx(struct os_mbuf *txpdu, uint8_t beg_trans, uint8_t end_trans)
{
    int rc;
    uint8_t *dptr;
    uint32_t state;
    uint32_t shortcuts;
    struct ble_mbuf_hdr *ble_hdr;

    /* Better have a pdu! */
    assert(txpdu != NULL);

    /* If radio is not disabled, */
    nrf52_wait_disabled();

    if (beg_trans == BLE_PHY_TRANSITION_RX_TX) {
        if ((NRF_RADIO->SHORTS & RADIO_SHORTS_DISABLED_TXEN_Msk) == 0) {
            assert(0);
        }
        /* Radio better be in TXRU state or we are in bad shape */
        state = RADIO_STATE_STATE_TxRu;
    } else {
        /* Radio should be in disabled state */
        state = RADIO_STATE_STATE_Disabled;
    }

    if (NRF_RADIO->STATE != state) {
        ble_phy_disable();
        ++g_ble_phy_stats.radio_state_errs;
        return BLE_PHY_ERR_RADIO_STATE;
    }

    /* Write LL header first */
    ble_hdr = BLE_MBUF_HDR_PTR(txpdu);
    dptr = (uint8_t *)&g_ble_phy_txrx_buf[0];
    dptr[0] = ble_hdr->txinfo.hdr_byte;
    dptr[1] = ble_hdr->txinfo.pyld_len;
    dptr += 2;

    /* Set radio transmit data pointer */
    NRF_RADIO->PACKETPTR = (uint32_t)&g_ble_phy_txrx_buf[0];

    /* Clear the ready, end and disabled events */
    NRF_RADIO->EVENTS_READY = 0;
    NRF_RADIO->EVENTS_END = 0;
    NRF_RADIO->EVENTS_DISABLED = 0;

    /* Enable shortcuts for transmit start/end. */
    shortcuts = RADIO_SHORTS_END_DISABLE_Msk | RADIO_SHORTS_READY_START_Msk;
    if (end_trans == BLE_PHY_TRANSITION_TX_RX) {
        /* If we are going into receive after this, try to get a buffer. */
        if (ble_phy_rxpdu_get()) {
            shortcuts |= RADIO_SHORTS_DISABLED_RXEN_Msk;
        }
    }
    NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk;
    NRF_RADIO->SHORTS = shortcuts;

    /* Trigger transmit if our state was disabled */
    if (state == RADIO_STATE_STATE_Disabled) {
        NRF_RADIO->TASKS_TXEN = 1;
    }

    /* Set the PHY transition */
    g_ble_phy_data.phy_transition = end_trans;

    /* Read back radio state. If in TXRU, we are fine */
    state = NRF_RADIO->STATE;
    if (state == RADIO_STATE_STATE_TxRu) {
        /* Copy data from mbuf into transmit buffer */
        os_mbuf_copydata(txpdu, ble_hdr->txinfo.offset, 
                         ble_hdr->txinfo.pyld_len, dptr);

        /* Set phy state to transmitting and count packet statistics */
        g_ble_phy_data.phy_state = BLE_PHY_STATE_TX;
        ++g_ble_phy_stats.tx_good;
        g_ble_phy_stats.tx_bytes += ble_hdr->txinfo.pyld_len + 
            BLE_LL_PDU_HDR_LEN;
        rc = BLE_ERR_SUCCESS;
    } else {
        if (state == RADIO_STATE_STATE_Tx) {
            ++g_ble_phy_stats.tx_late;
        } else {
            ++g_ble_phy_stats.tx_fail;
        }

        /* Frame failed to transmit */
        ble_phy_disable();
        rc = BLE_PHY_ERR_RADIO_STATE;
    }

    return rc;
}
static void
ble_phy_isr(void)
{
    int rc;
    uint8_t transition;
    uint8_t crcok;
    uint32_t irq_en;
    uint32_t state;
    uint32_t wfr_time;
    struct os_mbuf *rxpdu;
    struct ble_mbuf_hdr *ble_hdr;

    /* Read irq register to determine which interrupts are enabled */
    irq_en = NRF_RADIO->INTENCLR;

    /* Check for disabled event. This only happens for transmits now */
    if ((irq_en & RADIO_INTENCLR_DISABLED_Msk) && NRF_RADIO->EVENTS_DISABLED) {
        /* Better be in TX state! */
        assert(g_ble_phy_data.phy_state == BLE_PHY_STATE_TX);

        ble_ll_log(BLE_LL_LOG_ID_PHY_TXEND, g_ble_phy_txrx_buf[1], 0, 
                   NRF_TIMER0->CC[2]);

        /* Clear events and clear interrupt on disabled event */
        NRF_RADIO->EVENTS_DISABLED = 0;
        NRF_RADIO->INTENCLR = RADIO_INTENCLR_DISABLED_Msk;
        NRF_RADIO->EVENTS_END = 0;
        state = NRF_RADIO->SHORTS;

        transition = g_ble_phy_data.phy_transition;
        if (transition == BLE_PHY_TRANSITION_TX_RX) {
            /* Clear the rx started flag */
            g_ble_phy_data.phy_rx_started = 0;

            /* Packet pointer needs to be reset. */
            if (g_ble_phy_data.rxpdu != NULL) {
                NRF_RADIO->PACKETPTR = (uint32_t)g_ble_phy_data.rxpdu->om_data;

                /* I want to know when 1st byte received (after address) */
                NRF_RADIO->BCC = 8; /* in bits */
                NRF_RADIO->EVENTS_ADDRESS = 0;
                NRF_RADIO->EVENTS_DEVMATCH = 0;
                NRF_RADIO->EVENTS_BCMATCH = 0;
                NRF_RADIO->EVENTS_RSSIEND = 0;
                NRF_RADIO->SHORTS = RADIO_SHORTS_END_DISABLE_Msk | 
                                    RADIO_SHORTS_READY_START_Msk |
                                    RADIO_SHORTS_ADDRESS_BCSTART_Msk |
                                    RADIO_SHORTS_ADDRESS_RSSISTART_Msk |
                                    RADIO_SHORTS_DISABLED_RSSISTOP_Msk;

                NRF_RADIO->INTENSET = RADIO_INTENSET_ADDRESS_Msk;
                g_ble_phy_data.phy_state = BLE_PHY_STATE_RX;
            } else {
                /* Disable the phy */
                ++g_ble_phy_stats.no_bufs;
                ble_phy_disable();
            }

            /* 
             * Enable the wait for response timer. Note that cc #2 on
             * timer 0 contains the transmit end time
             */ 
            wfr_time = NRF_TIMER0->CC[2];
            wfr_time += cputime_usecs_to_ticks(BLE_LL_WFR_USECS);
            ble_ll_wfr_enable(wfr_time);
        } else {
            /* Better not be going from rx to tx! */
            assert(transition == BLE_PHY_TRANSITION_NONE);
        }

        /* Call transmit end callback */
        if (g_ble_phy_data.txend_cb) {
            g_ble_phy_data.txend_cb(g_ble_phy_data.txend_arg);
        }
    }

    /* We get this if we have started to receive a frame */
    if ((irq_en & RADIO_INTENCLR_ADDRESS_Msk) && NRF_RADIO->EVENTS_ADDRESS) {
        /* Clear events and clear interrupt */
        NRF_RADIO->EVENTS_ADDRESS = 0;
        NRF_RADIO->INTENCLR = RADIO_INTENCLR_ADDRESS_Msk;

        assert(g_ble_phy_data.rxpdu != NULL);

        /* Wait to get 1st byte of frame */
        while (1) {
            state = NRF_RADIO->STATE;
            if (NRF_RADIO->EVENTS_BCMATCH != 0) {
                break;
            }

            /* 
             * If state is disabled, we should have the BCMATCH. If not,
             * something is wrong!
             */ 
            if (state == RADIO_STATE_STATE_Disabled) {
                NRF_RADIO->INTENCLR = NRF52_RADIO_IRQ_MASK_ALL;
                NRF_RADIO->SHORTS = 0;
                goto phy_isr_exit;
            }
        }

        /* Initialize flags, channel and state in ble header at rx start */
        ble_hdr = BLE_MBUF_HDR_PTR(g_ble_phy_data.rxpdu);
        ble_hdr->rxinfo.flags = ble_ll_state_get();
        ble_hdr->rxinfo.channel = g_ble_phy_data.phy_chan;
        ble_hdr->rxinfo.handle = 0;

        /* Call Link Layer receive start function */
        rc = ble_ll_rx_start(g_ble_phy_data.rxpdu, g_ble_phy_data.phy_chan);
        if (rc >= 0) {
            g_ble_phy_data.phy_rx_started = 1;
            if (rc > 0) {
                /* We need to go from disabled to TXEN */
                NRF_RADIO->SHORTS = RADIO_SHORTS_END_DISABLE_Msk | 
                                    RADIO_SHORTS_READY_START_Msk |
                                    RADIO_SHORTS_DISABLED_TXEN_Msk;
            } else {
                NRF_RADIO->SHORTS = RADIO_SHORTS_END_DISABLE_Msk | 
                                    RADIO_SHORTS_READY_START_Msk;
            }

            /* Set rx end ISR enable */
            NRF_RADIO->INTENSET = RADIO_INTENSET_END_Msk;
        } else {
            /* Disable PHY */
            ble_phy_disable();
            irq_en = 0;
            ++g_ble_phy_stats.rx_aborts;
        }

        /* Count rx starts */
        ++g_ble_phy_stats.rx_starts;
    }

    /* Receive packet end (we dont enable this for transmit) */
    if ((irq_en & RADIO_INTENCLR_END_Msk) && NRF_RADIO->EVENTS_END) {
        /* Clear events and clear interrupt */
        NRF_RADIO->EVENTS_END = 0;
        NRF_RADIO->INTENCLR = RADIO_INTENCLR_END_Msk;

        /* Set RSSI and CRC status flag in header */
        ble_hdr = BLE_MBUF_HDR_PTR(g_ble_phy_data.rxpdu);
        assert(NRF_RADIO->EVENTS_RSSIEND != 0);
        ble_hdr->rxinfo.rssi = -1 * NRF_RADIO->RSSISAMPLE;
        ble_hdr->end_cputime = NRF_TIMER0->CC[2];

        /* Count PHY crc errors and valid packets */
        crcok = (uint8_t)NRF_RADIO->CRCSTATUS;
        if (!crcok) {
            ++g_ble_phy_stats.rx_crc_err;
        } else {
            ++g_ble_phy_stats.rx_valid;
            ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_CRC_OK;
        }

        /* Call Link Layer receive payload function */
        rxpdu = g_ble_phy_data.rxpdu;
        g_ble_phy_data.rxpdu = NULL;
        rc = ble_ll_rx_end(rxpdu, ble_hdr);
        if (rc < 0) {
            /* Disable the PHY. */
            ble_phy_disable();
        }
    }

phy_isr_exit:
    /* Ensures IRQ is cleared */
    state = NRF_RADIO->SHORTS;

    /* Count # of interrupts */
    ++g_ble_phy_stats.phy_isrs;
}