/*
|
* Licensed to the Apache Software Foundation (ASF) under one
|
* or more contributor license agreements. See the NOTICE file
|
* distributed with this work for additional information
|
* regarding copyright ownership. The ASF licenses this file
|
* to you 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 <stdint.h>
|
#include <string.h>
|
#include <assert.h>
|
#include <hal/nrf_radio.h>
|
#include <hal/nrf_ccm.h>
|
#include <hal/nrf_aar.h>
|
#include <hal/nrf_timer.h>
|
#include <hal/nrf_ppi.h>
|
#include <hal/nrf_rtc.h>
|
#include "nimble_syscfg.h"
|
#include "os/os.h"
|
/* Keep os_cputime explicitly to enable build on non-Mynewt platforms */
|
#include "os/os_cputime.h"
|
#include "ble/xcvr.h"
|
#include "nimble/ble.h"
|
#include "nimble/nimble_opt.h"
|
#include "nimble/nimble_npl.h"
|
#include "controller/ble_phy.h"
|
#include "controller/ble_phy_trace.h"
|
#include "controller/ble_ll.h"
|
#include "controller/ble_ll_plna.h"
|
#include "nrfx.h"
|
#if MYNEWT
|
#include "mcu/nrf52_clock.h"
|
#include "mcu/cmsis_nvic.h"
|
#include "hal/hal_gpio.h"
|
#else
|
#include "core_cm4.h"
|
#endif
|
|
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY)
|
#if !MYNEWT_VAL_CHOICE(MCU_TARGET, nRF52840) && !MYNEWT_VAL_CHOICE(MCU_TARGET, nRF52811)
|
#error LE Coded PHY can only be enabled on nRF52811 or nRF52840
|
#endif
|
#endif
|
|
#if BABBLESIM
|
extern void tm_tick(void);
|
#endif
|
|
/*
|
* NOTE: This code uses a couple of PPI channels so care should be taken when
|
* using PPI somewhere else.
|
*
|
* Pre-programmed channels: CH20, CH21, CH23, CH25, CH31
|
* Regular channels: CH4, CH5 and optionally CH6, CH7, CH17, CH18, CH19
|
* - CH4 = cancel wfr timer on address match
|
* - CH5 = disable radio on wfr timer expiry
|
* - CH6 = PA/LNA control (enable)
|
* - CH7 = PA/LNA control (disable)
|
* - CH17 = (optional) gpio debug for radio ramp-up
|
* - CH18 = (optional) gpio debug for wfr timer RX enabled
|
* - CH19 = (optional) gpio debug for wfr timer radio disabled
|
*
|
*/
|
|
/* XXX: 4) Make sure RF is higher priority interrupt than schedule */
|
|
/*
|
* XXX: Maximum possible transmit time is 1 msec for a 60ppm crystal
|
* and 16ms for a 30ppm crystal! We need to limit PDU size based on
|
* crystal accuracy. Look at this in the spec.
|
*/
|
|
/* XXX: private header file? */
|
extern uint8_t g_nrf_num_irks;
|
extern uint32_t g_nrf_irk_list[];
|
|
/* To disable all radio interrupts */
|
#define NRF_RADIO_IRQ_MASK_ALL (0x34FF)
|
|
/*
|
* We configure the nrf with a 1 byte S0 field, 8 bit length field, and
|
* zero bit S1 field. The preamble is 8 bits long.
|
*/
|
#define NRF_LFLEN_BITS (8)
|
#define NRF_S0LEN (1)
|
#define NRF_S1LEN_BITS (0)
|
#define NRF_CILEN_BITS (2)
|
#define NRF_TERMLEN_BITS (3)
|
|
/* Maximum length of frames */
|
#define NRF_MAXLEN (255)
|
#define NRF_BALEN (3) /* For base address of 3 bytes */
|
|
/* NRF_RADIO->PCNF0 configuration values */
|
#define NRF_PCNF0 (NRF_LFLEN_BITS << RADIO_PCNF0_LFLEN_Pos) | \
|
(RADIO_PCNF0_S1INCL_Msk) | \
|
(NRF_S0LEN << RADIO_PCNF0_S0LEN_Pos) | \
|
(NRF_S1LEN_BITS << RADIO_PCNF0_S1LEN_Pos)
|
#define NRF_PCNF0_1M (NRF_PCNF0) | \
|
(RADIO_PCNF0_PLEN_8bit << RADIO_PCNF0_PLEN_Pos)
|
#define NRF_PCNF0_2M (NRF_PCNF0) | \
|
(RADIO_PCNF0_PLEN_16bit << RADIO_PCNF0_PLEN_Pos)
|
#define NRF_PCNF0_CODED (NRF_PCNF0) | \
|
(RADIO_PCNF0_PLEN_LongRange << RADIO_PCNF0_PLEN_Pos) | \
|
(NRF_CILEN_BITS << RADIO_PCNF0_CILEN_Pos) | \
|
(NRF_TERMLEN_BITS << RADIO_PCNF0_TERMLEN_Pos)
|
|
/* BLE PHY data structure */
|
struct ble_phy_obj
|
{
|
uint8_t phy_stats_initialized;
|
int8_t phy_txpwr_dbm;
|
uint8_t phy_chan;
|
uint8_t phy_state;
|
uint8_t phy_transition;
|
uint8_t phy_transition_late;
|
uint8_t phy_rx_started;
|
uint8_t phy_encrypted;
|
uint8_t phy_privacy;
|
uint8_t phy_tx_pyld_len;
|
uint8_t phy_cur_phy_mode;
|
uint8_t phy_tx_phy_mode;
|
uint8_t phy_rx_phy_mode;
|
uint8_t phy_bcc_offset;
|
int8_t rx_pwr_compensation;
|
uint32_t phy_aar_scratch;
|
uint32_t phy_access_address;
|
struct ble_mbuf_hdr rxhdr;
|
void *txend_arg;
|
ble_phy_tx_end_func txend_cb;
|
uint32_t phy_start_cputime;
|
};
|
struct ble_phy_obj g_ble_phy_data;
|
|
/* XXX: if 27 byte packets desired we can make this smaller */
|
/* Global transmit/receive buffer */
|
static uint32_t g_ble_phy_tx_buf[(BLE_PHY_MAX_PDU_LEN + 3) / 4];
|
static uint32_t g_ble_phy_rx_buf[(BLE_PHY_MAX_PDU_LEN + 3) / 4];
|
|
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION)
|
/* Make sure word-aligned for faster copies */
|
static uint32_t g_ble_phy_enc_buf[(BLE_PHY_MAX_PDU_LEN + 3) / 4];
|
#endif
|
|
/* RF center frequency for each channel index (offset from 2400 MHz) */
|
static const uint8_t g_ble_phy_chan_freq[BLE_PHY_NUM_CHANS] = {
|
4, 6, 8, 10, 12, 14, 16, 18, 20, 22, /* 0-9 */
|
24, 28, 30, 32, 34, 36, 38, 40, 42, 44, /* 10-19 */
|
46, 48, 50, 52, 54, 56, 58, 60, 62, 64, /* 20-29 */
|
66, 68, 70, 72, 74, 76, 78, 2, 26, 80, /* 30-39 */
|
};
|
|
#if (BLE_LL_BT5_PHY_SUPPORTED == 1)
|
/* packet start offsets (in usecs) */
|
static const uint16_t g_ble_phy_mode_pkt_start_off[BLE_PHY_NUM_MODE] = {
|
[BLE_PHY_MODE_1M] = 40,
|
[BLE_PHY_MODE_2M] = 24,
|
[BLE_PHY_MODE_CODED_125KBPS] = 376,
|
[BLE_PHY_MODE_CODED_500KBPS] = 376
|
};
|
#endif
|
|
/* Various radio timings */
|
/* Radio ramp-up times in usecs (fast mode) */
|
#define BLE_PHY_T_TXENFAST (XCVR_TX_RADIO_RAMPUP_USECS)
|
#define BLE_PHY_T_RXENFAST (XCVR_RX_RADIO_RAMPUP_USECS)
|
|
#if BABBLESIM
|
/* delay between EVENTS_READY and start of tx */
|
static const uint8_t g_ble_phy_t_txdelay[BLE_PHY_NUM_MODE] = {
|
[BLE_PHY_MODE_1M] = 1,
|
[BLE_PHY_MODE_2M] = 1,
|
};
|
/* delay between EVENTS_END and end of txd packet */
|
static const uint8_t g_ble_phy_t_txenddelay[BLE_PHY_NUM_MODE] = {
|
[BLE_PHY_MODE_1M] = 1,
|
[BLE_PHY_MODE_2M] = 1,
|
};
|
/* delay between rxd access address (w/ TERM1 for coded) and EVENTS_ADDRESS */
|
static const uint8_t g_ble_phy_t_rxaddrdelay[BLE_PHY_NUM_MODE] = {
|
[BLE_PHY_MODE_1M] = 9,
|
[BLE_PHY_MODE_2M] = 5,
|
};
|
/* delay between end of rxd packet and EVENTS_END */
|
static const uint8_t g_ble_phy_t_rxenddelay[BLE_PHY_NUM_MODE] = {
|
[BLE_PHY_MODE_1M] = 9,
|
[BLE_PHY_MODE_2M] = 5,
|
};
|
#else
|
/* delay between EVENTS_READY and start of tx */
|
static const uint8_t g_ble_phy_t_txdelay[BLE_PHY_NUM_MODE] = {
|
[BLE_PHY_MODE_1M] = 4,
|
[BLE_PHY_MODE_2M] = 3,
|
[BLE_PHY_MODE_CODED_125KBPS] = 5,
|
[BLE_PHY_MODE_CODED_500KBPS] = 5
|
};
|
/* delay between EVENTS_END and end of txd packet */
|
static const uint8_t g_ble_phy_t_txenddelay[BLE_PHY_NUM_MODE] = {
|
[BLE_PHY_MODE_1M] = 4,
|
[BLE_PHY_MODE_2M] = 3,
|
[BLE_PHY_MODE_CODED_125KBPS] = 9,
|
[BLE_PHY_MODE_CODED_500KBPS] = 3
|
};
|
/* delay between rxd access address (w/ TERM1 for coded) and EVENTS_ADDRESS */
|
static const uint8_t g_ble_phy_t_rxaddrdelay[BLE_PHY_NUM_MODE] = {
|
[BLE_PHY_MODE_1M] = 6,
|
[BLE_PHY_MODE_2M] = 2,
|
[BLE_PHY_MODE_CODED_125KBPS] = 17,
|
[BLE_PHY_MODE_CODED_500KBPS] = 17
|
};
|
/* delay between end of rxd packet and EVENTS_END */
|
static const uint8_t g_ble_phy_t_rxenddelay[BLE_PHY_NUM_MODE] = {
|
[BLE_PHY_MODE_1M] = 6,
|
[BLE_PHY_MODE_2M] = 2,
|
[BLE_PHY_MODE_CODED_125KBPS] = 27,
|
[BLE_PHY_MODE_CODED_500KBPS] = 22
|
};
|
#endif
|
|
/* Statistics */
|
STATS_SECT_START(ble_phy_stats)
|
STATS_SECT_ENTRY(phy_isrs)
|
STATS_SECT_ENTRY(tx_good)
|
STATS_SECT_ENTRY(tx_fail)
|
STATS_SECT_ENTRY(tx_late)
|
STATS_SECT_ENTRY(tx_bytes)
|
STATS_SECT_ENTRY(rx_starts)
|
STATS_SECT_ENTRY(rx_aborts)
|
STATS_SECT_ENTRY(rx_valid)
|
STATS_SECT_ENTRY(rx_crc_err)
|
STATS_SECT_ENTRY(rx_late)
|
STATS_SECT_ENTRY(radio_state_errs)
|
STATS_SECT_ENTRY(rx_hw_err)
|
STATS_SECT_ENTRY(tx_hw_err)
|
STATS_SECT_END
|
STATS_SECT_DECL(ble_phy_stats) ble_phy_stats;
|
|
STATS_NAME_START(ble_phy_stats)
|
STATS_NAME(ble_phy_stats, phy_isrs)
|
STATS_NAME(ble_phy_stats, tx_good)
|
STATS_NAME(ble_phy_stats, tx_fail)
|
STATS_NAME(ble_phy_stats, tx_late)
|
STATS_NAME(ble_phy_stats, tx_bytes)
|
STATS_NAME(ble_phy_stats, rx_starts)
|
STATS_NAME(ble_phy_stats, rx_aborts)
|
STATS_NAME(ble_phy_stats, rx_valid)
|
STATS_NAME(ble_phy_stats, rx_crc_err)
|
STATS_NAME(ble_phy_stats, rx_late)
|
STATS_NAME(ble_phy_stats, radio_state_errs)
|
STATS_NAME(ble_phy_stats, rx_hw_err)
|
STATS_NAME(ble_phy_stats, tx_hw_err)
|
STATS_NAME_END(ble_phy_stats)
|
|
/*
|
* NOTE:
|
* Tested the following to see what would happen:
|
* -> NVIC has radio irq enabled (interrupt # 1, mask 0x2).
|
* -> Set up nrf to receive. Clear ADDRESS event register.
|
* -> Enable ADDRESS interrupt on nrf5 by writing to INTENSET.
|
* -> Enable RX.
|
* -> Disable interrupts globally using OS_ENTER_CRITICAL().
|
* -> Wait until a packet is received and the ADDRESS event occurs.
|
* -> Call ble_phy_disable().
|
*
|
* At this point I wanted to see the state of the cortex NVIC. The IRQ
|
* pending bit was TRUE for the radio interrupt (as expected) as we never
|
* serviced the radio interrupt (interrupts were disabled).
|
*
|
* What was unexpected was this: without clearing the pending IRQ in the NVIC,
|
* when radio interrupts were re-enabled (address event bit in INTENSET set to
|
* 1) and the radio ADDRESS event register read 1 (it was never cleared after
|
* the first address event), the radio did not enter the ISR! I would have
|
* expected that if the following were true, an interrupt would occur:
|
* -> NVIC ISER bit set to TRUE
|
* -> NVIC ISPR bit reads TRUE, meaning interrupt is pending.
|
* -> Radio peripheral interrupts are enabled for some event (or events).
|
* -> Corresponding event register(s) in radio peripheral read 1.
|
*
|
* Not sure what the end result of all this is. We will clear the pending
|
* bit in the NVIC just to be sure when we disable the PHY.
|
*/
|
|
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION)
|
|
/*
|
* Per nordic, the number of bytes needed for scratch is 16 + MAX_PKT_SIZE.
|
* However, when I used a smaller size it still overwrote the scratchpad. Until
|
* I figure this out I am just going to allocate 67 words so we have enough
|
* space for 267 bytes of scratch. I used 268 bytes since not sure if this
|
* needs to be aligned and burning a byte is no big deal.
|
*/
|
//#define NRF_ENC_SCRATCH_WORDS (((MYNEWT_VAL(BLE_LL_MAX_PKT_SIZE) + 16) + 3) / 4)
|
#define NRF_ENC_SCRATCH_WORDS (67)
|
|
uint32_t g_nrf_encrypt_scratchpad[NRF_ENC_SCRATCH_WORDS];
|
|
struct nrf_ccm_data
|
{
|
uint8_t key[16];
|
uint64_t pkt_counter;
|
uint8_t dir_bit;
|
uint8_t iv[8];
|
} __attribute__((packed));
|
|
struct nrf_ccm_data g_nrf_ccm_data;
|
#endif
|
|
static int g_ble_phy_gpiote_idx;
|
|
#if MYNEWT_VAL(BLE_LL_PA) || MYNEWT_VAL(BLE_LL_LNA)
|
|
#define PLNA_SINGLE_GPIO \
|
(!MYNEWT_VAL(BLE_LL_PA) || !MYNEWT_VAL(BLE_LL_LNA) || \
|
(MYNEWT_VAL(BLE_LL_PA_GPIO) == MYNEWT_VAL(BLE_LL_LNA_GPIO)))
|
|
#if PLNA_SINGLE_GPIO
|
static uint8_t plna_idx;
|
#else
|
#if MYNEWT_VAL(BLE_LL_PA)
|
static uint8_t plna_pa_idx;
|
#endif
|
#if MYNEWT_VAL(BLE_LL_LNA)
|
static uint8_t plna_lna_idx;
|
#endif
|
#endif
|
|
#endif
|
|
#ifndef BABBLESIM
|
static void
|
ble_phy_apply_errata_102_106_107(void)
|
{
|
/* [102] RADIO: PAYLOAD/END events delayed or not triggered after ADDRESS
|
* [106] RADIO: Higher CRC error rates for some access addresses
|
* [107] RADIO: Immediate address match for access addresses containing MSBs 0x00
|
*/
|
*(volatile uint32_t *)0x40001774 = ((*(volatile uint32_t *)0x40001774) &
|
0xfffffffe) | 0x01000000;
|
}
|
#endif
|
|
#if (BLE_LL_BT5_PHY_SUPPORTED == 1)
|
|
/* Packet start offset (in usecs). This is the preamble plus access address.
|
* For LE Coded PHY this also includes CI and TERM1. */
|
uint32_t
|
ble_phy_mode_pdu_start_off(int phy_mode)
|
{
|
return g_ble_phy_mode_pkt_start_off[phy_mode];
|
}
|
|
#if NRF52840_XXAA
|
static inline bool
|
ble_phy_mode_is_coded(uint8_t phy_mode)
|
{
|
return (phy_mode == BLE_PHY_MODE_CODED_125KBPS) ||
|
(phy_mode == BLE_PHY_MODE_CODED_500KBPS);
|
}
|
|
static void
|
ble_phy_apply_nrf52840_errata(uint8_t new_phy_mode)
|
{
|
bool new_coded = ble_phy_mode_is_coded(new_phy_mode);
|
bool cur_coded = ble_phy_mode_is_coded(g_ble_phy_data.phy_cur_phy_mode);
|
|
/*
|
* Workarounds should be applied only when switching to/from LE Coded PHY
|
* so no need to apply them every time.
|
*
|
* nRF52840 Engineering A Errata v1.2
|
* [164] RADIO: Low sensitivity in long range mode
|
*
|
* nRF52840 Rev 1 Errata
|
* [191] RADIO: High packet error rate in BLE Long Range mode
|
*/
|
if (new_coded == cur_coded) {
|
return;
|
}
|
|
if (new_coded) {
|
#if MYNEWT_VAL(BLE_PHY_NRF52840_ERRATA_164)
|
/* [164] */
|
*(volatile uint32_t *)0x4000173C |= 0x80000000;
|
*(volatile uint32_t *)0x4000173C =
|
((*(volatile uint32_t *)0x4000173C & 0xFFFFFF00) | 0x5C);
|
#endif
|
#if MYNEWT_VAL(BLE_PHY_NRF52840_ERRATA_191)
|
/* [191] */
|
*(volatile uint32_t *) 0x40001740 =
|
((*((volatile uint32_t *) 0x40001740)) & 0x7FFF00FF) |
|
0x80000000 | (((uint32_t)(196)) << 8);
|
#endif
|
} else {
|
#if MYNEWT_VAL(BLE_PHY_NRF52840_ERRATA_164)
|
/* [164] */
|
*(volatile uint32_t *)0x4000173C &= ~0x80000000;
|
#endif
|
#if MYNEWT_VAL(BLE_PHY_NRF52840_ERRATA_191)
|
/* [191] */
|
*(volatile uint32_t *) 0x40001740 =
|
((*((volatile uint32_t *) 0x40001740)) & 0x7FFFFFFF);
|
#endif
|
}
|
}
|
#endif
|
|
static void
|
ble_phy_mode_apply(uint8_t phy_mode)
|
{
|
if (phy_mode == g_ble_phy_data.phy_cur_phy_mode) {
|
return;
|
}
|
|
#if NRF52840_XXAA
|
ble_phy_apply_nrf52840_errata(phy_mode);
|
#endif
|
|
switch (phy_mode) {
|
case BLE_PHY_MODE_1M:
|
NRF_RADIO->MODE = RADIO_MODE_MODE_Ble_1Mbit;
|
NRF_RADIO->PCNF0 = NRF_PCNF0_1M;
|
break;
|
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY)
|
case BLE_PHY_MODE_2M:
|
NRF_RADIO->MODE = RADIO_MODE_MODE_Ble_2Mbit;
|
NRF_RADIO->PCNF0 = NRF_PCNF0_2M;
|
break;
|
#endif
|
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY)
|
case BLE_PHY_MODE_CODED_125KBPS:
|
NRF_RADIO->MODE = RADIO_MODE_MODE_Ble_LR125Kbit;
|
NRF_RADIO->PCNF0 = NRF_PCNF0_CODED;
|
break;
|
case BLE_PHY_MODE_CODED_500KBPS:
|
NRF_RADIO->MODE = RADIO_MODE_MODE_Ble_LR500Kbit;
|
NRF_RADIO->PCNF0 = NRF_PCNF0_CODED;
|
break;
|
#endif
|
default:
|
assert(0);
|
}
|
|
g_ble_phy_data.phy_cur_phy_mode = phy_mode;
|
}
|
|
void
|
ble_phy_mode_set(uint8_t tx_phy_mode, uint8_t rx_phy_mode)
|
{
|
g_ble_phy_data.phy_tx_phy_mode = tx_phy_mode;
|
g_ble_phy_data.phy_rx_phy_mode = rx_phy_mode;
|
}
|
#endif
|
|
static void
|
ble_phy_plna_enable_pa(void)
|
{
|
#if MYNEWT_VAL(BLE_LL_PA)
|
ble_ll_plna_pa_enable();
|
|
#if !PLNA_SINGLE_GPIO
|
NRF_PPI->CH[6].TEP = (uint32_t) &(NRF_GPIOTE->TASKS_SET[plna_pa_idx]);
|
NRF_PPI->CH[7].TEP = (uint32_t) &(NRF_GPIOTE->TASKS_CLR[plna_pa_idx]);
|
#endif
|
|
NRF_PPI->CHENSET = PPI_CHEN_CH6_Msk | PPI_CHEN_CH7_Msk;
|
#endif
|
}
|
|
static void
|
ble_phy_plna_enable_lna(void)
|
{
|
#if MYNEWT_VAL(BLE_LL_LNA)
|
ble_ll_plna_lna_enable();
|
|
#if !PLNA_SINGLE_GPIO
|
NRF_PPI->CH[6].TEP = (uint32_t) &(NRF_GPIOTE->TASKS_SET[plna_lna_idx]);
|
NRF_PPI->CH[7].TEP = (uint32_t) &(NRF_GPIOTE->TASKS_CLR[plna_lna_idx]);
|
#endif
|
|
NRF_PPI->CHENSET = PPI_CHEN_CH6_Msk | PPI_CHEN_CH7_Msk;
|
#endif
|
}
|
|
int
|
ble_phy_get_cur_phy(void)
|
{
|
#if (BLE_LL_BT5_PHY_SUPPORTED == 1)
|
switch (g_ble_phy_data.phy_cur_phy_mode) {
|
case BLE_PHY_MODE_1M:
|
return BLE_PHY_1M;
|
case BLE_PHY_MODE_2M:
|
return BLE_PHY_2M;
|
case BLE_PHY_MODE_CODED_125KBPS:
|
case BLE_PHY_MODE_CODED_500KBPS:
|
return BLE_PHY_CODED;
|
default:
|
assert(0);
|
return -1;
|
}
|
#else
|
return BLE_PHY_1M;
|
#endif
|
}
|
|
/**
|
* Copies the data from the phy receive buffer into a mbuf chain.
|
*
|
* @param dptr Pointer to receive buffer
|
* @param rxpdu Pointer to already allocated mbuf chain
|
*
|
* NOTE: the packet header already has the total mbuf length in it. The
|
* lengths of the individual mbufs are not set prior to calling.
|
*
|
*/
|
void
|
ble_phy_rxpdu_copy(uint8_t *dptr, struct os_mbuf *rxpdu)
|
{
|
uint32_t rem_len;
|
uint32_t copy_len;
|
uint32_t block_len;
|
uint32_t block_rem_len;
|
void *dst;
|
void *src;
|
struct os_mbuf * om;
|
|
/* Better be aligned */
|
assert(((uint32_t)dptr & 3) == 0);
|
|
block_len = rxpdu->om_omp->omp_databuf_len;
|
rem_len = OS_MBUF_PKTHDR(rxpdu)->omp_len;
|
src = dptr;
|
|
/*
|
* Setup for copying from first mbuf which is shorter due to packet header
|
* and extra leading space
|
*/
|
copy_len = block_len - rxpdu->om_pkthdr_len - 4;
|
om = rxpdu;
|
dst = om->om_data;
|
|
while (true) {
|
/*
|
* Always copy blocks of length aligned to word size, only last mbuf
|
* will have remaining non-word size bytes appended.
|
*/
|
block_rem_len = copy_len;
|
copy_len = min(copy_len, rem_len);
|
copy_len &= ~3;
|
|
dst = om->om_data;
|
om->om_len = copy_len;
|
rem_len -= copy_len;
|
block_rem_len -= copy_len;
|
|
#if BABBLESIM
|
memcpy(dst, src, copy_len);
|
dst += copy_len;
|
src += copy_len;
|
#else
|
__asm__ volatile (".syntax unified \n"
|
" mov r4, %[len] \n"
|
" b 2f \n"
|
"1: ldr r3, [%[src], %[len]] \n"
|
" str r3, [%[dst], %[len]] \n"
|
"2: subs %[len], #4 \n"
|
" bpl 1b \n"
|
" adds %[src], %[src], r4 \n"
|
" adds %[dst], %[dst], r4 \n"
|
: [dst] "+r" (dst), [src] "+r" (src),
|
[len] "+r" (copy_len)
|
:
|
: "r3", "r4", "memory"
|
);
|
#endif
|
|
if ((rem_len < 4) && (block_rem_len >= rem_len)) {
|
break;
|
}
|
|
/* Move to next mbuf */
|
om = SLIST_NEXT(om, om_next);
|
copy_len = block_len;
|
}
|
|
/* Copy remaining bytes, if any, to last mbuf */
|
om->om_len += rem_len;
|
|
#if BABBLESIM
|
memcpy(dst, src, rem_len);
|
#else
|
__asm__ volatile (".syntax unified \n"
|
" b 2f \n"
|
"1: ldrb r3, [%[src], %[len]] \n"
|
" strb r3, [%[dst], %[len]] \n"
|
"2: subs %[len], #1 \n"
|
" bpl 1b \n"
|
: [len] "+r" (rem_len)
|
: [dst] "r" (dst), [src] "r" (src)
|
: "r3", "memory"
|
);
|
#endif
|
|
/* Copy header */
|
memcpy(BLE_MBUF_HDR_PTR(rxpdu), &g_ble_phy_data.rxhdr,
|
sizeof(struct ble_mbuf_hdr));
|
}
|
|
/**
|
* Called when we want to wait if the radio is in either the rx or tx
|
* disable states. We want to wait until that state is over before doing
|
* anything to the radio
|
*/
|
static void
|
nrf_wait_disabled(void)
|
{
|
uint32_t state;
|
|
state = NRF_RADIO->STATE;
|
if (state != RADIO_STATE_STATE_Disabled) {
|
if ((state == RADIO_STATE_STATE_RxDisable) ||
|
(state == RADIO_STATE_STATE_TxDisable)) {
|
/* This will end within a short time (6 usecs). Just poll */
|
while (NRF_RADIO->STATE == state) {
|
/* If this fails, something is really wrong. Should last
|
* no more than 6 usecs */
|
#if BABBLESIM
|
tm_tick();
|
#endif
|
}
|
}
|
}
|
}
|
|
/**
|
*
|
*
|
*/
|
static int
|
ble_phy_set_start_time(uint32_t cputime, uint8_t rem_usecs, bool tx)
|
{
|
uint32_t next_cc;
|
uint32_t cur_cc;
|
uint32_t cntr;
|
uint32_t delta;
|
|
/*
|
* We need to adjust start time to include radio ramp-up and TX pipeline
|
* delay (the latter only if applicable, so only for TX).
|
*
|
* Radio ramp-up time is 40 usecs and TX delay is 3 or 5 usecs depending on
|
* phy, thus we'll offset RTC by 2 full ticks (61 usecs) and then compensate
|
* using TIMER0 with 1 usec precision.
|
*/
|
|
cputime -= 2;
|
rem_usecs += 61;
|
if (tx) {
|
rem_usecs -= BLE_PHY_T_TXENFAST;
|
rem_usecs -= g_ble_phy_t_txdelay[g_ble_phy_data.phy_cur_phy_mode];
|
} else {
|
rem_usecs -= BLE_PHY_T_RXENFAST;
|
}
|
|
/*
|
* rem_usecs will be no more than 2 ticks, but if it is more than single
|
* tick then we should better count one more low-power tick rather than
|
* 30 high-power usecs. Also make sure we don't set TIMER0 CC to 0 as the
|
* compare won't occur.
|
*/
|
|
if (rem_usecs > 30) {
|
cputime++;
|
rem_usecs -= 30;
|
}
|
|
/*
|
* Can we set the RTC compare to start TIMER0? We can do it if:
|
* a) Current compare value is not N+1 or N+2 ticks from current
|
* counter.
|
* b) The value we want to set is not at least N+2 from current
|
* counter.
|
*
|
* NOTE: since the counter can tick 1 while we do these calculations we
|
* need to account for it.
|
*/
|
next_cc = cputime & 0xffffff;
|
cur_cc = NRF_RTC0->CC[0];
|
cntr = NRF_RTC0->COUNTER;
|
|
delta = (cur_cc - cntr) & 0xffffff;
|
if ((delta <= 3) && (delta != 0)) {
|
return -1;
|
}
|
delta = (next_cc - cntr) & 0xffffff;
|
if ((delta & 0x800000) || (delta < 3)) {
|
return -1;
|
}
|
|
/* Clear and set TIMER0 to fire off at proper time */
|
nrf_timer_task_trigger(NRF_TIMER0, NRF_TIMER_TASK_CLEAR);
|
nrf_timer_cc_set(NRF_TIMER0, 0, rem_usecs);
|
NRF_TIMER0->EVENTS_COMPARE[0] = 0;
|
|
/* Set RTC compare to start TIMER0 */
|
NRF_RTC0->EVENTS_COMPARE[0] = 0;
|
nrf_rtc_cc_set(NRF_RTC0, 0, next_cc);
|
nrf_rtc_event_enable(NRF_RTC0, RTC_EVTENSET_COMPARE0_Msk);
|
|
/* Enable PPI */
|
nrf_ppi_channels_enable(NRF_PPI, PPI_CHEN_CH31_Msk);
|
|
/* Store the cputime at which we set the RTC */
|
g_ble_phy_data.phy_start_cputime = cputime;
|
|
return 0;
|
}
|
|
static int
|
ble_phy_set_start_now(void)
|
{
|
os_sr_t sr;
|
uint32_t now;
|
|
OS_ENTER_CRITICAL(sr);
|
|
/*
|
* Set TIMER0 to fire immediately. We can't set CC to 0 as compare will not
|
* occur in such case.
|
*/
|
nrf_timer_task_trigger(NRF_TIMER0, NRF_TIMER_TASK_CLEAR);
|
nrf_timer_cc_set(NRF_TIMER0, 0, 1);
|
NRF_TIMER0->EVENTS_COMPARE[0] = 0;
|
|
/*
|
* Set RTC compare to start TIMER0. We need to set it to at least N+2 ticks
|
* from current value to guarantee triggering compare event, but let's set
|
* it to N+3 to account for possible extra tick on RTC0 during these
|
* operations.
|
*/
|
now = os_cputime_get32();
|
NRF_RTC0->EVENTS_COMPARE[0] = 0;
|
nrf_rtc_cc_set(NRF_RTC0, 0, now + 3);
|
nrf_rtc_event_enable(NRF_RTC0, RTC_EVTENSET_COMPARE0_Msk);
|
|
/* Enable PPI */
|
nrf_ppi_channels_enable(NRF_PPI, PPI_CHEN_CH31_Msk);
|
/*
|
* Store the cputime at which we set the RTC
|
*
|
* XXX Compare event may be triggered on previous CC value (if it was set to
|
* less than N+2) so in rare cases actual start time may be 2 ticks earlier
|
* than what we expect. Since this is only used on RX, it may cause AUX scan
|
* to be scheduled 1 or 2 ticks too late so we'll miss it - it's acceptable
|
* for now.
|
*/
|
g_ble_phy_data.phy_start_cputime = now + 3;
|
|
OS_EXIT_CRITICAL(sr);
|
|
return 0;
|
}
|
|
/**
|
* Function is used to set PPI so that we can time out waiting for a reception
|
* to occur. This happens for two reasons: we have sent a packet and we are
|
* waiting for a response (txrx should be set to ENABLE_TXRX) or we are
|
* starting a connection event and we are a slave and we are waiting for the
|
* master to send us a packet (txrx should be set to ENABLE_RX).
|
*
|
* NOTE: when waiting for a txrx turn-around, wfr_usecs is not used as there
|
* is no additional time to wait; we know when we should receive the address of
|
* the received frame.
|
*
|
* @param txrx Flag denoting if this wfr is a txrx turn-around or not.
|
* @param tx_phy_mode phy mode for last TX (only valid for TX->RX)
|
* @param wfr_usecs Amount of usecs to wait.
|
*/
|
void
|
ble_phy_wfr_enable(int txrx, uint8_t tx_phy_mode, uint32_t wfr_usecs)
|
{
|
uint32_t end_time;
|
uint8_t phy;
|
|
phy = g_ble_phy_data.phy_cur_phy_mode;
|
|
if (txrx == BLE_PHY_WFR_ENABLE_TXRX) {
|
/* RX shall start exactly T_IFS after TX end captured in CC[2] */
|
end_time = NRF_TIMER0->CC[2] + BLE_LL_IFS;
|
/* Adjust for delay between EVENT_END and actual TX end time */
|
end_time += g_ble_phy_t_txenddelay[tx_phy_mode];
|
/* Wait a bit longer due to allowed active clock accuracy */
|
end_time += 2;
|
/*
|
* It's possible that we'll capture PDU start time at the end of timer
|
* cycle and since wfr expires at the beginning of calculated timer
|
* cycle it can be almost 1 usec too early. Let's compensate for this
|
* by waiting 1 usec more.
|
*/
|
end_time += 1;
|
} else {
|
/*
|
* RX shall start no later than wfr_usecs after RX enabled.
|
* CC[0] is the time of RXEN so adjust for radio ram-up.
|
* Do not add jitter since this is already covered by LL.
|
*/
|
end_time = NRF_TIMER0->CC[0] + BLE_PHY_T_RXENFAST + wfr_usecs;
|
}
|
|
/*
|
* Note: on LE Coded EVENT_ADDRESS is fired after TERM1 is received, so
|
* we are actually calculating relative to start of packet payload
|
* which is fine.
|
*/
|
|
/* Adjust for receiving access address since this triggers EVENT_ADDRESS */
|
end_time += ble_phy_mode_pdu_start_off(phy);
|
/* Adjust for delay between actual access address RX and EVENT_ADDRESS */
|
end_time += g_ble_phy_t_rxaddrdelay[phy];
|
|
/* wfr_secs is the time from rxen until timeout */
|
nrf_timer_cc_set(NRF_TIMER0, 3, end_time);
|
NRF_TIMER0->EVENTS_COMPARE[3] = 0;
|
|
/* Enable wait for response PPI */
|
nrf_ppi_channels_enable(NRF_PPI, (PPI_CHEN_CH4_Msk | PPI_CHEN_CH5_Msk));
|
|
/*
|
* It may happen that if CPU is halted for a brief moment (e.g. during flash
|
* erase or write), TIMER0 already counted past CC[3] and thus wfr will not
|
* fire as expected. In case this happened, let's just disable PPIs for wfr
|
* and trigger wfr manually (i.e. disable radio).
|
*
|
* Note that the same applies to RX start time set in CC[0] but since it
|
* should fire earlier than wfr, fixing wfr is enough.
|
*
|
* CC[1] is only used as a reference on RX start, we do not need it here so
|
* it can be used to read TIMER0 counter.
|
*/
|
nrf_timer_task_trigger(NRF_TIMER0, NRF_TIMER_TASK_CAPTURE1);
|
if (NRF_TIMER0->CC[1] > NRF_TIMER0->CC[3]) {
|
nrf_ppi_channels_disable(NRF_PPI, PPI_CHEN_CH4_Msk | PPI_CHEN_CH5_Msk);
|
nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_DISABLE);
|
}
|
}
|
|
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION)
|
static uint32_t
|
ble_phy_get_ccm_datarate(void)
|
{
|
#if BLE_LL_BT5_PHY_SUPPORTED
|
switch (g_ble_phy_data.phy_cur_phy_mode) {
|
case BLE_PHY_MODE_1M:
|
return CCM_MODE_DATARATE_1Mbit << CCM_MODE_DATARATE_Pos;
|
case BLE_PHY_MODE_2M:
|
return CCM_MODE_DATARATE_2Mbit << CCM_MODE_DATARATE_Pos;
|
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY)
|
case BLE_PHY_MODE_CODED_125KBPS:
|
return CCM_MODE_DATARATE_125Kbps << CCM_MODE_DATARATE_Pos;
|
case BLE_PHY_MODE_CODED_500KBPS:
|
return CCM_MODE_DATARATE_500Kbps << CCM_MODE_DATARATE_Pos;
|
#endif
|
}
|
|
assert(0);
|
return 0;
|
#else
|
return CCM_MODE_DATARATE_1Mbit << CCM_MODE_DATARATE_Pos;
|
#endif
|
}
|
#endif
|
|
/**
|
* Setup transceiver for receive.
|
*/
|
static void
|
ble_phy_rx_xcvr_setup(void)
|
{
|
uint8_t *dptr;
|
|
dptr = (uint8_t *)&g_ble_phy_rx_buf[0];
|
dptr += 3;
|
|
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION)
|
if (g_ble_phy_data.phy_encrypted) {
|
NRF_RADIO->PACKETPTR = (uint32_t)&g_ble_phy_enc_buf[0];
|
NRF_CCM->INPTR = (uint32_t)&g_ble_phy_enc_buf[0];
|
NRF_CCM->OUTPTR = (uint32_t)dptr;
|
NRF_CCM->SCRATCHPTR = (uint32_t)&g_nrf_encrypt_scratchpad[0];
|
NRF_CCM->MODE = CCM_MODE_LENGTH_Msk | CCM_MODE_MODE_Decryption |
|
ble_phy_get_ccm_datarate();
|
NRF_CCM->CNFPTR = (uint32_t)&g_nrf_ccm_data;
|
NRF_CCM->SHORTS = 0;
|
NRF_CCM->EVENTS_ERROR = 0;
|
NRF_CCM->EVENTS_ENDCRYPT = 0;
|
nrf_ccm_task_trigger(NRF_CCM, NRF_CCM_TASK_KSGEN);
|
nrf_ppi_channels_enable(NRF_PPI, PPI_CHEN_CH25_Msk);
|
} else {
|
NRF_RADIO->PACKETPTR = (uint32_t)dptr;
|
}
|
#else
|
NRF_RADIO->PACKETPTR = (uint32_t)dptr;
|
#endif
|
|
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
|
if (g_ble_phy_data.phy_privacy) {
|
NRF_AAR->ENABLE = AAR_ENABLE_ENABLE_Enabled;
|
NRF_AAR->IRKPTR = (uint32_t)&g_nrf_irk_list[0];
|
NRF_AAR->SCRATCHPTR = (uint32_t)&g_ble_phy_data.phy_aar_scratch;
|
NRF_AAR->EVENTS_END = 0;
|
NRF_AAR->EVENTS_RESOLVED = 0;
|
NRF_AAR->EVENTS_NOTRESOLVED = 0;
|
} else {
|
if (g_ble_phy_data.phy_encrypted == 0) {
|
NRF_AAR->ENABLE = AAR_ENABLE_ENABLE_Disabled;
|
}
|
}
|
#endif
|
|
/* Turn off trigger TXEN on output compare match and AAR on bcmatch */
|
nrf_ppi_channels_disable(NRF_PPI, PPI_CHEN_CH20_Msk | PPI_CHEN_CH23_Msk);
|
|
/* Reset the rx started flag. Used for the wait for response */
|
g_ble_phy_data.phy_rx_started = 0;
|
g_ble_phy_data.phy_state = BLE_PHY_STATE_RX;
|
|
#if BLE_LL_BT5_PHY_SUPPORTED
|
/*
|
* On Coded PHY there are CI and TERM1 fields before PDU starts so we need
|
* to take this into account when setting up BCC.
|
*/
|
if (g_ble_phy_data.phy_cur_phy_mode == BLE_PHY_MODE_CODED_125KBPS ||
|
g_ble_phy_data.phy_cur_phy_mode == BLE_PHY_MODE_CODED_500KBPS) {
|
g_ble_phy_data.phy_bcc_offset = 5;
|
} else {
|
g_ble_phy_data.phy_bcc_offset = 0;
|
}
|
#else
|
g_ble_phy_data.phy_bcc_offset = 0;
|
#endif
|
|
/* I want to know when 1st byte received (after address) */
|
nrf_radio_bcc_set(NRF_RADIO, 8 + g_ble_phy_data.phy_bcc_offset); /* in bits */
|
NRF_RADIO->EVENTS_ADDRESS = 0;
|
NRF_RADIO->EVENTS_DEVMATCH = 0;
|
NRF_RADIO->EVENTS_BCMATCH = 0;
|
NRF_RADIO->EVENTS_RSSIEND = 0;
|
NRF_RADIO->EVENTS_CRCOK = 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_int_enable(NRF_RADIO, RADIO_INTENSET_ADDRESS_Msk |
|
RADIO_INTENSET_DISABLED_Msk);
|
}
|
|
/**
|
* Called from interrupt context when the transmit ends
|
*
|
*/
|
static void
|
ble_phy_tx_end_isr(void)
|
{
|
uint8_t tx_phy_mode;
|
uint8_t was_encrypted;
|
uint8_t transition;
|
uint32_t rx_time;
|
|
/* Store PHY on which we've just transmitted smth */
|
tx_phy_mode = g_ble_phy_data.phy_cur_phy_mode;
|
|
/* If this transmission was encrypted we need to remember it */
|
was_encrypted = g_ble_phy_data.phy_encrypted;
|
(void)was_encrypted;
|
|
/* Better be in TX state! */
|
assert(g_ble_phy_data.phy_state == BLE_PHY_STATE_TX);
|
|
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION)
|
/*
|
* 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) {
|
|
#if (BLE_LL_BT5_PHY_SUPPORTED == 1)
|
ble_phy_mode_apply(g_ble_phy_data.phy_rx_phy_mode);
|
#endif
|
|
/* Packet pointer needs to be reset. */
|
ble_phy_rx_xcvr_setup();
|
|
ble_phy_wfr_enable(BLE_PHY_WFR_ENABLE_TXRX, tx_phy_mode, 0);
|
|
/* Schedule RX exactly T_IFS after TX end captured in CC[2] */
|
rx_time = NRF_TIMER0->CC[2] + BLE_LL_IFS;
|
/* Adjust for delay between EVENT_END and actual TX end time */
|
rx_time += g_ble_phy_t_txenddelay[tx_phy_mode];
|
/* Adjust for radio ramp-up */
|
rx_time -= BLE_PHY_T_RXENFAST;
|
/* Start listening a bit earlier due to allowed active clock accuracy */
|
rx_time -= 2;
|
|
nrf_timer_cc_set(NRF_TIMER0, 0, rx_time);
|
NRF_TIMER0->EVENTS_COMPARE[0] = 0;
|
nrf_ppi_channels_enable(NRF_PPI, PPI_CHEN_CH21_Msk);
|
|
ble_phy_plna_enable_lna();
|
} else {
|
/*
|
* XXX: not sure we need to stop the timer here all the time. Or that
|
* it should be stopped here.
|
*/
|
nrf_timer_task_trigger(NRF_TIMER0, NRF_TIMER_TASK_STOP);
|
NRF_TIMER0->TASKS_SHUTDOWN = 1;
|
nrf_ppi_channels_disable(NRF_PPI, PPI_CHEN_CH4_Msk | PPI_CHEN_CH5_Msk |
|
PPI_CHEN_CH20_Msk | PPI_CHEN_CH31_Msk);
|
assert(transition == BLE_PHY_TRANSITION_NONE);
|
}
|
}
|
|
static inline uint8_t
|
ble_phy_get_cur_rx_phy_mode(void)
|
{
|
uint8_t phy;
|
|
phy = g_ble_phy_data.phy_cur_phy_mode;
|
|
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY)
|
/*
|
* For Coded PHY mode can be set to either codings since actual coding is
|
* set in packet header. However, here we need actual coding of received
|
* packet as this determines pipeline delays so need to figure this out
|
* using CI field.
|
*/
|
if ((phy == BLE_PHY_MODE_CODED_125KBPS) ||
|
(phy == BLE_PHY_MODE_CODED_500KBPS)) {
|
phy = NRF_RADIO->PDUSTAT & RADIO_PDUSTAT_CISTAT_Msk ?
|
BLE_PHY_MODE_CODED_500KBPS :
|
BLE_PHY_MODE_CODED_125KBPS;
|
}
|
#endif
|
|
return phy;
|
}
|
|
static void
|
ble_phy_rx_end_isr(void)
|
{
|
int rc;
|
uint8_t *dptr;
|
uint8_t crcok;
|
uint32_t tx_time;
|
struct ble_mbuf_hdr *ble_hdr;
|
|
/* Disable automatic RXEN */
|
nrf_ppi_channels_disable(NRF_PPI, PPI_CHEN_CH21_Msk);
|
|
/* Set RSSI and CRC status flag in header */
|
ble_hdr = &g_ble_phy_data.rxhdr;
|
assert(NRF_RADIO->EVENTS_RSSIEND != 0);
|
ble_hdr->rxinfo.rssi = (-1 * NRF_RADIO->RSSISAMPLE) +
|
g_ble_phy_data.rx_pwr_compensation;
|
|
dptr = (uint8_t *)&g_ble_phy_rx_buf[0];
|
dptr += 3;
|
|
/* Count PHY crc errors and valid packets */
|
crcok = NRF_RADIO->EVENTS_CRCOK;
|
if (!crcok) {
|
STATS_INC(ble_phy_stats, rx_crc_err);
|
} else {
|
STATS_INC(ble_phy_stats, rx_valid);
|
ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_CRC_OK;
|
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION)
|
if (g_ble_phy_data.phy_encrypted) {
|
/* Only set MIC failure flag if frame is not zero length */
|
if ((dptr[1] != 0) && (NRF_CCM->MICSTATUS == 0)) {
|
ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_MIC_FAILURE;
|
}
|
|
/*
|
* XXX: not sure how to deal with this. This should not
|
* be a MIC failure but we should not hand it up. I guess
|
* this is just some form of rx error and that is how we
|
* handle it? For now, just set CRC error flags
|
*/
|
if (NRF_CCM->EVENTS_ERROR) {
|
STATS_INC(ble_phy_stats, rx_hw_err);
|
ble_hdr->rxinfo.flags &= ~BLE_MBUF_HDR_F_CRC_OK;
|
}
|
|
/*
|
* XXX: This is a total hack work-around for now but I dont
|
* know what else to do. If ENDCRYPT is not set and we are
|
* encrypted we need to not trust this frame and drop it.
|
*/
|
if (NRF_CCM->EVENTS_ENDCRYPT == 0) {
|
STATS_INC(ble_phy_stats, rx_hw_err);
|
ble_hdr->rxinfo.flags &= ~BLE_MBUF_HDR_F_CRC_OK;
|
}
|
}
|
#endif
|
}
|
|
#if (BLE_LL_BT5_PHY_SUPPORTED == 1)
|
ble_phy_mode_apply(g_ble_phy_data.phy_tx_phy_mode);
|
#endif
|
|
/*
|
* Let's schedule TX now and we will just cancel it after processing RXed
|
* packet if we don't need TX.
|
*
|
* We need this to initiate connection in case AUX_CONNECT_REQ was sent on
|
* LE Coded S8. In this case the time we process RXed packet is roughly the
|
* same as the limit when we need to have TX scheduled (i.e. TIMER0 and PPI
|
* armed) so we may simply miss the slot and set the timer in the past.
|
*
|
* When TX is scheduled in advance, we may event process packet a bit longer
|
* during radio ramp-up - this gives us extra 40 usecs which is more than
|
* enough.
|
*/
|
|
/* Schedule TX exactly T_IFS after RX end captured in CC[2] */
|
tx_time = NRF_TIMER0->CC[2] + BLE_LL_IFS;
|
/* Adjust for delay between actual RX end time and EVENT_END */
|
tx_time -= g_ble_phy_t_rxenddelay[ble_hdr->rxinfo.phy_mode];
|
/* Adjust for radio ramp-up */
|
tx_time -= BLE_PHY_T_TXENFAST;
|
/* Adjust for delay between EVENT_READY and actual TX start time */
|
tx_time -= g_ble_phy_t_txdelay[g_ble_phy_data.phy_cur_phy_mode];
|
|
nrf_timer_cc_set(NRF_TIMER0, 0, tx_time);
|
NRF_TIMER0->EVENTS_COMPARE[0] = 0;
|
nrf_ppi_channels_enable(NRF_PPI, PPI_CHEN_CH20_Msk);
|
|
ble_phy_plna_enable_pa();
|
|
/*
|
* XXX: Hack warning!
|
*
|
* It may happen (during flash erase) that CPU is stopped for a moment and
|
* TIMER0 already counted past CC[0]. In such case we will be stuck waiting
|
* for TX to start since EVENTS_COMPARE[0] will not happen any time soon.
|
* For now let's set a flag denoting that we are late in RX-TX transition so
|
* ble_phy_tx() will fail - this allows everything to cleanup nicely without
|
* the need for extra handling in many places.
|
*
|
* Note: CC[3] is used only for wfr which we do not need here.
|
*/
|
nrf_timer_task_trigger(NRF_TIMER0, NRF_TIMER_TASK_CAPTURE3);
|
if (NRF_TIMER0->CC[3] > NRF_TIMER0->CC[0]) {
|
nrf_ppi_channels_disable(NRF_PPI, PPI_CHEN_CH20_Msk);
|
g_ble_phy_data.phy_transition_late = 1;
|
}
|
|
/*
|
* XXX: This is a horrible ugly hack to deal with the RAM S1 byte
|
* that is not sent over the air but is present here. Simply move the
|
* data pointer to deal with it. Fix this later.
|
*/
|
dptr[2] = dptr[1];
|
dptr[1] = dptr[0];
|
rc = ble_ll_rx_end(dptr + 1, ble_hdr);
|
if (rc < 0) {
|
ble_phy_disable();
|
}
|
}
|
|
static bool
|
ble_phy_rx_start_isr(void)
|
{
|
int rc;
|
uint32_t state;
|
uint32_t usecs;
|
uint32_t pdu_usecs;
|
uint32_t ticks;
|
struct ble_mbuf_hdr *ble_hdr;
|
uint8_t *dptr;
|
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
|
int adva_offset;
|
#endif
|
|
dptr = (uint8_t *)&g_ble_phy_rx_buf[0];
|
|
/* Clear events and clear interrupt */
|
NRF_RADIO->EVENTS_ADDRESS = 0;
|
nrf_radio_int_disable(NRF_RADIO, RADIO_INTENCLR_ADDRESS_Msk);
|
|
/* Clear wfr timer channels */
|
nrf_ppi_channels_disable(NRF_PPI, PPI_CHEN_CH4_Msk | PPI_CHEN_CH5_Msk);
|
|
/* Initialize the ble mbuf header */
|
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->rxinfo.phy = ble_phy_get_cur_phy();
|
ble_hdr->rxinfo.phy_mode = ble_phy_get_cur_rx_phy_mode();
|
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
|
ble_hdr->rxinfo.user_data = NULL;
|
#endif
|
|
/*
|
* Calculate accurate packets start time (with remainder)
|
*
|
* We may start receiving packet somewhere during preamble in which case
|
* it is possible that actual transmission started before TIMER0 was
|
* running - need to take this into account.
|
*/
|
ble_hdr->beg_cputime = g_ble_phy_data.phy_start_cputime;
|
|
usecs = NRF_TIMER0->CC[1];
|
pdu_usecs = ble_phy_mode_pdu_start_off(ble_hdr->rxinfo.phy_mode) +
|
g_ble_phy_t_rxaddrdelay[ble_hdr->rxinfo.phy_mode];
|
if (usecs < pdu_usecs) {
|
g_ble_phy_data.phy_start_cputime--;
|
usecs += 30;
|
}
|
usecs -= pdu_usecs;
|
|
ticks = os_cputime_usecs_to_ticks(usecs);
|
usecs -= os_cputime_ticks_to_usecs(ticks);
|
if (usecs == 31) {
|
usecs = 0;
|
++ticks;
|
}
|
|
ble_hdr->beg_cputime += ticks;
|
ble_hdr->rem_usecs = usecs;
|
|
/* XXX: I wonder if we always have the 1st byte. If we need to wait for
|
* rx chain delay, it could be 18 usecs from address interrupt. The
|
nrf52 may be able to get here early. */
|
/* 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_int_disable(NRF_RADIO, NRF_RADIO_IRQ_MASK_ALL);
|
NRF_RADIO->SHORTS = 0;
|
return false;
|
}
|
|
#if BABBLESIM
|
tm_tick();
|
#endif
|
}
|
|
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
|
/*
|
* If privacy is enabled and received PDU has TxAdd bit set (i.e. random
|
* address) we try to resolve address using AAR.
|
*/
|
if (g_ble_phy_data.phy_privacy && (dptr[3] & 0x40)) {
|
/*
|
* AdvA is located at 4th octet in RX buffer (after S0, length an S1
|
* fields). In case of extended advertising PDU we need to add 2 more
|
* octets for extended header.
|
*/
|
adva_offset = (dptr[3] & 0x0f) == 0x07 ? 2 : 0;
|
NRF_AAR->ADDRPTR = (uint32_t)(dptr + 3 + adva_offset);
|
|
/* Trigger AAR after last bit of AdvA is received */
|
NRF_RADIO->EVENTS_BCMATCH = 0;
|
nrf_ppi_channels_enable(NRF_PPI, PPI_CHEN_CH23_Msk);
|
nrf_radio_bcc_set(NRF_RADIO, (BLE_LL_PDU_HDR_LEN + adva_offset +
|
BLE_DEV_ADDR_LEN) * 8 + g_ble_phy_data.phy_bcc_offset);
|
}
|
#endif
|
|
/* Call Link Layer receive start function */
|
rc = ble_ll_rx_start(dptr + 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;
|
} else {
|
/* Disable PHY */
|
ble_phy_disable();
|
STATS_INC(ble_phy_stats, rx_aborts);
|
}
|
|
/* Count rx starts */
|
STATS_INC(ble_phy_stats, rx_starts);
|
|
return true;
|
}
|
|
static void
|
ble_phy_isr(void)
|
{
|
uint32_t irq_en;
|
|
os_trace_isr_enter();
|
|
/* Read irq register to determine which interrupts are enabled */
|
irq_en = NRF_RADIO->INTENSET;
|
|
/*
|
* NOTE: order of checking is important! Possible, if things get delayed,
|
* we have both an ADDRESS and DISABLED interrupt in rx state. If we get
|
* an address, we disable the DISABLED interrupt.
|
*/
|
|
/* We get this if we have started to receive a frame */
|
if ((irq_en & RADIO_INTENCLR_ADDRESS_Msk) && NRF_RADIO->EVENTS_ADDRESS) {
|
/*
|
* wfr timer is calculated to expire at the exact time we should start
|
* receiving a packet (with 1 usec precision) so it is possible it will
|
* fire at the same time as EVENT_ADDRESS. If this happens, radio will
|
* be disabled while we are waiting for EVENT_BCCMATCH after 1st byte
|
* of payload is received and ble_phy_rx_start_isr() will fail. In this
|
* case we should not clear DISABLED irq mask so it will be handled as
|
* regular radio disabled event below. In other case radio was disabled
|
* on purpose and there's nothing more to handle so we can clear mask.
|
*/
|
if (ble_phy_rx_start_isr()) {
|
irq_en &= ~RADIO_INTENCLR_DISABLED_Msk;
|
}
|
}
|
|
/* Handle disabled event. This is enabled for both TX and RX. On RX, we
|
* need to check phy_rx_started flag to make sure we actually were receiving
|
* a PDU, otherwise this is due to wfr.
|
*/
|
if ((irq_en & RADIO_INTENCLR_DISABLED_Msk) && NRF_RADIO->EVENTS_DISABLED) {
|
BLE_LL_ASSERT(NRF_RADIO->EVENTS_END ||
|
((g_ble_phy_data.phy_state == BLE_PHY_STATE_RX) &&
|
!g_ble_phy_data.phy_rx_started));
|
NRF_RADIO->EVENTS_END = 0;
|
NRF_RADIO->EVENTS_DISABLED = 0;
|
nrf_radio_int_disable(NRF_RADIO, RADIO_INTENCLR_DISABLED_Msk);
|
|
switch (g_ble_phy_data.phy_state) {
|
case BLE_PHY_STATE_RX:
|
#if MYNEWT_VAL(BLE_LL_LNA)
|
NRF_PPI->CHENCLR = PPI_CHEN_CH6_Msk | PPI_CHEN_CH7_Msk;
|
ble_ll_plna_lna_disable();
|
#endif
|
if (g_ble_phy_data.phy_rx_started) {
|
ble_phy_rx_end_isr();
|
} else {
|
ble_ll_wfr_timer_exp(NULL);
|
}
|
break;
|
case BLE_PHY_STATE_TX:
|
#if MYNEWT_VAL(BLE_LL_PA)
|
NRF_PPI->CHENCLR = PPI_CHEN_CH6_Msk | PPI_CHEN_CH7_Msk;
|
ble_ll_plna_pa_disable();
|
#endif
|
ble_phy_tx_end_isr();
|
break;
|
default:
|
BLE_LL_ASSERT(0);
|
}
|
}
|
|
g_ble_phy_data.phy_transition_late = 0;
|
|
/* Count # of interrupts */
|
STATS_INC(ble_phy_stats, phy_isrs);
|
|
os_trace_isr_exit();
|
}
|
|
#if MYNEWT_VAL(BLE_PHY_DBG_TIME_TXRXEN_READY_PIN) >= 0 || \
|
MYNEWT_VAL(BLE_PHY_DBG_TIME_ADDRESS_END_PIN) >= 0 || \
|
MYNEWT_VAL(BLE_PHY_DBG_TIME_WFR_PIN) >= 0 || \
|
MYNEWT_VAL(BLE_LL_PA) || \
|
MYNEWT_VAL(BLE_LL_LNA)
|
static int
|
ble_phy_gpiote_configure(int pin)
|
{
|
NRF_GPIO_Type *port;
|
|
g_ble_phy_gpiote_idx--;
|
|
#if NRF52840_XXAA
|
port = pin > 31 ? NRF_P1 : NRF_P0;
|
pin &= 0x1f;
|
#else
|
port = NRF_P0;
|
#endif
|
|
/* Configure GPIO directly to avoid dependency to hal_gpio (for porting) */
|
port->DIRSET = (1 << pin);
|
port->OUTCLR = (1 << pin);
|
|
NRF_GPIOTE->CONFIG[g_ble_phy_gpiote_idx] =
|
(GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos) |
|
((pin & 0x1F) << GPIOTE_CONFIG_PSEL_Pos) |
|
#if NRF52840_XXAA
|
((port == NRF_P1) << GPIOTE_CONFIG_PORT_Pos);
|
#else
|
0;
|
#endif
|
|
BLE_LL_ASSERT(g_ble_phy_gpiote_idx >= 0);
|
|
return g_ble_phy_gpiote_idx;
|
}
|
#endif
|
|
static void
|
ble_phy_dbg_time_setup(void)
|
{
|
int idx __attribute__((unused));
|
|
/*
|
* We setup GPIOTE starting from last configuration index to minimize risk
|
* of conflict with GPIO setup via hal. It's not great solution, but since
|
* this is just debugging code we can live with this.
|
*/
|
|
#if MYNEWT_VAL(BLE_PHY_DBG_TIME_TXRXEN_READY_PIN) >= 0
|
idx = ble_phy_gpiote_configure(MYNEWT_VAL(BLE_PHY_DBG_TIME_TXRXEN_READY_PIN));
|
|
nrf_ppi_channel_endpoint_setup(NRF_PPI, 17, (uint32_t)&(NRF_RADIO->EVENTS_READY),
|
(uint32_t)&(NRF_GPIOTE->TASKS_CLR[idx]));
|
nrf_ppi_channels_enable(NRF_PPI, PPI_CHEN_CH17_Msk);
|
|
/* CH[20] and PPI CH[21] are on to trigger TASKS_TXEN or TASKS_RXEN */
|
nrf_ppi_fork_endpoint_setup(NRF_PPI, 20, (uint32_t)&(NRF_GPIOTE->TASKS_SET[idx]));
|
nrf_ppi_fork_endpoint_setup(NRF_PPI, 21, (uint32_t)&(NRF_GPIOTE->TASKS_SET[idx]));
|
#endif
|
|
#if MYNEWT_VAL(BLE_PHY_DBG_TIME_ADDRESS_END_PIN) >= 0
|
idx = ble_phy_gpiote_configure(MYNEWT_VAL(BLE_PHY_DBG_TIME_ADDRESS_END_PIN));
|
|
/* CH[26] and CH[27] are always on for EVENT_ADDRESS and EVENT_END */
|
nrf_ppi_fork_endpoint_setup(NRF_PPI, 26, (uint32_t)&(NRF_GPIOTE->TASKS_SET[idx]));
|
nrf_ppi_fork_endpoint_setup(NRF_PPI, 27, (uint32_t)&(NRF_GPIOTE->TASKS_CLR[idx]));
|
#endif
|
|
#if MYNEWT_VAL(BLE_PHY_DBG_TIME_WFR_PIN) >= 0
|
idx = ble_phy_gpiote_configure(MYNEWT_VAL(BLE_PHY_DBG_TIME_WFR_PIN));
|
|
#if NRF52840_XXAA
|
nrf_ppi_channel_endpoint_setup(NRF_PPI, 18, (uint32_t)&(NRF_RADIO->EVENTS_RXREADY),
|
(uint32_t)&(NRF_GPIOTE->TASKS_SET[idx]));
|
#else
|
nrf_ppi_channel_endpoint_setup(NRF_PPI, 18, (uint32_t)&(NRF_RADIO->EVENTS_READY),
|
(uint32_t)&(NRF_GPIOTE->TASKS_SET[idx]));
|
#endif
|
nrf_ppi_channel_endpoint_setup(NRF_PPI, 19, (uint32_t)&(NRF_RADIO->EVENTS_DISABLED),
|
(uint32_t)&(NRF_GPIOTE->TASKS_CLR[idx]));
|
nrf_ppi_channels_enable(NRF_PPI, PPI_CHEN_CH18_Msk | PPI_CHEN_CH19_Msk);
|
|
/* CH[4] and CH[5] are always on for wfr */
|
nrf_ppi_fork_endpoint_setup(NRF_PPI, 4, (uint32_t)&(NRF_GPIOTE->TASKS_CLR[idx]));
|
nrf_ppi_fork_endpoint_setup(NRF_PPI, 5, (uint32_t)&(NRF_GPIOTE->TASKS_CLR[idx]));
|
#endif
|
}
|
|
/**
|
* ble phy init
|
*
|
* Initialize the PHY.
|
*
|
* @return int 0: success; PHY error code otherwise
|
*/
|
int
|
ble_phy_init(void)
|
{
|
int rc;
|
|
g_ble_phy_gpiote_idx = 8;
|
|
/* Default phy to use is 1M */
|
g_ble_phy_data.phy_cur_phy_mode = BLE_PHY_MODE_1M;
|
g_ble_phy_data.phy_tx_phy_mode = BLE_PHY_MODE_1M;
|
g_ble_phy_data.phy_rx_phy_mode = BLE_PHY_MODE_1M;
|
|
g_ble_phy_data.rx_pwr_compensation = 0;
|
|
/* Set phy channel to an invalid channel so first set channel works */
|
g_ble_phy_data.phy_chan = BLE_PHY_NUM_CHANS;
|
|
/* Toggle peripheral power to reset (just in case) */
|
nrf_radio_power_set(NRF_RADIO, false);
|
nrf_radio_power_set(NRF_RADIO, true);
|
|
/* Disable all interrupts */
|
nrf_radio_int_disable(NRF_RADIO, NRF_RADIO_IRQ_MASK_ALL);
|
|
/* Set configuration registers */
|
NRF_RADIO->MODE = RADIO_MODE_MODE_Ble_1Mbit;
|
NRF_RADIO->PCNF0 = NRF_PCNF0;
|
|
/* XXX: should maxlen be 251 for encryption? */
|
NRF_RADIO->PCNF1 = NRF_MAXLEN |
|
(RADIO_PCNF1_ENDIAN_Little << RADIO_PCNF1_ENDIAN_Pos) |
|
(NRF_BALEN << RADIO_PCNF1_BALEN_Pos) |
|
RADIO_PCNF1_WHITEEN_Msk;
|
|
/* Enable radio fast ramp-up */
|
NRF_RADIO->MODECNF0 |= (RADIO_MODECNF0_RU_Fast << RADIO_MODECNF0_RU_Pos) &
|
RADIO_MODECNF0_RU_Msk;
|
|
/* Set logical address 1 for TX and RX */
|
NRF_RADIO->TXADDRESS = 0;
|
NRF_RADIO->RXADDRESSES = (1 << 0);
|
|
/* Configure the CRC registers */
|
NRF_RADIO->CRCCNF = (RADIO_CRCCNF_SKIPADDR_Skip << RADIO_CRCCNF_SKIPADDR_Pos) | RADIO_CRCCNF_LEN_Three;
|
|
/* Configure BLE poly */
|
NRF_RADIO->CRCPOLY = 0x0000065B;
|
|
/* Configure IFS */
|
NRF_RADIO->TIFS = BLE_LL_IFS;
|
|
/* Captures tx/rx start in timer0 cc 1 and tx/rx end in timer0 cc 2 */
|
nrf_ppi_channels_enable(NRF_PPI, PPI_CHEN_CH26_Msk | PPI_CHEN_CH27_Msk);
|
|
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION)
|
nrf_ccm_int_disable(NRF_CCM, 0xffffffff);
|
NRF_CCM->SHORTS = CCM_SHORTS_ENDKSGEN_CRYPT_Msk;
|
NRF_CCM->EVENTS_ERROR = 0;
|
memset(g_nrf_encrypt_scratchpad, 0, sizeof(g_nrf_encrypt_scratchpad));
|
#endif
|
|
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
|
g_ble_phy_data.phy_aar_scratch = 0;
|
NRF_AAR->IRKPTR = (uint32_t)&g_nrf_irk_list[0];
|
nrf_aar_int_disable(NRF_AAR, 0xffffffff);
|
NRF_AAR->EVENTS_END = 0;
|
NRF_AAR->EVENTS_RESOLVED = 0;
|
NRF_AAR->EVENTS_NOTRESOLVED = 0;
|
NRF_AAR->NIRK = 0;
|
#endif
|
|
/* TIMER0 setup for PHY when using RTC */
|
nrf_timer_task_trigger(NRF_TIMER0, NRF_TIMER_TASK_STOP);
|
NRF_TIMER0->TASKS_SHUTDOWN = 1;
|
NRF_TIMER0->BITMODE = 3; /* 32-bit timer */
|
NRF_TIMER0->MODE = 0; /* Timer mode */
|
NRF_TIMER0->PRESCALER = 4; /* gives us 1 MHz */
|
|
/*
|
* PPI setup.
|
* Channel 4: Captures TIMER0 in CC[3] when EVENTS_ADDRESS occurs. Used
|
* to cancel the wait for response timer.
|
* Channel 5: TIMER0 CC[3] to TASKS_DISABLE on radio. This is the wait
|
* for response timer.
|
*/
|
nrf_ppi_channel_endpoint_setup(NRF_PPI, NRF_PPI_CHANNEL4,
|
(uint32_t)&(NRF_RADIO->EVENTS_ADDRESS),
|
(uint32_t)&(NRF_TIMER0->TASKS_CAPTURE[3]));
|
nrf_ppi_channel_endpoint_setup(NRF_PPI, NRF_PPI_CHANNEL5,
|
(uint32_t)&(NRF_TIMER0->EVENTS_COMPARE[3]),
|
(uint32_t)&(NRF_RADIO->TASKS_DISABLE));
|
|
#if MYNEWT_VAL(BLE_LL_PA) || MYNEWT_VAL(BLE_LL_LNA)
|
#if PLNA_SINGLE_GPIO
|
plna_idx = ble_phy_gpiote_configure(MYNEWT_VAL(BLE_LL_PA_GPIO));
|
NRF_PPI->CH[6].TEP = (uint32_t) &(NRF_GPIOTE->TASKS_SET[plna_idx]);
|
NRF_PPI->CH[7].TEP = (uint32_t) &(NRF_GPIOTE->TASKS_CLR[plna_idx]);
|
#else
|
#if MYNEWT_VAL(BLE_LL_PA)
|
plna_pa_idx = ble_phy_gpiote_configure(MYNEWT_VAL(BLE_LL_PA_GPIO));
|
NRF_GPIOTE->TASKS_CLR[plna_pa_idx] = 1;
|
#endif
|
#if MYNEWT_VAL(BLE_LL_LNA)
|
plna_lna_idx = ble_phy_gpiote_configure(MYNEWT_VAL(BLE_LL_LNA_GPIO));
|
NRF_GPIOTE->TASKS_CLR[plna_lna_idx] = 1;
|
#endif
|
#endif
|
|
NRF_PPI->CH[6].EEP = (uint32_t)&(NRF_RADIO->EVENTS_READY);
|
NRF_PPI->CH[7].EEP = (uint32_t)&(NRF_RADIO->EVENTS_DISABLED);
|
NRF_PPI->CHENCLR = PPI_CHEN_CH6_Msk | PPI_CHEN_CH7_Msk;
|
#endif
|
|
/* Set isr in vector table and enable interrupt */
|
#ifndef RIOT_VERSION
|
NVIC_SetPriority(RADIO_IRQn, 0);
|
#endif
|
#if MYNEWT
|
NVIC_SetVector(RADIO_IRQn, (uint32_t)ble_phy_isr);
|
#else
|
ble_npl_hw_set_isr(RADIO_IRQn, ble_phy_isr);
|
#endif
|
NVIC_EnableIRQ(RADIO_IRQn);
|
|
/* Register phy statistics */
|
if (!g_ble_phy_data.phy_stats_initialized) {
|
rc = stats_init_and_reg(STATS_HDR(ble_phy_stats),
|
STATS_SIZE_INIT_PARMS(ble_phy_stats,
|
STATS_SIZE_32),
|
STATS_NAME_INIT_PARMS(ble_phy_stats),
|
"ble_phy");
|
assert(rc == 0);
|
|
g_ble_phy_data.phy_stats_initialized = 1;
|
}
|
|
ble_phy_dbg_time_setup();
|
|
return 0;
|
}
|
|
/**
|
* Puts the phy into receive mode.
|
*
|
* @return int 0: success; BLE Phy error code otherwise
|
*/
|
int
|
ble_phy_rx(void)
|
{
|
/*
|
* Check radio state.
|
*
|
* In case radio is now disabling we'll wait for it to finish, but if for
|
* any reason it's just in idle state we proceed with RX as usual since
|
* nRF52 radio can ramp-up from idle state as well.
|
*
|
* Note that TX and RX states values are the same except for 3rd bit so we
|
* can make a shortcut here when checking for idle state.
|
*/
|
nrf_wait_disabled();
|
if ((NRF_RADIO->STATE != RADIO_STATE_STATE_Disabled) &&
|
((NRF_RADIO->STATE & 0x07) != RADIO_STATE_STATE_RxIdle)) {
|
ble_phy_disable();
|
STATS_INC(ble_phy_stats, radio_state_errs);
|
return BLE_PHY_ERR_RADIO_STATE;
|
}
|
|
/* Make sure all interrupts are disabled */
|
nrf_radio_int_disable(NRF_RADIO, NRF_RADIO_IRQ_MASK_ALL);
|
|
/* Clear events prior to enabling receive */
|
NRF_RADIO->EVENTS_END = 0;
|
NRF_RADIO->EVENTS_DISABLED = 0;
|
|
/* Setup for rx */
|
ble_phy_rx_xcvr_setup();
|
|
/* PPI to start radio automatically shall be set here */
|
assert(NRF_PPI->CHEN & PPI_CHEN_CH21_Msk);
|
|
return 0;
|
}
|
|
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION)
|
/**
|
* Called to enable encryption at the PHY. Note that this state will persist
|
* in the PHY; in other words, if you call this function you have to call
|
* disable so that future PHY transmits/receives will not be encrypted.
|
*
|
* @param pkt_counter
|
* @param iv
|
* @param key
|
* @param is_master
|
*/
|
void
|
ble_phy_encrypt_enable(uint64_t pkt_counter, uint8_t *iv, uint8_t *key,
|
uint8_t is_master)
|
{
|
memcpy(g_nrf_ccm_data.key, key, 16);
|
g_nrf_ccm_data.pkt_counter = pkt_counter;
|
memcpy(g_nrf_ccm_data.iv, iv, 8);
|
g_nrf_ccm_data.dir_bit = is_master;
|
g_ble_phy_data.phy_encrypted = 1;
|
/* Enable the module (AAR cannot be on while CCM on) */
|
NRF_AAR->ENABLE = AAR_ENABLE_ENABLE_Disabled;
|
NRF_CCM->ENABLE = CCM_ENABLE_ENABLE_Enabled;
|
}
|
|
void
|
ble_phy_encrypt_set_pkt_cntr(uint64_t pkt_counter, int dir)
|
{
|
g_nrf_ccm_data.pkt_counter = pkt_counter;
|
g_nrf_ccm_data.dir_bit = dir;
|
}
|
|
void
|
ble_phy_encrypt_disable(void)
|
{
|
nrf_ppi_channels_disable(NRF_PPI, PPI_CHEN_CH25_Msk);
|
nrf_ccm_task_trigger(NRF_CCM, NRF_CCM_TASK_STOP);
|
NRF_CCM->EVENTS_ERROR = 0;
|
NRF_CCM->ENABLE = CCM_ENABLE_ENABLE_Disabled;
|
|
g_ble_phy_data.phy_encrypted = 0;
|
}
|
#endif
|
|
void
|
ble_phy_set_txend_cb(ble_phy_tx_end_func txend_cb, void *arg)
|
{
|
/* Set transmit end callback and arg */
|
g_ble_phy_data.txend_cb = txend_cb;
|
g_ble_phy_data.txend_arg = arg;
|
}
|
|
/**
|
* Called to set the start time of a transmission.
|
*
|
* This function is called to set the start time when we are not going from
|
* rx to tx automatically.
|
*
|
* NOTE: care must be taken when calling this function. The channel should
|
* already be set.
|
*
|
* @param cputime This is the tick at which the 1st bit of the preamble
|
* should be transmitted
|
* @param rem_usecs This is used only when the underlying timing uses a 32.768
|
* kHz crystal. It is the # of usecs from the cputime tick
|
* at which the first bit of the preamble should be
|
* transmitted.
|
* @return int
|
*/
|
int
|
ble_phy_tx_set_start_time(uint32_t cputime, uint8_t rem_usecs)
|
{
|
int rc;
|
|
ble_phy_trace_u32x2(BLE_PHY_TRACE_ID_START_TX, cputime, rem_usecs);
|
|
#if (BLE_LL_BT5_PHY_SUPPORTED == 1)
|
ble_phy_mode_apply(g_ble_phy_data.phy_tx_phy_mode);
|
#endif
|
|
/* XXX: This should not be necessary, but paranoia is good! */
|
/* Clear timer0 compare to RXEN since we are transmitting */
|
nrf_ppi_channels_disable(NRF_PPI, PPI_CHEN_CH21_Msk);
|
|
if (ble_phy_set_start_time(cputime, rem_usecs, true) != 0) {
|
STATS_INC(ble_phy_stats, tx_late);
|
ble_phy_disable();
|
rc = BLE_PHY_ERR_TX_LATE;
|
} else {
|
/* Enable PPI to automatically start TXEN */
|
nrf_ppi_channels_enable(NRF_PPI, PPI_CHEN_CH20_Msk);
|
rc = 0;
|
|
ble_phy_plna_enable_pa();
|
}
|
|
return rc;
|
}
|
|
/**
|
* Called to set the start time of a reception
|
*
|
* This function acts a bit differently than transmit. If we are late getting
|
* here we will still attempt to receive.
|
*
|
* NOTE: care must be taken when calling this function. The channel should
|
* already be set.
|
*
|
* @param cputime
|
*
|
* @return int
|
*/
|
int
|
ble_phy_rx_set_start_time(uint32_t cputime, uint8_t rem_usecs)
|
{
|
bool late = false;
|
int rc = 0;
|
|
ble_phy_trace_u32x2(BLE_PHY_TRACE_ID_START_RX, cputime, rem_usecs);
|
|
#if (BLE_LL_BT5_PHY_SUPPORTED == 1)
|
ble_phy_mode_apply(g_ble_phy_data.phy_rx_phy_mode);
|
#endif
|
|
/* XXX: This should not be necessary, but paranoia is good! */
|
/* Clear timer0 compare to TXEN since we are transmitting */
|
nrf_ppi_channels_disable(NRF_PPI, PPI_CHEN_CH20_Msk);
|
|
if (ble_phy_set_start_time(cputime, rem_usecs, false) != 0) {
|
STATS_INC(ble_phy_stats, rx_late);
|
|
/* We're late so let's just try to start RX as soon as possible */
|
ble_phy_set_start_now();
|
|
late = true;
|
}
|
|
/* Enable PPI to automatically start RXEN */
|
nrf_ppi_channels_enable(NRF_PPI, PPI_CHEN_CH21_Msk);
|
|
ble_phy_plna_enable_lna();
|
|
/* Start rx */
|
rc = ble_phy_rx();
|
|
/*
|
* If we enabled receiver but were late, let's return proper error code so
|
* caller can handle this.
|
*/
|
if (!rc && late) {
|
rc = BLE_PHY_ERR_RX_LATE;
|
}
|
|
return rc;
|
}
|
|
int
|
ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg, uint8_t end_trans)
|
{
|
int rc;
|
uint8_t *dptr;
|
uint8_t *pktptr;
|
uint8_t payload_len;
|
uint8_t hdr_byte;
|
uint32_t state;
|
uint32_t shortcuts;
|
|
if (g_ble_phy_data.phy_transition_late) {
|
ble_phy_disable();
|
STATS_INC(ble_phy_stats, tx_late);
|
return BLE_PHY_ERR_TX_LATE;
|
}
|
|
/*
|
* This check is to make sure that the radio is not in a state where
|
* it is moving to disabled state. If so, let it get there.
|
*/
|
nrf_wait_disabled();
|
|
/*
|
* XXX: Although we may not have to do this here, I clear all the PPI
|
* that should not be used when transmitting. Some of them are only enabled
|
* if encryption and/or privacy is on, but I dont care. Better to be
|
* paranoid, and if you are going to clear one, might as well clear them
|
* all.
|
*/
|
nrf_ppi_channels_disable(NRF_PPI, PPI_CHEN_CH4_Msk | PPI_CHEN_CH5_Msk |
|
PPI_CHEN_CH23_Msk | PPI_CHEN_CH25_Msk);
|
|
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION)
|
if (g_ble_phy_data.phy_encrypted) {
|
dptr = (uint8_t *)&g_ble_phy_enc_buf[0];
|
pktptr = (uint8_t *)&g_ble_phy_tx_buf[0];
|
NRF_CCM->SHORTS = CCM_SHORTS_ENDKSGEN_CRYPT_Msk;
|
NRF_CCM->INPTR = (uint32_t)dptr;
|
NRF_CCM->OUTPTR = (uint32_t)pktptr;
|
NRF_CCM->SCRATCHPTR = (uint32_t)&g_nrf_encrypt_scratchpad[0];
|
NRF_CCM->EVENTS_ERROR = 0;
|
NRF_CCM->MODE = CCM_MODE_LENGTH_Msk | ble_phy_get_ccm_datarate();
|
NRF_CCM->CNFPTR = (uint32_t)&g_nrf_ccm_data;
|
} else {
|
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
|
NRF_AAR->IRKPTR = (uint32_t)&g_nrf_irk_list[0];
|
#endif
|
dptr = (uint8_t *)&g_ble_phy_tx_buf[0];
|
pktptr = dptr;
|
}
|
#else
|
dptr = (uint8_t *)&g_ble_phy_tx_buf[0];
|
pktptr = dptr;
|
#endif
|
|
/* Set PDU payload */
|
payload_len = pducb(&dptr[3], pducb_arg, &hdr_byte);
|
|
/* RAM representation has S0, LENGTH and S1 fields. (3 bytes) */
|
dptr[0] = hdr_byte;
|
dptr[1] = payload_len;
|
dptr[2] = 0;
|
|
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION)
|
/* Start key-stream generation and encryption (via short) */
|
if (g_ble_phy_data.phy_encrypted) {
|
nrf_ccm_task_trigger(NRF_CCM, NRF_CCM_TASK_KSGEN);
|
}
|
#endif
|
|
NRF_RADIO->PACKETPTR = (uint32_t)pktptr;
|
|
/* Clear the ready, end and disabled events */
|
NRF_RADIO->EVENTS_READY = 0;
|
NRF_RADIO->EVENTS_END = 0;
|
NRF_RADIO->EVENTS_DISABLED = 0;
|
|
/* Enable shortcuts for transmit start/end. */
|
shortcuts = RADIO_SHORTS_END_DISABLE_Msk | RADIO_SHORTS_READY_START_Msk;
|
NRF_RADIO->SHORTS = shortcuts;
|
nrf_radio_int_enable(NRF_RADIO, RADIO_INTENSET_DISABLED_Msk);
|
|
/* Set the PHY transition */
|
g_ble_phy_data.phy_transition = end_trans;
|
|
/* Set transmitted payload length */
|
g_ble_phy_data.phy_tx_pyld_len = payload_len;
|
|
/* If we already started transmitting, abort it! */
|
state = NRF_RADIO->STATE;
|
if (state != RADIO_STATE_STATE_Tx) {
|
/* Set phy state to transmitting and count packet statistics */
|
g_ble_phy_data.phy_state = BLE_PHY_STATE_TX;
|
STATS_INC(ble_phy_stats, tx_good);
|
STATS_INCN(ble_phy_stats, tx_bytes, payload_len + BLE_LL_PDU_HDR_LEN);
|
rc = BLE_ERR_SUCCESS;
|
} else {
|
ble_phy_disable();
|
STATS_INC(ble_phy_stats, tx_late);
|
rc = BLE_PHY_ERR_RADIO_STATE;
|
}
|
|
return rc;
|
}
|
|
/**
|
* ble phy txpwr set
|
*
|
* Set the transmit output power (in dBm).
|
*
|
* NOTE: If the output power specified is within the BLE limits but outside
|
* the chip limits, we "rail" the power level so we dont exceed the min/max
|
* chip values.
|
*
|
* @param dbm Power output in dBm.
|
*
|
* @return int 0: success; anything else is an error
|
*/
|
int
|
ble_phy_txpwr_set(int dbm)
|
{
|
/* "Rail" power level if outside supported range */
|
dbm = ble_phy_txpower_round(dbm);
|
|
NRF_RADIO->TXPOWER = dbm;
|
g_ble_phy_data.phy_txpwr_dbm = dbm;
|
|
return 0;
|
}
|
|
/**
|
* ble phy txpwr round
|
*
|
* Get the rounded transmit output power (in dBm).
|
*
|
* @param dbm Power output in dBm.
|
*
|
* @return int Rounded power in dBm
|
*/
|
int ble_phy_txpower_round(int dbm)
|
{
|
/* TODO this should be per nRF52XXX */
|
|
/* "Rail" power level if outside supported range */
|
if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Pos4dBm) {
|
return (int8_t)RADIO_TXPOWER_TXPOWER_Pos4dBm;
|
}
|
|
if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Pos3dBm) {
|
return (int8_t)RADIO_TXPOWER_TXPOWER_Pos3dBm;
|
}
|
|
if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_0dBm) {
|
return (int8_t)RADIO_TXPOWER_TXPOWER_0dBm;
|
}
|
|
if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg4dBm) {
|
return (int8_t)RADIO_TXPOWER_TXPOWER_Neg4dBm;
|
}
|
|
if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg8dBm) {
|
return (int8_t)RADIO_TXPOWER_TXPOWER_Neg8dBm;
|
}
|
|
if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg12dBm) {
|
return (int8_t)RADIO_TXPOWER_TXPOWER_Neg12dBm;
|
}
|
|
if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg20dBm) {
|
return (int8_t)RADIO_TXPOWER_TXPOWER_Neg20dBm;
|
}
|
|
return (int8_t)RADIO_TXPOWER_TXPOWER_Neg40dBm;
|
}
|
|
/**
|
* ble phy set access addr
|
*
|
* Set access address.
|
*
|
* @param access_addr Access address
|
*
|
* @return int 0: success; PHY error code otherwise
|
*/
|
static int
|
ble_phy_set_access_addr(uint32_t access_addr)
|
{
|
NRF_RADIO->BASE0 = (access_addr << 8);
|
NRF_RADIO->PREFIX0 = (NRF_RADIO->PREFIX0 & 0xFFFFFF00) | (access_addr >> 24);
|
|
g_ble_phy_data.phy_access_address = access_addr;
|
|
#ifndef BABBLESIM
|
ble_phy_apply_errata_102_106_107();
|
#endif
|
return 0;
|
}
|
|
/**
|
* ble phy txpwr get
|
*
|
* Get the transmit power.
|
*
|
* @return int The current PHY transmit power, in dBm
|
*/
|
int
|
ble_phy_txpwr_get(void)
|
{
|
return g_ble_phy_data.phy_txpwr_dbm;
|
}
|
|
void
|
ble_phy_set_rx_pwr_compensation(int8_t compensation)
|
{
|
g_ble_phy_data.rx_pwr_compensation = compensation;
|
}
|
|
/**
|
* ble phy setchan
|
*
|
* Sets the logical frequency of the transceiver. The input parameter is the
|
* BLE channel index (0 to 39, inclusive). The NRF frequency register works like
|
* this: logical frequency = 2400 + FREQ (MHz).
|
*
|
* Thus, to get a logical frequency of 2402 MHz, you would program the
|
* FREQUENCY register to 2.
|
*
|
* @param chan This is the Data Channel Index or Advertising Channel index
|
*
|
* @return int 0: success; PHY error code otherwise
|
*/
|
int
|
ble_phy_setchan(uint8_t chan, uint32_t access_addr, uint32_t crcinit)
|
{
|
assert(chan < BLE_PHY_NUM_CHANS);
|
|
/* Check for valid channel range */
|
if (chan >= BLE_PHY_NUM_CHANS) {
|
return BLE_PHY_ERR_INV_PARAM;
|
}
|
|
/* Set current access address */
|
ble_phy_set_access_addr(access_addr);
|
|
/* Configure crcinit */
|
NRF_RADIO->CRCINIT = crcinit;
|
|
/* Set the frequency and the data whitening initial value */
|
g_ble_phy_data.phy_chan = chan;
|
NRF_RADIO->FREQUENCY = g_ble_phy_chan_freq[chan];
|
NRF_RADIO->DATAWHITEIV = chan;
|
|
return 0;
|
}
|
|
/**
|
* Stop the timer used to count microseconds when using RTC for cputime
|
*/
|
static void
|
ble_phy_stop_usec_timer(void)
|
{
|
nrf_timer_task_trigger(NRF_TIMER0, NRF_TIMER_TASK_STOP);
|
NRF_TIMER0->TASKS_SHUTDOWN = 1;
|
nrf_rtc_event_disable(NRF_RTC0, RTC_EVTENSET_COMPARE0_Msk);
|
}
|
|
/**
|
* ble phy disable irq and ppi
|
*
|
* This routine is to be called when reception was stopped due to either a
|
* wait for response timeout or a packet being received and the phy is to be
|
* restarted in receive mode. Generally, the disable routine is called to stop
|
* the phy.
|
*/
|
static void
|
ble_phy_disable_irq_and_ppi(void)
|
{
|
nrf_radio_int_disable(NRF_RADIO, NRF_RADIO_IRQ_MASK_ALL);
|
NRF_RADIO->SHORTS = 0;
|
nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_DISABLE);
|
nrf_ppi_channels_disable(NRF_PPI, PPI_CHEN_CH4_Msk | PPI_CHEN_CH5_Msk |
|
PPI_CHEN_CH20_Msk | PPI_CHEN_CH21_Msk | PPI_CHEN_CH23_Msk |
|
PPI_CHEN_CH25_Msk | PPI_CHEN_CH31_Msk);
|
nrf_ppi_channels_disable(NRF_PPI, PPI_CHEN_CH6_Msk | PPI_CHEN_CH7_Msk);
|
NVIC_ClearPendingIRQ(RADIO_IRQn);
|
g_ble_phy_data.phy_state = BLE_PHY_STATE_IDLE;
|
}
|
|
void
|
ble_phy_restart_rx(void)
|
{
|
ble_phy_stop_usec_timer();
|
ble_phy_disable_irq_and_ppi();
|
|
ble_phy_set_start_now();
|
/* Enable PPI to automatically start RXEN */
|
nrf_ppi_channels_enable(NRF_PPI, PPI_CHEN_CH21_Msk);
|
|
ble_phy_rx();
|
}
|
|
/**
|
* ble phy disable
|
*
|
* Disables the PHY. This should be called when an event is over. It stops
|
* the usec timer (if used), disables interrupts, disables the RADIO, disables
|
* PPI and sets state to idle.
|
*/
|
void
|
ble_phy_disable(void)
|
{
|
ble_phy_trace_void(BLE_PHY_TRACE_ID_DISABLE);
|
|
ble_phy_stop_usec_timer();
|
ble_phy_disable_irq_and_ppi();
|
}
|
|
/* Gets the current access address */
|
uint32_t ble_phy_access_addr_get(void)
|
{
|
return g_ble_phy_data.phy_access_address;
|
}
|
|
/**
|
* Return the phy state
|
*
|
* @return int The current PHY state.
|
*/
|
int
|
ble_phy_state_get(void)
|
{
|
return g_ble_phy_data.phy_state;
|
}
|
|
/**
|
* Called to see if a reception has started
|
*
|
* @return int
|
*/
|
int
|
ble_phy_rx_started(void)
|
{
|
return g_ble_phy_data.phy_rx_started;
|
}
|
|
/**
|
* Return the transceiver state
|
*
|
* @return int transceiver state.
|
*/
|
uint8_t
|
ble_phy_xcvr_state_get(void)
|
{
|
uint32_t state;
|
state = NRF_RADIO->STATE;
|
return (uint8_t)state;
|
}
|
|
/**
|
* Called to return the maximum data pdu payload length supported by the
|
* phy. For this chip, if encryption is enabled, the maximum payload is 27
|
* bytes.
|
*
|
* @return uint8_t Maximum data channel PDU payload size supported
|
*/
|
uint8_t
|
ble_phy_max_data_pdu_pyld(void)
|
{
|
return BLE_LL_DATA_PDU_MAX_PYLD;
|
}
|
|
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
|
void
|
ble_phy_resolv_list_enable(void)
|
{
|
NRF_AAR->NIRK = (uint32_t)g_nrf_num_irks;
|
g_ble_phy_data.phy_privacy = 1;
|
}
|
|
void
|
ble_phy_resolv_list_disable(void)
|
{
|
g_ble_phy_data.phy_privacy = 0;
|
}
|
#endif
|
|
#if MYNEWT_VAL(BLE_LL_DTM)
|
void ble_phy_enable_dtm(void)
|
{
|
/* When DTM is enabled we need to disable whitening as per
|
* Bluetooth v5.0 Vol 6. Part F. 4.1.1
|
*/
|
NRF_RADIO->PCNF1 &= ~RADIO_PCNF1_WHITEEN_Msk;
|
}
|
|
void ble_phy_disable_dtm(void)
|
{
|
/* Enable whitening */
|
NRF_RADIO->PCNF1 |= RADIO_PCNF1_WHITEEN_Msk;
|
}
|
#endif
|
|
void
|
ble_phy_rfclk_enable(void)
|
{
|
#if MYNEWT
|
nrf52_clock_hfxo_request();
|
#else
|
nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_HFCLKSTART);
|
#endif
|
}
|
|
void
|
ble_phy_rfclk_disable(void)
|
{
|
#if MYNEWT
|
nrf52_clock_hfxo_release();
|
#else
|
nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_HFCLKSTOP);
|
#endif
|
}
|