static void ble_ll_adv_set_sched(struct ble_ll_adv_sm *advsm, int sched_new) { uint32_t max_usecs; struct ble_ll_sched_item *sch; sch = &advsm->adv_sch; sch->cb_arg = advsm; sch->sched_cb = ble_ll_adv_tx_start_cb; sch->sched_type = BLE_LL_SCHED_TYPE_ADV; /* Set end time to maximum time this schedule item may take */ max_usecs = BLE_TX_DUR_USECS_M(advsm->adv_pdu_len); switch (advsm->adv_type) { case BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_LD: case BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD: max_usecs += BLE_LL_ADV_DIRECT_SCHED_MAX_USECS; break; case BLE_HCI_ADV_TYPE_ADV_IND: case BLE_HCI_ADV_TYPE_ADV_SCAN_IND: max_usecs += BLE_LL_ADV_SCHED_MAX_USECS; break; default: break; } /* * XXX: For now, just schedule some additional time so we insure we have * enough time to do everything we want. */ max_usecs += XCVR_PROC_DELAY_USECS; if (sched_new) { /* * We have to add the scheduling delay and tx start delay to the max * time of the event since the pdu does not start at the scheduled start. */ max_usecs += XCVR_TX_SCHED_DELAY_USECS; sch->start_time = cputime_get32(); sch->end_time = sch->start_time + cputime_usecs_to_ticks(max_usecs); } else { sch->start_time = advsm->adv_pdu_start_time - cputime_usecs_to_ticks(XCVR_TX_SCHED_DELAY_USECS); sch->end_time = advsm->adv_pdu_start_time + cputime_usecs_to_ticks(max_usecs); } }
void ble_ll_adv_scheduled(uint32_t sch_start) { struct ble_ll_adv_sm *advsm; advsm = &g_ble_ll_adv_sm; /* The event start time is when we start transmission of the adv PDU */ advsm->adv_event_start_time = sch_start + cputime_usecs_to_ticks(XCVR_TX_SCHED_DELAY_USECS); advsm->adv_pdu_start_time = advsm->adv_event_start_time; /* * Set the time at which we must end directed, high-duty cycle advertising. * Does not matter that we calculate this value if we are not doing high * duty cycle advertising. */ advsm->adv_dir_hd_end_time = advsm->adv_event_start_time + cputime_usecs_to_ticks(BLE_LL_ADV_STATE_HD_MAX * 1000); }
/** * Called from interrupt context when the transmit ends * */ static void ble_phy_tx_end_isr(void) { uint8_t was_encrypted; uint8_t transition; uint8_t txlen; uint32_t wfr_time; uint32_t txstart; /* * Read captured tx start time. This is not the actual transmit start * time but it is the time at which the address event occurred * (after transmission of access address) */ txstart = NRF_TIMER0->CC[1]; /* If this transmission was encrypted we need to remember it */ was_encrypted = g_ble_phy_data.phy_encrypted; /* Better be in TX state! */ assert(g_ble_phy_data.phy_state == BLE_PHY_STATE_TX); /* Log the event */ ble_ll_log(BLE_LL_LOG_ID_PHY_TXEND, (g_ble_phy_tx_buf[0] >> 8) & 0xFF, was_encrypted, txstart); /* 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; wfr_time = NRF_RADIO->SHORTS; #if (BLE_LL_CFG_FEAT_LE_ENCRYPTION == 1) /* * XXX: not sure what to do. We had a HW error during transmission. * For now I just count a stat but continue on like all is good. */ if (was_encrypted) { if (NRF_CCM->EVENTS_ERROR) { STATS_INC(ble_phy_stats, tx_hw_err); NRF_CCM->EVENTS_ERROR = 0; } } #endif /* Call transmit end callback */ if (g_ble_phy_data.txend_cb) { g_ble_phy_data.txend_cb(g_ble_phy_data.txend_arg); } transition = g_ble_phy_data.phy_transition; if (transition == BLE_PHY_TRANSITION_TX_RX) { /* Packet pointer needs to be reset. */ ble_phy_rx_xcvr_setup(); /* * Enable the wait for response timer. Note that cc #1 on * timer 0 contains the transmit start time */ txlen = g_ble_phy_data.phy_tx_pyld_len; if (txlen && was_encrypted) { txlen += BLE_LL_DATA_MIC_LEN; } wfr_time = txstart - BLE_TX_LEN_USECS_M(NRF_RX_START_OFFSET); wfr_time += BLE_TX_DUR_USECS_M(txlen); wfr_time += cputime_usecs_to_ticks(BLE_LL_WFR_USECS); ble_ll_wfr_enable(wfr_time); } else { /* Disable automatic TXEN */ NRF_PPI->CHENCLR = PPI_CHEN_CH20_Msk; assert(transition == BLE_PHY_TRANSITION_NONE); } }
/** * Called when an advertising event is over. * * Context: Link Layer task. * * @param arg Pointer to advertising state machine. */ void ble_ll_adv_event_done(void *arg) { uint8_t mask; uint8_t final_adv_chan; int32_t delta_t; uint32_t itvl; uint32_t start_time; struct ble_ll_adv_sm *advsm; /* Stop advertising event */ advsm = (struct ble_ll_adv_sm *)arg; assert(advsm->enabled); /* Remove the element from the schedule if it is still there. */ ble_ll_sched_rmv_elem(&advsm->adv_sch); os_eventq_remove(&g_ble_ll_data.ll_evq, &advsm->adv_txdone_ev); /* * Check if we have ended our advertising event. If our last advertising * packet was sent on the last channel, it means we are done with this * event. */ if (advsm->adv_chanmask & 0x04) { final_adv_chan = BLE_PHY_ADV_CHAN_START + 2; } else if (advsm->adv_chanmask & 0x02) { final_adv_chan = BLE_PHY_ADV_CHAN_START + 1; } else { final_adv_chan = BLE_PHY_ADV_CHAN_START; } if (advsm->adv_chan == final_adv_chan) { /* Check if we need to resume scanning */ ble_ll_scan_chk_resume(); /* This event is over. Set adv channel to first one */ advsm->adv_chan = ble_ll_adv_first_chan(advsm); /* Calculate start time of next advertising event */ itvl = advsm->adv_itvl_usecs; if (advsm->adv_type != BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD) { itvl += rand() % (BLE_LL_ADV_DELAY_MS_MAX * 1000); } advsm->adv_event_start_time += cputime_usecs_to_ticks(itvl); advsm->adv_pdu_start_time = advsm->adv_event_start_time; /* * The scheduled time better be in the future! If it is not, we will * just keep advancing until we the time is in the future */ start_time = advsm->adv_pdu_start_time - cputime_usecs_to_ticks(XCVR_TX_SCHED_DELAY_USECS); delta_t = (int32_t)(start_time - cputime_get32()); if (delta_t < 0) { /* Calculate start time of next advertising event */ while (delta_t < 0) { itvl = advsm->adv_itvl_usecs; if (advsm->adv_type != BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD) { itvl += rand() % (BLE_LL_ADV_DELAY_MS_MAX * 1000); } itvl = cputime_usecs_to_ticks(itvl); advsm->adv_event_start_time += itvl; advsm->adv_pdu_start_time = advsm->adv_event_start_time; delta_t += (int32_t)itvl; } } } else { /* * Move to next advertising channel. If not in the mask, just * increment by 1. We can do this because we already checked if we * just transmitted on the last advertising channel */ ++advsm->adv_chan; mask = 1 << (advsm->adv_chan - BLE_PHY_ADV_CHAN_START); if ((mask & advsm->adv_chanmask) == 0) { ++advsm->adv_chan; } /* * We will transmit right away. Set next pdu start time to now * plus a xcvr start delay just so we dont count late adv starts */ advsm->adv_pdu_start_time = cputime_get32() + cputime_usecs_to_ticks(XCVR_TX_SCHED_DELAY_USECS); } /* * Stop high duty cycle directed advertising if we have been doing * it for more than 1.28 seconds */ if (advsm->adv_type == BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD) { if (advsm->adv_pdu_start_time >= advsm->adv_dir_hd_end_time) { /* Disable advertising */ advsm->enabled = 0; ble_ll_conn_comp_event_send(NULL, BLE_ERR_DIR_ADV_TMO); ble_ll_scan_chk_resume(); return; } } /* Schedule advertising transmit */ ble_ll_adv_set_sched(advsm, 0); /* * In the unlikely event we cant reschedule this, just post a done * event and we will reschedule the next advertising event */ if (ble_ll_sched_adv_reschedule(&advsm->adv_sch)) { os_eventq_put(&g_ble_ll_data.ll_evq, &advsm->adv_txdone_ev); } }
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; }