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