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);
}
Example #3
0
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);
    }
}
Example #5
0
/**
 * 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);
    }
}