zephyr/subsys/bluetooth/controller/hal/radio.c
Vinayak Chettimada 6de7a808af Bluetooth: Controller: ctrl pdu processing based on available CPU time
On CPUs like nRF51 which run at 16MHz, certain BLE control
procedure PDU processing take more CPU time than permitted
inside tIFS (150us). Current implementation of Data Length
Update procedure does not span over multiple connection
interval (unlike Encryption Setup, which is another control
procedure processing that would consume more CPU time)
hence taking more CPU time inside tIFS on nRF51.

During the radio ISR, the active clock and packet timer are
active and it is used to profile the CPU time taken which
is used to decide on whether there is sufficient time in
the current radio event to process the control packet.

This commit also fixes a potential bug that would cause
disconnection due to MIC failure on encrypted connections
that performed Data Length Update. Controller used to NACK
the request/response PDU if it was not in a state to resize
the receive buffers but did not reset the CCM counter. This
is now fixed by the change done to NACK control PDU based
on available CPU time in radio ISR.

Change-id: Id58322ad76a0dbc284738cdd9a7c0437c9e8c423
Signed-off-by: Vinayak Chettimada <vinayak.kariappa.chettimada@nordicsemi.no>
2016-12-16 10:27:48 +02:00

568 lines
13 KiB
C

/*
* Copyright (c) 2016 Nordic Semiconductor ASA
* Copyright (c) 2016 Vinayak Kariappa Chettimada
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <soc.h>
#include <misc/util.h>
#include "hal_work.h"
#include "defines.h"
#include "ccm.h"
#include "radio.h"
#if defined(CONFIG_SOC_SERIES_NRF51X)
#define RADIO_PDU_LEN_MAX (BIT(5) - 1)
#elif defined(NRF52)
#define RADIO_PDU_LEN_MAX (BIT(8) - 1)
#else
#error "Platform not defined."
#endif
static radio_isr_fp sfp_radio_isr;
void radio_isr(void)
{
if (sfp_radio_isr) {
sfp_radio_isr();
}
}
void radio_isr_set(radio_isr_fp fp_radio_isr)
{
sfp_radio_isr = fp_radio_isr; /* atomic assignment of 32-bit word */
NRF_RADIO->INTENSET = (0 |
/* RADIO_INTENSET_READY_Msk |
* RADIO_INTENSET_ADDRESS_Msk |
* RADIO_INTENSET_PAYLOAD_Msk |
* RADIO_INTENSET_END_Msk |
*/
RADIO_INTENSET_DISABLED_Msk
/* | RADIO_INTENSET_RSSIEND_Msk |
*/
);
_NvicIrqUnpend(RADIO_IRQn);
irq_enable(RADIO_IRQn);
}
void radio_reset(void)
{
irq_disable(RADIO_IRQn);
NRF_RADIO->POWER =
((RADIO_POWER_POWER_Disabled << RADIO_POWER_POWER_Pos) &
RADIO_POWER_POWER_Msk);
NRF_RADIO->POWER =
((RADIO_POWER_POWER_Enabled << RADIO_POWER_POWER_Pos) &
RADIO_POWER_POWER_Msk);
}
void radio_phy_set(uint8_t phy)
{
NRF_RADIO->MODE =
(((phy) ? (uint32_t)phy : RADIO_MODE_MODE_Ble_1Mbit) <<
RADIO_MODE_MODE_Pos) & RADIO_MODE_MODE_Msk;
}
void radio_tx_power_set(uint32_t power)
{
/* TODO map power to h/w values. */
NRF_RADIO->TXPOWER = power;
}
void radio_freq_chnl_set(uint32_t chnl)
{
NRF_RADIO->FREQUENCY = chnl;
}
void radio_whiten_iv_set(uint32_t iv)
{
NRF_RADIO->DATAWHITEIV = iv;
}
void radio_aa_set(uint8_t *aa)
{
NRF_RADIO->TXADDRESS =
(((0UL) << RADIO_TXADDRESS_TXADDRESS_Pos) &
RADIO_TXADDRESS_TXADDRESS_Msk);
NRF_RADIO->RXADDRESSES =
((RADIO_RXADDRESSES_ADDR0_Enabled) << RADIO_RXADDRESSES_ADDR0_Pos);
NRF_RADIO->PREFIX0 = aa[3];
NRF_RADIO->BASE0 = (aa[2] << 24) | (aa[1] << 16) | (aa[0] << 8);
}
void radio_pkt_configure(uint8_t preamble16, uint8_t bits_len, uint8_t max_len)
{
#if defined(CONFIG_SOC_SERIES_NRF51X)
ARG_UNUSED(preamble16);
if (bits_len == 8) {
bits_len = 5;
}
#endif
NRF_RADIO->PCNF0 = ((((1UL) << RADIO_PCNF0_S0LEN_Pos) &
RADIO_PCNF0_S0LEN_Msk) |
((((uint32_t)bits_len) << RADIO_PCNF0_LFLEN_Pos) &
RADIO_PCNF0_LFLEN_Msk) |
#if !defined(CONFIG_SOC_SERIES_NRF51X)
(((RADIO_PCNF0_S1INCL_Include) <<
RADIO_PCNF0_S1INCL_Pos) &
RADIO_PCNF0_S1INCL_Msk) |
((((preamble16) ? RADIO_PCNF0_PLEN_16bit :
RADIO_PCNF0_PLEN_8bit) << RADIO_PCNF0_PLEN_Pos) &
RADIO_PCNF0_PLEN_Msk) |
#endif
((((uint32_t)8-bits_len) <<
RADIO_PCNF0_S1LEN_Pos) &
RADIO_PCNF0_S1LEN_Msk));
NRF_RADIO->PCNF1 = (((((uint32_t)max_len) << RADIO_PCNF1_MAXLEN_Pos) &
RADIO_PCNF1_MAXLEN_Msk) |
(((0UL) << RADIO_PCNF1_STATLEN_Pos) &
RADIO_PCNF1_STATLEN_Msk) |
(((3UL) << RADIO_PCNF1_BALEN_Pos) &
RADIO_PCNF1_BALEN_Msk) |
(((RADIO_PCNF1_ENDIAN_Little) <<
RADIO_PCNF1_ENDIAN_Pos) &
RADIO_PCNF1_ENDIAN_Msk) |
(((1UL) << RADIO_PCNF1_WHITEEN_Pos) &
RADIO_PCNF1_WHITEEN_Msk));
}
void radio_pkt_rx_set(void *rx_packet)
{
NRF_RADIO->PACKETPTR = (uint32_t)rx_packet;
}
void radio_pkt_tx_set(void *tx_packet)
{
NRF_RADIO->PACKETPTR = (uint32_t)tx_packet;
}
void radio_rx_enable(void)
{
NRF_RADIO->TASKS_RXEN = 1;
}
void radio_tx_enable(void)
{
NRF_RADIO->TASKS_TXEN = 1;
}
void radio_disable(void)
{
NRF_RADIO->SHORTS = 0;
NRF_RADIO->TASKS_DISABLE = 1;
}
void radio_status_reset(void)
{
NRF_RADIO->EVENTS_READY = 0;
NRF_RADIO->EVENTS_ADDRESS = 0;
NRF_RADIO->EVENTS_PAYLOAD = 0;
NRF_RADIO->EVENTS_END = 0;
NRF_RADIO->EVENTS_DISABLED = 0;
}
uint32_t radio_is_ready(void)
{
return NRF_RADIO->EVENTS_READY;
}
uint32_t radio_is_done(void)
{
return NRF_RADIO->EVENTS_END;
}
uint32_t radio_has_disabled(void)
{
return NRF_RADIO->EVENTS_DISABLED;
}
uint32_t radio_is_idle(void)
{
return (NRF_RADIO->STATE == 0);
}
void radio_crc_configure(uint32_t polynomial, uint32_t iv)
{
NRF_RADIO->CRCCNF =
(((RADIO_CRCCNF_SKIPADDR_Skip) << RADIO_CRCCNF_SKIPADDR_Pos) &
RADIO_CRCCNF_SKIPADDR_Msk) |
(((RADIO_CRCCNF_LEN_Three) << RADIO_CRCCNF_LEN_Pos) &
RADIO_CRCCNF_LEN_Msk);
NRF_RADIO->CRCPOLY = polynomial;
NRF_RADIO->CRCINIT = iv;
}
uint32_t radio_crc_is_valid(void)
{
return NRF_RADIO->CRCSTATUS;
}
static uint8_t ALIGNED(4) _pkt_empty[RADIO_EMPDU_SIZE_MAX];
static uint8_t ALIGNED(4) _pkt_scratch[
((RADIO_PDU_LEN_MAX + 3) > RADIO_ACPDU_SIZE_MAX) ?
(RADIO_PDU_LEN_MAX + 3) : RADIO_ACPDU_SIZE_MAX];
void *radio_pkt_empty_get(void)
{
return _pkt_empty;
}
void *radio_pkt_scratch_get(void)
{
return _pkt_scratch;
}
void radio_switch_complete_and_rx(void)
{
NRF_RADIO->SHORTS =
(RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_END_DISABLE_Msk |
RADIO_SHORTS_DISABLED_RXEN_Msk);
}
void radio_switch_complete_and_tx(void)
{
NRF_RADIO->SHORTS =
(RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_END_DISABLE_Msk |
RADIO_SHORTS_DISABLED_TXEN_Msk);
}
void radio_switch_complete_and_disable(void)
{
NRF_RADIO->SHORTS =
(RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_END_DISABLE_Msk);
}
void radio_rssi_measure(void)
{
NRF_RADIO->SHORTS |=
(RADIO_SHORTS_ADDRESS_RSSISTART_Msk |
RADIO_SHORTS_DISABLED_RSSISTOP_Msk);
}
uint32_t radio_rssi_get(void)
{
return NRF_RADIO->RSSISAMPLE;
}
void radio_rssi_status_reset(void)
{
NRF_RADIO->EVENTS_RSSIEND = 0;
}
uint32_t radio_rssi_is_ready(void)
{
return NRF_RADIO->EVENTS_RSSIEND;
}
void radio_filter_configure(uint8_t bitmask_enable,
uint8_t bitmask_addr_type,
uint8_t *bdaddr)
{
uint8_t index;
for (index = 0; index < 8; index++) {
NRF_RADIO->DAB[index] = ((uint32_t)bdaddr[3] << 24) |
((uint32_t)bdaddr[2] << 16) |
((uint32_t)bdaddr[1] << 8) |
bdaddr[0];
NRF_RADIO->DAP[index] = ((uint32_t)bdaddr[5] << 8) | bdaddr[4];
bdaddr += BDADDR_SIZE;
}
NRF_RADIO->DACNF = ((uint32_t)bitmask_addr_type << 8) | bitmask_enable;
}
void radio_filter_disable(void)
{
NRF_RADIO->DACNF &= ~(0x000000FF);
}
void radio_filter_status_reset(void)
{
NRF_RADIO->EVENTS_DEVMATCH = 0;
NRF_RADIO->EVENTS_DEVMISS = 0;
}
uint32_t radio_filter_has_match(void)
{
return NRF_RADIO->EVENTS_DEVMATCH;
}
void radio_bc_configure(uint32_t n)
{
NRF_RADIO->BCC = n;
NRF_RADIO->SHORTS |= RADIO_SHORTS_ADDRESS_BCSTART_Msk;
}
void radio_bc_status_reset(void)
{
NRF_RADIO->EVENTS_BCMATCH = 0;
}
uint32_t radio_bc_has_match(void)
{
return NRF_RADIO->EVENTS_BCMATCH;
}
void radio_tmr_status_reset(void)
{
NRF_RTC0->EVTENCLR = RTC_EVTENCLR_COMPARE2_Msk;
NRF_PPI->CHENCLR =
(PPI_CHEN_CH0_Msk | PPI_CHEN_CH1_Msk | PPI_CHEN_CH2_Msk |
PPI_CHEN_CH3_Msk | PPI_CHEN_CH4_Msk | PPI_CHEN_CH5_Msk |
PPI_CHEN_CH6_Msk | PPI_CHEN_CH7_Msk);
}
void radio_tmr_tifs_set(uint32_t tifs)
{
NRF_RADIO->TIFS = tifs;
}
uint32_t radio_tmr_start(uint8_t trx, uint32_t ticks_start, uint32_t remainder)
{
if ((!(remainder / 1000000UL)) || (remainder & 0x80000000)) {
ticks_start--;
remainder += 30517578UL;
}
remainder /= 1000000UL;
NRF_TIMER0->TASKS_CLEAR = 1;
NRF_TIMER0->MODE = 0;
NRF_TIMER0->PRESCALER = 4;
NRF_TIMER0->BITMODE = 2; /* 24 - bit */
NRF_TIMER0->CC[0] = remainder;
NRF_TIMER0->EVENTS_COMPARE[0] = 0;
NRF_RTC0->CC[2] = ticks_start;
NRF_RTC0->EVTENSET = RTC_EVTENSET_COMPARE2_Msk;
NRF_RTC0->EVENTS_COMPARE[2] = 0;
NRF_PPI->CH[1].EEP = (uint32_t)&(NRF_RTC0->EVENTS_COMPARE[2]);
NRF_PPI->CH[1].TEP = (uint32_t)&(NRF_TIMER0->TASKS_START);
NRF_PPI->CHENSET = PPI_CHEN_CH1_Msk;
if (trx) {
NRF_PPI->CH[0].EEP =
(uint32_t)&(NRF_TIMER0->EVENTS_COMPARE[0]);
NRF_PPI->CH[0].TEP =
(uint32_t)&(NRF_RADIO->TASKS_TXEN);
NRF_PPI->CHENSET = PPI_CHEN_CH0_Msk;
} else {
NRF_PPI->CH[0].EEP =
(uint32_t)&(NRF_TIMER0->EVENTS_COMPARE[0]);
NRF_PPI->CH[0].TEP =
(uint32_t)&(NRF_RADIO->TASKS_RXEN);
NRF_PPI->CHENSET = PPI_CHEN_CH0_Msk;
}
return remainder;
}
void radio_tmr_stop(void)
{
NRF_TIMER0->TASKS_STOP = 1;
NRF_TIMER0->TASKS_SHUTDOWN = 1;
}
void radio_tmr_hcto_configure(uint32_t hcto)
{
NRF_TIMER0->CC[2] = hcto;
NRF_TIMER0->EVENTS_COMPARE[2] = 0;
NRF_PPI->CH[4].EEP = (uint32_t)&(NRF_RADIO->EVENTS_ADDRESS);
NRF_PPI->CH[4].TEP = (uint32_t)&(NRF_TIMER0->TASKS_CAPTURE[2]);
NRF_PPI->CH[5].EEP = (uint32_t)&(NRF_TIMER0->EVENTS_COMPARE[2]);
NRF_PPI->CH[5].TEP = (uint32_t)&(NRF_RADIO->TASKS_DISABLE);
NRF_PPI->CHENSET = (PPI_CHEN_CH4_Msk | PPI_CHEN_CH5_Msk);
}
void radio_tmr_aa_capture(void)
{
NRF_PPI->CH[2].EEP = (uint32_t)&(NRF_RADIO->EVENTS_READY);
NRF_PPI->CH[2].TEP = (uint32_t)&(NRF_TIMER0->TASKS_CAPTURE[0]);
NRF_PPI->CH[3].EEP = (uint32_t)&(NRF_RADIO->EVENTS_ADDRESS);
NRF_PPI->CH[3].TEP = (uint32_t)&(NRF_TIMER0->TASKS_CAPTURE[1]);
NRF_PPI->CHENSET = (PPI_CHEN_CH2_Msk | PPI_CHEN_CH3_Msk);
}
uint32_t radio_tmr_aa_get(void)
{
return (NRF_TIMER0->CC[1] - NRF_TIMER0->CC[0]);
}
void radio_tmr_end_capture(void)
{
NRF_PPI->CH[7].EEP = (uint32_t)&(NRF_RADIO->EVENTS_END);
NRF_PPI->CH[7].TEP = (uint32_t)&(NRF_TIMER0->TASKS_CAPTURE[2]);
NRF_PPI->CHENSET = PPI_CHEN_CH7_Msk;
}
uint32_t radio_tmr_end_get(void)
{
return NRF_TIMER0->CC[2];
}
void radio_tmr_sample(void)
{
NRF_TIMER0->TASKS_CAPTURE[3] = 1;
}
uint32_t radio_tmr_sample_get(void)
{
return NRF_TIMER0->CC[3];
}
static uint8_t ALIGNED(4) _ccm_scratch[(RADIO_PDU_LEN_MAX - 4) + 16];
void *radio_ccm_rx_pkt_set(struct ccm *ccm, void *pkt)
{
NRF_CCM->ENABLE = CCM_ENABLE_ENABLE_Disabled;
NRF_CCM->ENABLE = CCM_ENABLE_ENABLE_Enabled;
NRF_CCM->MODE =
#if !defined(CONFIG_SOC_SERIES_NRF51X)
((CCM_MODE_LENGTH_Extended << CCM_MODE_LENGTH_Pos) &
CCM_MODE_LENGTH_Msk) |
#endif
((CCM_MODE_MODE_Decryption << CCM_MODE_MODE_Pos) &
CCM_MODE_MODE_Msk);
NRF_CCM->CNFPTR = (uint32_t)ccm;
NRF_CCM->INPTR = (uint32_t)_pkt_scratch;
NRF_CCM->OUTPTR = (uint32_t)pkt;
NRF_CCM->SCRATCHPTR = (uint32_t)_ccm_scratch;
NRF_CCM->SHORTS = 0;
NRF_CCM->EVENTS_ENDKSGEN = 0;
NRF_CCM->EVENTS_ENDCRYPT = 0;
NRF_CCM->EVENTS_ERROR = 0;
NRF_PPI->CH[6].EEP = (uint32_t)&(NRF_RADIO->EVENTS_ADDRESS);
NRF_PPI->CH[6].TEP = (uint32_t)&(NRF_CCM->TASKS_CRYPT);
NRF_PPI->CHENSET = PPI_CHEN_CH6_Msk;
NRF_CCM->TASKS_KSGEN = 1;
return _pkt_scratch;
}
void *radio_ccm_tx_pkt_set(struct ccm *ccm, void *pkt)
{
NRF_CCM->ENABLE = CCM_ENABLE_ENABLE_Disabled;
NRF_CCM->ENABLE = CCM_ENABLE_ENABLE_Enabled;
NRF_CCM->MODE =
#if !defined(CONFIG_SOC_SERIES_NRF51X)
((CCM_MODE_LENGTH_Extended << CCM_MODE_LENGTH_Pos) &
CCM_MODE_LENGTH_Msk) |
#endif
((CCM_MODE_MODE_Encryption << CCM_MODE_MODE_Pos) &
CCM_MODE_MODE_Msk);
NRF_CCM->CNFPTR = (uint32_t)ccm;
NRF_CCM->INPTR = (uint32_t)pkt;
NRF_CCM->OUTPTR = (uint32_t)_pkt_scratch;
NRF_CCM->SCRATCHPTR = (uint32_t)_ccm_scratch;
NRF_CCM->SHORTS = CCM_SHORTS_ENDKSGEN_CRYPT_Msk;
NRF_CCM->EVENTS_ENDKSGEN = 0;
NRF_CCM->EVENTS_ENDCRYPT = 0;
NRF_CCM->EVENTS_ERROR = 0;
#if defined(CONFIG_SOC_SERIES_NRF51X)
/* set up PPI to enable CCM */
NRF_PPI->CH[6].EEP = (uint32_t)&(NRF_RADIO->EVENTS_READY);
NRF_PPI->CH[6].TEP = (uint32_t)&(NRF_CCM->TASKS_KSGEN);
NRF_PPI->CHENSET = PPI_CHEN_CH6_Msk;
#elif 0
/* encrypt tx packet */
NRF_CCM->INTENSET = CCM_INTENSET_ENDCRYPT_Msk;
NRF_CCM->TASKS_KSGEN = 1;
while (NRF_CCM->EVENTS_ENDCRYPT == 0) {
__WFE();
__SEV();
__WFE();
}
NRF_CCM->INTENCLR = CCM_INTENCLR_ENDCRYPT_Msk;
_NvicIrqUnpend(CCM_AAR_IRQn);
LL_ASSERT(NRF_CCM->EVENTS_ERROR == 0);
#else
/* start KSGEN early, but dont wait for ENDCRYPT */
NRF_CCM->TASKS_KSGEN = 1;
#endif
return _pkt_scratch;
}
uint32_t radio_ccm_is_done(void)
{
NRF_CCM->INTENSET = CCM_INTENSET_ENDCRYPT_Msk;
while (NRF_CCM->EVENTS_ENDCRYPT == 0) {
__WFE();
__SEV();
__WFE();
}
NRF_CCM->INTENCLR = CCM_INTENCLR_ENDCRYPT_Msk;
_NvicIrqUnpend(CCM_AAR_IRQn);
return (NRF_CCM->EVENTS_ERROR == 0);
}
uint32_t radio_ccm_mic_is_valid(void)
{
return NRF_CCM->MICSTATUS;
}
static uint8_t ALIGNED(4) _aar_scratch[3];
void radio_ar_configure(uint32_t nirk, void *irk)
{
NRF_AAR->ENABLE = 1;
NRF_AAR->NIRK = nirk;
NRF_AAR->IRKPTR = (uint32_t)irk;
NRF_AAR->ADDRPTR = (uint32_t)NRF_RADIO->PACKETPTR;
NRF_AAR->SCRATCHPTR = (uint32_t)_aar_scratch[0];
radio_bc_configure(64);
NRF_PPI->CH[6].EEP = (uint32_t)&(NRF_RADIO->EVENTS_BCMATCH);
NRF_PPI->CH[6].TEP = (uint32_t)&(NRF_AAR->TASKS_START);
NRF_PPI->CHENSET = PPI_CHEN_CH6_Msk;
}
uint32_t radio_ar_match_get(void)
{
return NRF_AAR->STATUS;
}
void radio_ar_status_reset(void)
{
if (radio_bc_has_match()) {
NRF_AAR->EVENTS_END = 0;
NRF_AAR->EVENTS_RESOLVED = 0;
NRF_AAR->EVENTS_NOTRESOLVED = 0;
}
radio_bc_status_reset();
}
uint32_t radio_ar_has_match(void)
{
return (radio_bc_has_match() &&
(NRF_AAR->EVENTS_END) &&
(NRF_AAR->EVENTS_RESOLVED));
}