/** Callback function for the "interval" LL timer */ static void t_ll_interval_cb(void) { switch(current_state) { case LL_STATE_ADVERTISING: adv_ch_idx = first_adv_ch_idx(); t_ll_single_shot_cb(); break; case LL_STATE_SCANNING: if(!inc_adv_ch_idx()) adv_ch_idx = first_adv_ch_idx(); radio_prepare(adv_chs[adv_ch_idx], LL_ACCESS_ADDRESS_ADV, LL_CRCINIT_ADV); radio_recv(0); timer_start(t_ll_single_shot, t_scan_window); break; case LL_STATE_INITIATING: case LL_STATE_CONNECTION: /* Not implemented */ case LL_STATE_STANDBY: default: /* Nothing to do */ return; } }
/** Callback function for the "single shot" LL timer */ static void t_ll_single_shot_cb(void) { switch(current_state) { case LL_STATE_ADVERTISING: radio_stop(); radio_prepare(adv_chs[adv_ch_idx], LL_ACCESS_ADDRESS_ADV, LL_CRCINIT_ADV); radio_send((uint8_t *) &pdu_adv, rx ? RADIO_FLAGS_RX_NEXT : 0); prev_adv_ch_idx = adv_ch_idx; if (!inc_adv_ch_idx()) timer_start(t_ll_single_shot, t_adv_pdu_interval); break; case LL_STATE_SCANNING: /* Called at the end of the scan window */ radio_stop(); break; case LL_STATE_INITIATING: case LL_STATE_CONNECTION: /* Not implemented */ case LL_STATE_STANDBY: default: /* Nothing to do */ return; } }
/**@brief Function for handling vendor specific commands. * Used when packet type is set to Vendor specific. * The length field is used for encoding vendor specific command. * The frequency field is used for encoding vendor specific options to the command. * * @param[in] vendor_cmd Vendor specific command to be executed. * @param[in] vendor_option Vendor specific option to the vendor command. * * @return DTM_SUCCESS or one of the DTM_ERROR_ values */ static uint32_t dtm_vendor_specific_pkt(uint32_t vendor_cmd, dtm_freq_t vendor_option) { switch (vendor_cmd) { // nRFgo Studio uses CARRIER_TEST_STUDIO to indicate a continuous carrier without // a modulated signal. case CARRIER_TEST: case CARRIER_TEST_STUDIO: // Not a packet type, but used to indicate that a continuous carrier signal // should be transmitted by the radio. radio_prepare(TX_MODE); dtm_constant_carrier(); // Shortcut between READY event and START task NRF_RADIO->SHORTS = 1 << RADIO_SHORTS_READY_START_Pos; // Shortcut will start radio in Tx mode when it is ready NRF_RADIO->TASKS_TXEN = 1; m_state = STATE_CARRIER_TEST; break; case SET_TX_POWER: if (!dtm_set_txpower(vendor_option)) { return DTM_ERROR_ILLEGAL_CONFIGURATION; } break; case SELECT_TIMER: if (!dtm_set_timer(vendor_option)) { return DTM_ERROR_ILLEGAL_CONFIGURATION; } break; } // Event code is unchanged, successful return DTM_SUCCESS; }
uint32_t dtm_cmd(dtm_cmd_t cmd, dtm_freq_t freq, uint32_t length, dtm_pkt_type_t payload) { // Save specified packet in static variable for tx/rx functions to use. // Note that BLE conformance testers always use full length packets. m_packet_length = (m_packet_length & 0xC0) | ((uint8_t)length & 0x3F); m_packet_type = payload; m_phys_ch = freq; // Clean out any non-retrieved event that might linger from an earlier test m_new_event = true; // Set default event; any error will set it to LE_TEST_STATUS_EVENT_ERROR m_event = LE_TEST_STATUS_EVENT_SUCCESS; if (m_state == STATE_UNINITIALIZED) { // Application has not explicitly initialized DTM, return DTM_ERROR_UNINITIALIZED; } if (cmd == LE_RESET) { // Note that timer will continue running after a reset dtm_test_done(); if (freq == 0x01) { m_packet_length = length << 6; } else { m_packet_length = 0; } return DTM_SUCCESS; } if (cmd == LE_TEST_END) { if (m_state == STATE_IDLE) { // Sequencing error - only rx or tx test may be ended! m_event = LE_TEST_STATUS_EVENT_ERROR; return DTM_ERROR_INVALID_STATE; } m_event = LE_PACKET_REPORTING_EVENT | m_rx_pkt_count; dtm_test_done(); return DTM_SUCCESS; } if (m_state != STATE_IDLE) { // Sequencing error - only TEST_END/RESET are legal while test is running // Note: State is unchanged; ongoing test not affected m_event = LE_TEST_STATUS_EVENT_ERROR; return DTM_ERROR_INVALID_STATE; } // Check for illegal values of m_phys_ch. Skip the check if the packet is vendor spesific. if (payload != DTM_PKT_VENDORSPECIFIC && m_phys_ch > PHYS_CH_MAX) { // Parameter error // Note: State is unchanged; ongoing test not affected m_event = LE_TEST_STATUS_EVENT_ERROR; return DTM_ERROR_ILLEGAL_CHANNEL; } m_rx_pkt_count = 0; if (cmd == LE_RECEIVER_TEST) { // Zero fill all pdu fields to avoid stray data from earlier test run memset(&m_pdu, 0, DTM_PDU_MAX_MEMORY_SIZE); radio_prepare(RX_MODE); // Reinitialize "everything"; RF interrupts OFF m_state = STATE_RECEIVER_TEST; return DTM_SUCCESS; } if (cmd == LE_TRANSMITTER_TEST) { // Check for illegal values of m_packet_length. Skip the check if the packet is vendor spesific. if (payload != DTM_PKT_VENDORSPECIFIC && m_packet_length > DTM_PAYLOAD_MAX_SIZE) { // Parameter error m_event = LE_TEST_STATUS_EVENT_ERROR; return DTM_ERROR_ILLEGAL_LENGTH; } // Note that PDU uses 4 bits even though BLE DTM uses only 2 (the HCI SDU uses all 4) m_pdu.content[DTM_HEADER_OFFSET] = ((uint8_t)m_packet_type & 0x0F); m_pdu.content[DTM_LENGTH_OFFSET] = m_packet_length; switch (m_packet_type) { case DTM_PKT_PRBS9: // Non-repeated, must copy entire pattern to PDU memcpy(m_pdu.content + DTM_HEADER_SIZE, m_prbs_content, m_packet_length); break; case DTM_PKT_0X0F: // Bit pattern 00001111 repeated memset(m_pdu.content + DTM_HEADER_SIZE, RFPHY_TEST_0X0F_REF_PATTERN, m_packet_length); break; case DTM_PKT_0X55: // Bit pattern 01010101 repeated memset(m_pdu.content + DTM_HEADER_SIZE, RFPHY_TEST_0X55_REF_PATTERN, m_packet_length); break; case DTM_PKT_VENDORSPECIFIC: // The length field is for indicating the vendor specific command to execute. // The frequency field is used for vendor specific options to the command. return dtm_vendor_specific_pkt(length, freq); default: // Parameter error m_event = LE_TEST_STATUS_EVENT_ERROR; return DTM_ERROR_ILLEGAL_CONFIGURATION; } // Initialize CRC value, set channel: radio_prepare(TX_MODE); // Set the timer to the correct period. The delay between each packet is described in the // Bluetooth Core Spsification version 4.2 Vol. 6 Part F Section 4.1.6. if ((m_packet_length + DTM_ON_AIR_OVERHEAD_SIZE ) * 8 <= 376) { mp_timer->CC[0] = 625; // 625uS with 1MHz clock to the timer } else if ((m_packet_length + DTM_ON_AIR_OVERHEAD_SIZE ) * 8 <= 1000) { mp_timer->CC[0] = 1250; // 625uS with 1MHz clock to the timer } else if ((m_packet_length + DTM_ON_AIR_OVERHEAD_SIZE ) * 8 <= 1624) { mp_timer->CC[0] = 1875; // 625uS with 1MHz clock to the timer } else { mp_timer->CC[0] = 2500; // 625uS with 1MHz clock to the timer } // Configure PPI so that timer will activate radio every 625 us NRF_PPI->CH[0].EEP = (uint32_t)&mp_timer->EVENTS_COMPARE[0]; NRF_PPI->CH[0].TEP = (uint32_t)&NRF_RADIO->TASKS_TXEN; NRF_PPI->CHENSET = 0x01; m_state = STATE_TRANSMITTER_TEST; } return DTM_SUCCESS; }