/** * Stop advertising state machine * * Context: Link Layer task. * * @param advsm */ static void ble_ll_adv_sm_stop(struct ble_ll_adv_sm *advsm) { os_sr_t sr; if (advsm->enabled) { /* Remove any scheduled advertising items */ ble_ll_sched_rmv_elem(&advsm->adv_sch); /* Set to standby if we are no longer advertising */ OS_ENTER_CRITICAL(sr); if (ble_ll_state_get() == BLE_LL_STATE_ADV) { ble_phy_disable(); ble_ll_wfr_disable(); ble_ll_state_set(BLE_LL_STATE_STANDBY); } OS_EXIT_CRITICAL(sr); os_eventq_remove(&g_ble_ll_data.ll_evq, &advsm->adv_txdone_ev); /* If there is an event buf we need to free it */ if (advsm->conn_comp_ev) { ble_hci_trans_buf_free(advsm->conn_comp_ev); advsm->conn_comp_ev = NULL; } /* Disable advertising */ advsm->enabled = 0; } }
/** * Puts the phy into receive mode. * * @return int 0: success; BLE Phy error code otherwise */ int ble_phy_rx(void) { /* Check radio state */ nrf_wait_disabled(); if (NRF_RADIO->STATE != RADIO_STATE_STATE_Disabled) { ble_phy_disable(); STATS_INC(ble_phy_stats, radio_state_errs); return BLE_PHY_ERR_RADIO_STATE; } /* Make sure all interrupts are disabled */ NRF_RADIO->INTENCLR = NRF_RADIO_IRQ_MASK_ALL; /* Clear events prior to enabling receive */ NRF_RADIO->EVENTS_END = 0; NRF_RADIO->EVENTS_DISABLED = 0; /* Setup for rx */ ble_phy_rx_xcvr_setup(); /* Start the receive task in the radio if not automatically going to rx */ if ((NRF_PPI->CHEN & PPI_CHEN_CH21_Msk) == 0) { NRF_RADIO->TASKS_RXEN = 1; } ble_ll_log(BLE_LL_LOG_ID_PHY_RX, g_ble_phy_data.phy_encrypted, 0, 0); return 0; }
/** * Called to reset the controller. This performs a "software reset" of the link * layer; it does not perform a HW reset of the controller nor does it reset * the HCI interface. * * Context: Link Layer task (HCI command) * * @return int The ble error code to place in the command complete event that * is returned when this command is issued. */ int ble_ll_reset(void) { int rc; os_sr_t sr; /* Stop the phy */ ble_phy_disable(); /* Stop any wait for response timer */ OS_ENTER_CRITICAL(sr); ble_ll_wfr_disable(); ble_ll_sched_stop(); OS_EXIT_CRITICAL(sr); /* Stop any scanning */ ble_ll_scan_reset(); /* Stop any advertising */ ble_ll_adv_reset(); /* FLush all packets from Link layer queues */ ble_ll_flush_pkt_queue(&g_ble_ll_data.ll_tx_pkt_q); ble_ll_flush_pkt_queue(&g_ble_ll_data.ll_rx_pkt_q); /* Reset LL stats */ memset((uint8_t *)&ble_ll_stats + sizeof(struct stats_hdr), 0, sizeof(struct stats_ble_ll_stats) - sizeof(struct stats_hdr)); #ifdef BLE_LL_LOG g_ble_ll_log_index = 0; memset(&g_ble_ll_log, 0, sizeof(g_ble_ll_log)); #endif /* Reset connection module */ ble_ll_conn_module_reset(); /* All this does is re-initialize the event masks so call the hci init */ ble_ll_hci_init(); /* Set state to standby */ ble_ll_state_set(BLE_LL_STATE_STANDBY); /* Reset our random address */ memset(g_random_addr, 0, BLE_DEV_ADDR_LEN); /* Clear the whitelist */ ble_ll_whitelist_clear(); /* Reset resolving list */ #if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1) ble_ll_resolv_list_reset(); #endif /* Re-initialize the PHY */ rc = ble_phy_init(); return rc; }
int ble_phy_rx(void) { /* Check radio state */ nrf52_wait_disabled(); if (NRF_RADIO->STATE != RADIO_STATE_STATE_Disabled) { ble_phy_disable(); ++g_ble_phy_stats.radio_state_errs; return BLE_PHY_ERR_RADIO_STATE; } /* If no pdu, get one */ if (ble_phy_rxpdu_get() == NULL) { return BLE_PHY_ERR_NO_BUFS; } /* Set packet pointer */ NRF_RADIO->PACKETPTR = (uint32_t)g_ble_phy_data.rxpdu->om_data; /* Make sure all interrupts are disabled */ NRF_RADIO->INTENCLR = NRF52_RADIO_IRQ_MASK_ALL; /* Clear events prior to enabling receive */ NRF_RADIO->EVENTS_END = 0; NRF_RADIO->EVENTS_ADDRESS = 0; NRF_RADIO->EVENTS_DISABLED = 0; NRF_RADIO->EVENTS_BCMATCH = 0; NRF_RADIO->EVENTS_RSSIEND = 0; NRF_RADIO->EVENTS_DEVMATCH = 0; /* I want to know when 1st byte received (after address) */ NRF_RADIO->BCC = 8; /* in bits */ 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; /* Reset the rx started flag. Used for the wait for response */ g_ble_phy_data.phy_rx_started = 0; /* Start the receive task in the radio */ NRF_RADIO->TASKS_RXEN = 1; g_ble_phy_data.phy_state = BLE_PHY_STATE_RX; return 0; }
/** * Called to set the start time of a transmission. * * This function is called to set the start time when we are not going from * rx to tx automatically. * * NOTE: care must be taken when calling this function. The channel should * already be set. * * @param cputime * * @return int */ int ble_phy_tx_set_start_time(uint32_t cputime) { int rc; NRF_TIMER0->CC[0] = cputime; NRF_PPI->CHENSET = PPI_CHEN_CH20_Msk; NRF_PPI->CHENCLR = PPI_CHEN_CH21_Msk; if ((int32_t)(os_cputime_get32() - cputime) >= 0) { STATS_INC(ble_phy_stats, tx_late); ble_phy_disable(); rc = BLE_PHY_ERR_TX_LATE; } else { rc = 0; } return rc; }
/** * This is the scheduler callback (called from interrupt context) which * transmits an advertisement. * * Context: Interrupt (scheduler) * * @param sch * * @return int */ static int ble_ll_adv_tx_start_cb(struct ble_ll_sched_item *sch) { int rc; uint8_t end_trans; uint32_t txstart; struct ble_ll_adv_sm *advsm; struct os_mbuf *adv_pdu; /* Get the state machine for the event */ advsm = (struct ble_ll_adv_sm *)sch->cb_arg; /* Set channel */ rc = ble_phy_setchan(advsm->adv_chan, 0, 0); assert(rc == 0); /* Set transmit start time. */ txstart = sch->start_time + XCVR_PROC_DELAY_USECS; rc = ble_phy_tx_set_start_time(txstart); if (rc) { STATS_INC(ble_ll_stats, adv_late_starts); goto adv_tx_done; } #if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) == 1) /* XXX: automatically do this in the phy based on channel? */ ble_phy_encrypt_disable(); #endif #if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1) advsm->adv_rpa_index = -1; if (ble_ll_resolv_enabled()) { ble_phy_resolv_list_enable(); } else { ble_phy_resolv_list_disable(); } #endif /* Set phy mode based on type of advertisement */ if (advsm->adv_type == BLE_HCI_ADV_TYPE_ADV_NONCONN_IND) { end_trans = BLE_PHY_TRANSITION_NONE; ble_phy_set_txend_cb(ble_ll_adv_tx_done, advsm); } else { end_trans = BLE_PHY_TRANSITION_TX_RX; ble_phy_set_txend_cb(NULL, NULL); } /* Get an advertising mbuf (packet header) */ adv_pdu = os_msys_get_pkthdr(BLE_ADV_MAX_PKT_LEN, sizeof(struct ble_mbuf_hdr)); if (!adv_pdu) { ble_phy_disable(); goto adv_tx_done; } ble_ll_adv_pdu_make(advsm, adv_pdu); /* Transmit advertisement */ rc = ble_phy_tx(adv_pdu, end_trans); os_mbuf_free_chain(adv_pdu); if (rc) { goto adv_tx_done; } /* Enable/disable whitelisting based on filter policy */ if (advsm->adv_filter_policy != BLE_HCI_ADV_FILT_NONE) { ble_ll_whitelist_enable(); } else { ble_ll_whitelist_disable(); } /* Set link layer state to advertising */ ble_ll_state_set(BLE_LL_STATE_ADV); /* Count # of adv. sent */ STATS_INC(ble_ll_stats, adv_txg); return BLE_LL_SCHED_STATE_RUNNING; adv_tx_done: ble_ll_adv_tx_done(advsm); return BLE_LL_SCHED_STATE_DONE; }
/** * Called when the LL wait for response timer expires while in the advertising * state. Disables the phy and * */ void ble_ll_adv_wfr_timer_exp(void) { ble_phy_disable(); ble_ll_adv_tx_done(&g_ble_ll_adv_sm); }
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; }
static void ble_phy_rx_start_isr(void) { int rc; uint32_t state; struct ble_mbuf_hdr *ble_hdr; /* Clear events and clear interrupt */ NRF_RADIO->EVENTS_ADDRESS = 0; NRF_RADIO->INTENCLR = RADIO_INTENCLR_ADDRESS_Msk; /* 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 = NRF_RADIO_IRQ_MASK_ALL; NRF_RADIO->SHORTS = 0; return; } } /* Initialize flags, channel and state in ble header at rx start */ ble_hdr = &g_ble_phy_data.rxhdr; ble_hdr->rxinfo.flags = ble_ll_state_get(); ble_hdr->rxinfo.channel = g_ble_phy_data.phy_chan; ble_hdr->rxinfo.handle = 0; ble_hdr->beg_cputime = NRF_TIMER0->CC[1] - os_cputime_usecs_to_ticks(BLE_TX_LEN_USECS_M(NRF_RX_START_OFFSET)); /* Call Link Layer receive start function */ rc = ble_ll_rx_start((uint8_t *)&g_ble_phy_rx_buf[0] + 3, g_ble_phy_data.phy_chan, &g_ble_phy_data.rxhdr); if (rc >= 0) { /* Set rx started flag and enable rx end ISR */ g_ble_phy_data.phy_rx_started = 1; NRF_RADIO->INTENSET = RADIO_INTENSET_END_Msk; #if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1) /* Must start aar if we need to */ if (g_ble_phy_data.phy_privacy) { NRF_RADIO->EVENTS_BCMATCH = 0; NRF_PPI->CHENSET = PPI_CHEN_CH23_Msk; NRF_RADIO->BCC = (BLE_DEV_ADDR_LEN + BLE_LL_PDU_HDR_LEN) * 8; } #endif } else { /* Disable PHY */ ble_phy_disable(); STATS_INC(ble_phy_stats, rx_aborts); } /* Count rx starts */ STATS_INC(ble_phy_stats, rx_starts); }
static void ble_phy_rx_end_isr(void) { int rc; uint8_t *dptr; uint8_t crcok; struct ble_mbuf_hdr *ble_hdr; /* Clear events and clear interrupt */ NRF_RADIO->EVENTS_END = 0; NRF_RADIO->INTENCLR = RADIO_INTENCLR_END_Msk; /* Disable automatic RXEN */ NRF_PPI->CHENCLR = PPI_CHEN_CH21_Msk; /* Set RSSI and CRC status flag in header */ ble_hdr = &g_ble_phy_data.rxhdr; assert(NRF_RADIO->EVENTS_RSSIEND != 0); ble_hdr->rxinfo.rssi = -1 * NRF_RADIO->RSSISAMPLE; dptr = (uint8_t *)&g_ble_phy_rx_buf[0]; dptr += 3; /* Count PHY crc errors and valid packets */ crcok = (uint8_t)NRF_RADIO->CRCSTATUS; if (!crcok) { STATS_INC(ble_phy_stats, rx_crc_err); } else { STATS_INC(ble_phy_stats, rx_valid); ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_CRC_OK; #if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) == 1) if (g_ble_phy_data.phy_encrypted) { /* Only set MIC failure flag if frame is not zero length */ if ((dptr[1] != 0) && (NRF_CCM->MICSTATUS == 0)) { ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_MIC_FAILURE; } /* * XXX: not sure how to deal with this. This should not * be a MIC failure but we should not hand it up. I guess * this is just some form of rx error and that is how we * handle it? For now, just set CRC error flags */ if (NRF_CCM->EVENTS_ERROR) { STATS_INC(ble_phy_stats, rx_hw_err); ble_hdr->rxinfo.flags &= ~BLE_MBUF_HDR_F_CRC_OK; } /* * XXX: This is a total hack work-around for now but I dont * know what else to do. If ENDCRYPT is not set and we are * encrypted we need to not trust this frame and drop it. */ if (NRF_CCM->EVENTS_ENDCRYPT == 0) { STATS_INC(ble_phy_stats, rx_hw_err); ble_hdr->rxinfo.flags &= ~BLE_MBUF_HDR_F_CRC_OK; } } #endif } /* * XXX: This is a horrible ugly hack to deal with the RAM S1 byte * that is not sent over the air but is present here. Simply move the * data pointer to deal with it. Fix this later. */ dptr[2] = dptr[1]; dptr[1] = dptr[0]; rc = ble_ll_rx_end(dptr + 1, ble_hdr); if (rc < 0) { ble_phy_disable(); } }
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; }