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 = os_cputime_get32(); sch->end_time = sch->start_time + os_cputime_usecs_to_ticks(max_usecs); } else { sch->start_time = advsm->adv_pdu_start_time - os_cputime_usecs_to_ticks(XCVR_TX_SCHED_DELAY_USECS); sch->end_time = advsm->adv_pdu_start_time + os_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 + os_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 + os_cputime_usecs_to_ticks(BLE_LL_ADV_STATE_HD_MAX * 1000); }
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); }
/** * Called when an advertising event is over. * * Context: Link Layer task. * * @param arg Pointer to advertising state machine. */ static void ble_ll_adv_done(struct ble_ll_adv_sm *advsm) { uint8_t mask; uint8_t final_adv_chan; int32_t delta_t; uint32_t itvl; uint32_t start_time; 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 += os_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 - os_cputime_usecs_to_ticks(XCVR_TX_SCHED_DELAY_USECS); delta_t = (int32_t)(start_time - os_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 = os_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 = os_cputime_get32() + os_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, advsm->conn_comp_ev); advsm->conn_comp_ev = NULL; ble_ll_scan_chk_resume(); return; } } /* We need to regenerate our RPA's if we have passed timeout */ #if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1) ble_ll_adv_chk_rpa_timeout(advsm); #endif /* 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); } }
/** * 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_data.phy_tx_pyld_len, 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 (MYNEWT_VAL(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 = BLE_LL_WFR_USECS - BLE_TX_LEN_USECS_M(NRF_RX_START_OFFSET); wfr_time += BLE_TX_DUR_USECS_M(txlen); wfr_time = os_cputime_usecs_to_ticks(wfr_time); ble_ll_wfr_enable(txstart + wfr_time); } else { /* Disable automatic TXEN */ NRF_PPI->CHENCLR = PPI_CHEN_CH20_Msk; assert(transition == BLE_PHY_TRANSITION_NONE); } }