This commit is contained in:
Andrzej Kuroś 2024-04-09 11:33:04 +02:00 committed by GitHub
commit 6ca45c3a08
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 481 additions and 240 deletions

View File

@ -41,11 +41,14 @@
#include "nrf_802154_assert.h"
#include <string.h>
#include "hal/nrf_ecb.h"
#include "nrf_802154_const.h"
#include "nrf_802154_config.h"
#include "nrf_802154_tx_work_buffer.h"
#include "platform/nrf_802154_irq.h"
#if defined(CONFIG_MPSL)
#include "mpsl_ecb.h"
#else
#include "hal/nrf_ecb.h"
#endif
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b)) ///< Leaves the minimum of the two arguments
@ -90,56 +93,117 @@ static uint8_t m_m[NRF_802154_AES_CCM_BLOCK_SIZE];
static uint8_t m_a[NRF_802154_AES_CCM_BLOCK_SIZE]; ///< A[i] octet for Encryption Transformation - Annex B4.1.3 b)
static ccm_state_t m_state; ///< State of AES-CCM* transformation
static uint8_t m_auth_tag[MIC_128_SIZE]; ///< Authorization Tag
static bool m_initialized; ///< Flag that indicates whether the module has been initialized.
static uint8_t * mp_ciphertext; ///< Pointer to ciphertext destination buffer.
static uint8_t * mp_work_buffer; ///< Pointer to work buffer that stores the frame being transformed.
static const uint8_t m_mic_size[] = { 0, MIC_32_SIZE, MIC_64_SIZE, MIC_128_SIZE }; ///< Security level - 802.15.4-2015 Standard Table 9.6
/******************************************************************************/
/******************************************************************************/
/******************************************************************************/
static uint8_t m_ecb_data[48]; ///< ECB data structure for RNG peripheral to access.
static uint8_t * mp_ecb_key; ///< Key: Starts at ecb_data
static uint8_t * mp_ecb_cleartext; ///< Cleartext: Starts at ecb_data + 16 bytes.
static uint8_t * mp_ecb_ciphertext; ///< Ciphertext: Starts at ecb_data + 32 bytes.
static void nrf_ecb_init(void)
typedef struct
{
mp_ecb_key = m_ecb_data;
mp_ecb_cleartext = m_ecb_data + 16;
mp_ecb_ciphertext = m_ecb_data + 32;
uint32_t key[NRF_802154_AES_CCM_BLOCK_SIZE / sizeof(uint32_t)];
uint8_t cleartext[NRF_802154_AES_CCM_BLOCK_SIZE];
uint8_t ciphertext[NRF_802154_AES_CCM_BLOCK_SIZE];
} nrf_802154_hal_ecb_data_t;
nrf_ecb_data_pointer_set(NRF_ECB, m_ecb_data);
/******************************************************************************/
/******************************************************************************/
/******************************************************************************/
static nrf_802154_hal_ecb_data_t m_ecb_hal_data;
static bool m_ecb_hal_req_run;
#if defined(CONFIG_MPSL)
static inline void ecb_block_encrypt(nrf_802154_hal_ecb_data_t * p_ecb_data)
{
/* Note: nrf_802154_hal_ecb_data_t and mpsl_ecb_hal_data_t are equivalent
* and pointers to them can be safely cast.
*/
mpsl_ecb_block_encrypt((mpsl_ecb_hal_data_t *)p_ecb_data);
}
static void nrf_ecb_set_key(const uint8_t * p_key)
#else
#define ECB_INST NRF_ECB
static inline void sleep_wfe(void)
{
memcpy(mp_ecb_key, p_key, 16);
#if defined(CONFIG_SOC_SERIES_BSIM_NRFXX)
void z_impl_k_busy_wait();
z_impl_k_busy_wait(10);
#elif defined(CONFIG_SOC_COMPATIBLE_NRF52X)
__WFE();
#else
/* Do-nothing. This includes nRF5340 series due multiple sleep-related anomalies (160, 165, 168) */
#endif
}
static void ecb_irq_handler(void);
/**
* @brief Initializes the ECB peripheral.
*/
static void ecb_init(void)
static void wait_for_ecb_end(void)
{
if (!m_initialized)
while (!nrf_ecb_event_check(ECB_INST, NRF_ECB_EVENT_ENDECB) &&
!nrf_ecb_event_check(ECB_INST, NRF_ECB_EVENT_ERRORECB))
{
nrf_802154_irq_init(nrfx_get_irq_number(NRF_ECB), NRF_802154_ECB_PRIORITY, ecb_irq_handler);
m_initialized = true;
#if !defined(CONFIG_SOC_SERIES_BSIM_NRFXX)
if ((SCB->SCR & SCB_SCR_SEVONPEND_Msk) == SCB_SCR_SEVONPEND_Msk)
#endif
{
NVIC_ClearPendingIRQ(ECB_IRQn);
uint32_t irq_was_masked = __get_PRIMASK();
__disable_irq();
nrf_ecb_int_enable(ECB_INST, NRF_ECB_INT_ENDECB_MASK | NRF_ECB_INT_ERRORECB_MASK);
if (!nrf_ecb_event_check(ECB_INST, NRF_ECB_EVENT_ENDECB) &&
!nrf_ecb_event_check(ECB_INST, NRF_ECB_EVENT_ERRORECB))
{
sleep_wfe();
}
if (!irq_was_masked)
{
__enable_irq();
}
}
}
}
// TODO: ensure ECB initialization is handled by zephyr
// TODO: what about ECB initialization in baremetal scenario?
nrf_ecb_init();
static void ecb_block_encrypt(nrf_802154_hal_ecb_data_t * p_ecb_data)
{
nrf_ecb_int_disable(ECB_INST, NRF_ECB_INT_ENDECB_MASK | NRF_ECB_INT_ERRORECB_MASK);
nrf_802154_irq_clear_pending(nrfx_get_irq_number(NRF_ECB));
nrf_802154_irq_enable(nrfx_get_irq_number(NRF_ECB));
nrf_ecb_int_enable(NRF_ECB, NRF_ECB_INT_ENDECB_MASK);
nrf_ecb_int_enable(NRF_ECB, NRF_ECB_INT_ERRORECB_MASK);
do
{
nrf_ecb_task_trigger(ECB_INST, NRF_ECB_TASK_STOPECB);
nrf_ecb_event_clear(ECB_INST, NRF_ECB_EVENT_ENDECB);
nrf_ecb_event_clear(ECB_INST, NRF_ECB_EVENT_ERRORECB);
nrf_ecb_data_pointer_set(ECB_INST, p_ecb_data);
nrf_ecb_task_trigger(ECB_INST, NRF_ECB_TASK_STARTECB);
wait_for_ecb_end();
}
while (nrf_ecb_event_check(ECB_INST, NRF_ECB_EVENT_ERRORECB));
nrf_ecb_int_disable(ECB_INST, NRF_ECB_INT_ENDECB_MASK | NRF_ECB_INT_ERRORECB_MASK);
nrf_ecb_event_clear(ECB_INST, NRF_ECB_EVENT_ERRORECB);
nrf_ecb_event_clear(ECB_INST, NRF_ECB_EVENT_ENDECB);
NVIC_ClearPendingIRQ(ECB_IRQn);
}
#endif /* defined(CONFIG_MPSL) */
static inline uint8_t * ecb_hal_cleartext_ptr_get(void)
{
return (uint8_t *)m_ecb_hal_data.cleartext;
}
static inline uint8_t * ecb_hal_ciphertext_ptr_get(void)
{
return (uint8_t *)m_ecb_hal_data.ciphertext;
}
static void ecb_hal_key_set(const uint8_t * p_key)
{
memcpy(m_ecb_hal_data.key, p_key, NRF_802154_AES_CCM_BLOCK_SIZE);
}
/******************************************************************************/
@ -316,9 +380,11 @@ static bool plain_text_data_get(const nrf_802154_aes_ccm_data_t * p_frame,
static inline void process_ecb_auth_iteration(void)
{
m_state.iteration++;
two_blocks_xor(mp_ecb_ciphertext, m_b, NRF_802154_AES_CCM_BLOCK_SIZE);
memcpy(mp_ecb_cleartext, mp_ecb_ciphertext, NRF_802154_AES_CCM_BLOCK_SIZE);
nrf_ecb_task_trigger(NRF_ECB, NRF_ECB_TASK_STARTECB);
two_blocks_xor(ecb_hal_ciphertext_ptr_get(), m_b, NRF_802154_AES_CCM_BLOCK_SIZE);
memcpy(ecb_hal_cleartext_ptr_get(),
ecb_hal_ciphertext_ptr_get(),
NRF_802154_AES_CCM_BLOCK_SIZE);
m_ecb_hal_req_run = true;
}
/**
@ -327,8 +393,8 @@ static inline void process_ecb_auth_iteration(void)
static inline void process_ecb_encrypt_iteration(void)
{
ai_format(&m_aes_ccm_data, m_state.iteration, m_a);
memcpy(mp_ecb_cleartext, m_a, NRF_802154_AES_CCM_BLOCK_SIZE);
nrf_ecb_task_trigger(NRF_ECB, NRF_ECB_TASK_STARTECB);
memcpy(ecb_hal_cleartext_ptr_get(), m_a, NRF_802154_AES_CCM_BLOCK_SIZE);
m_ecb_hal_req_run = true;
}
/**
@ -336,7 +402,7 @@ static inline void process_ecb_encrypt_iteration(void)
*/
static void perform_plain_text_encryption(void)
{
memcpy(m_auth_tag, mp_ecb_ciphertext, m_mic_size[m_aes_ccm_data.mic_level]);
memcpy(m_auth_tag, ecb_hal_ciphertext_ptr_get(), m_mic_size[m_aes_ccm_data.mic_level]);
m_state.iteration = 0;
m_state.transformation = PLAIN_TEXT_ENCRYPT;
@ -377,99 +443,72 @@ static void transformation_finished(void)
m_aes_ccm_data.raw_frame = NULL;
}
/**
* @brief Handler to ECB Interrupt Routine
* Performs AES-CCM* calculation in pipeline
*/
static void ecb_irq_handler(void)
static void ecb_hal_block_encrypted_handler(void)
{
uint8_t len = 0;
uint8_t offset;
if (nrf_ecb_int_enable_check(NRF_ECB, NRF_ECB_INT_ENDECB_MASK) &&
nrf_ecb_event_check(NRF_ECB, NRF_ECB_EVENT_ENDECB))
switch (m_state.transformation)
{
nrf_ecb_event_clear(NRF_ECB, NRF_ECB_EVENT_ENDECB);
case ADD_AUTH_DATA_AUTH:
if (add_auth_data_get(&m_aes_ccm_data, m_state.iteration, m_b))
{
process_ecb_auth_iteration();
}
else
{
m_state.iteration = 0;
m_state.transformation = PLAIN_TEXT_AUTH;
perform_plain_text_authorization();
}
break;
switch (m_state.transformation)
{
case ADD_AUTH_DATA_AUTH:
if (add_auth_data_get(&m_aes_ccm_data, m_state.iteration, m_b))
{
process_ecb_auth_iteration();
}
else
case PLAIN_TEXT_AUTH:
perform_plain_text_authorization();
break;
case PLAIN_TEXT_ENCRYPT:
two_blocks_xor(m_m, ecb_hal_ciphertext_ptr_get(), NRF_802154_AES_CCM_BLOCK_SIZE);
offset = (m_state.iteration - 1) * NRF_802154_AES_CCM_BLOCK_SIZE;
len = MIN(m_aes_ccm_data.plain_text_data_len - offset,
NRF_802154_AES_CCM_BLOCK_SIZE);
memcpy(mp_ciphertext + offset, m_m, len);
if (plain_text_data_get(&m_aes_ccm_data, m_state.iteration, m_m))
{
m_state.iteration++;
process_ecb_encrypt_iteration();
}
else
{
if (m_mic_size[m_aes_ccm_data.mic_level] != 0)
{
m_state.iteration = 0;
m_state.transformation = PLAIN_TEXT_AUTH;
perform_plain_text_authorization();
}
break;
case PLAIN_TEXT_AUTH:
perform_plain_text_authorization();
break;
case PLAIN_TEXT_ENCRYPT:
two_blocks_xor(m_m, mp_ecb_ciphertext, NRF_802154_AES_CCM_BLOCK_SIZE);
offset = (m_state.iteration - 1) * NRF_802154_AES_CCM_BLOCK_SIZE;
len = MIN(m_aes_ccm_data.plain_text_data_len - offset,
NRF_802154_AES_CCM_BLOCK_SIZE);
memcpy(mp_ciphertext + offset, m_m, len);
if (plain_text_data_get(&m_aes_ccm_data, m_state.iteration, m_m))
{
m_state.iteration++;
m_state.transformation = CALCULATE_ENCRYPTED_TAG;
process_ecb_encrypt_iteration();
}
else
{
if (m_mic_size[m_aes_ccm_data.mic_level] != 0)
{
m_state.iteration = 0;
m_state.transformation = CALCULATE_ENCRYPTED_TAG;
process_ecb_encrypt_iteration();
}
else
{
transformation_finished();
}
transformation_finished();
}
break;
}
break;
case CALCULATE_ENCRYPTED_TAG:
two_blocks_xor(m_auth_tag,
mp_ecb_ciphertext,
m_mic_size[m_aes_ccm_data.mic_level]);
memcpy(mp_work_buffer +
(mp_work_buffer[PHR_OFFSET] - FCS_SIZE -
m_mic_size[m_aes_ccm_data.mic_level] +
PHR_SIZE),
m_auth_tag,
m_mic_size[m_aes_ccm_data.mic_level]);
transformation_finished();
break;
case CALCULATE_ENCRYPTED_TAG:
two_blocks_xor(m_auth_tag,
ecb_hal_ciphertext_ptr_get(),
m_mic_size[m_aes_ccm_data.mic_level]);
memcpy(mp_work_buffer +
(mp_work_buffer[PHR_OFFSET] - FCS_SIZE -
m_mic_size[m_aes_ccm_data.mic_level] +
PHR_SIZE),
m_auth_tag,
m_mic_size[m_aes_ccm_data.mic_level]);
transformation_finished();
break;
default:
break;
}
}
if (nrf_ecb_int_enable_check(NRF_ECB, NRF_ECB_INT_ERRORECB_MASK) &&
nrf_ecb_event_check(NRF_ECB, NRF_ECB_EVENT_ERRORECB))
{
/*
* It is possible that the ERRORECB event is caused by the
* AAR and CCM peripherals, which share the same hardware resources.
* At this point it is assumed, that ECB, AAR and CCM peripherals
* are not used by anything, except the 802.15.4 driver and
* other MPSL clients and thus it is impossible that ECB was aborted
* for any other reason, than the TX failed event caused by a terminated
* 802.15.4 transmit operation or end of timeslot.
*
* Therefore no action is taken in this handler.
*/
nrf_ecb_event_clear(NRF_ECB, NRF_ECB_EVENT_ERRORECB);
default:
break;
}
}
@ -478,11 +517,18 @@ static void ecb_irq_handler(void)
*/
static void start_ecb_auth_transformation(void)
{
memcpy((uint8_t *)nrf_ecb_data_pointer_get(NRF_ECB) + 16, m_x, 16);
ecb_hal_key_set(m_aes_ccm_data.key);
memcpy(ecb_hal_cleartext_ptr_get(), m_x, NRF_802154_AES_CCM_BLOCK_SIZE);
m_state.iteration = 0;
m_state.transformation = ADD_AUTH_DATA_AUTH;
nrf_ecb_event_clear(NRF_ECB, NRF_ECB_EVENT_ENDECB);
nrf_ecb_task_trigger(NRF_ECB, NRF_ECB_TASK_STARTECB);
m_ecb_hal_req_run = true;
while (m_ecb_hal_req_run)
{
m_ecb_hal_req_run = false;
ecb_block_encrypt(&m_ecb_hal_data);
ecb_hal_block_encrypted_handler();
}
}
void nrf_802154_aes_ccm_transform_reset(void)
@ -554,9 +600,6 @@ void nrf_802154_aes_ccm_transform_start(uint8_t * p_frame)
b0_format(&m_aes_ccm_data, auth_flags, p_b);
two_blocks_xor(p_x, p_b, NRF_802154_AES_CCM_BLOCK_SIZE);
ecb_init();
memset(mp_ecb_key, 0, 48);
nrf_ecb_set_key(m_aes_ccm_data.key);
start_ecb_auth_transformation();
}
@ -568,16 +611,6 @@ void nrf_802154_aes_ccm_transform_abort(uint8_t * p_frame)
return;
}
/*
* Temporarily disable ENDECB interrupt, trigger STOPECB task
* to stop encryption in case it is still running and clear
* the ENDECB event in case the encryption has completed.
*/
nrf_ecb_int_disable(NRF_ECB, NRF_ECB_INT_ENDECB_MASK);
nrf_ecb_task_trigger(NRF_ECB, NRF_ECB_TASK_STOPECB);
nrf_ecb_event_clear(NRF_ECB, NRF_ECB_EVENT_ENDECB);
nrf_ecb_int_enable(NRF_ECB, NRF_ECB_INT_ENDECB_MASK);
m_aes_ccm_data.raw_frame = NULL;
}

View File

@ -758,7 +758,7 @@ static void operation_terminated_notify(radio_state_t state, bool receiving_psdu
nrf_802154_transmit_done_metadata_t metadata = {};
nrf_802154_tx_work_buffer_original_frame_update(mp_tx_data, &metadata.frame_props);
transmit_failed_notify(mp_tx_data, NRF_802154_TX_ERROR_ABORTED, &metadata);
transmit_failed_notify(mp_tx_data, NRF_802154_TX_ERROR_NO_ACK, &metadata);
}
break;
@ -843,16 +843,6 @@ static bool current_operation_terminate(nrf_802154_term_t term_lvl,
return result;
}
/** Enter Sleep state. */
static void sleep_init(void)
{
// This function is always executed from a critical section, so this check is safe.
if (timeslot_is_granted())
{
nrf_802154_timer_coord_stop();
}
}
/** Initialize Falling Asleep operation. */
static void falling_asleep_init(void)
{
@ -862,7 +852,6 @@ static void falling_asleep_init(void)
}
else
{
sleep_init();
state_set(RADIO_STATE_SLEEP);
}
}
@ -1212,7 +1201,6 @@ static void switch_to_idle(void)
{
if (!nrf_802154_pib_rx_on_when_idle_get())
{
sleep_init();
state_set(RADIO_STATE_SLEEP);
}
else
@ -1884,7 +1872,6 @@ void nrf_802154_trx_go_idle_finished(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
sleep_init();
state_set(RADIO_STATE_SLEEP);
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
@ -2093,12 +2080,43 @@ void nrf_802154_trx_receive_frame_received(void)
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}
static bool fcf_is_security_enabled(const uint8_t * p_frame)
{
return p_frame[SECURITY_ENABLED_OFFSET] & SECURITY_ENABLED_BIT;
}
static inline bool tx_started_core_hooks_will_fit_within_timeslot(const uint8_t * p_frame)
{
if (!fcf_is_security_enabled(p_frame))
{
return true;
}
uint32_t estimated_max_hook_time = nrf_802154_frame_duration_get(p_frame[0], false, true) / 2U;
return nrf_802154_rsch_timeslot_us_left_get() >= estimated_max_hook_time;
}
void nrf_802154_trx_transmit_frame_started(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
NRF_802154_ASSERT((m_state == RADIO_STATE_TX) || (m_state == RADIO_STATE_CCA_TX));
transmit_started_notify();
if (tx_started_core_hooks_will_fit_within_timeslot(mp_tx_data))
{
transmit_started_notify();
}
else
{
nrf_802154_trx_abort();
switch_to_idle();
nrf_802154_transmit_done_metadata_t metadata = {};
nrf_802154_tx_work_buffer_original_frame_update(mp_tx_data, &metadata.frame_props);
transmit_failed_notify(mp_tx_data, NRF_802154_TX_ERROR_TIMESLOT_ENDED, &metadata);
}
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}
@ -2108,7 +2126,21 @@ void nrf_802154_trx_transmit_ack_started(void)
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
NRF_802154_ASSERT(m_state == RADIO_STATE_TX_ACK);
transmit_ack_started_notify();
if (tx_started_core_hooks_will_fit_within_timeslot(mp_ack))
{
transmit_ack_started_notify();
}
else
{
uint8_t * p_received_data = mp_current_rx_buffer->data;
nrf_802154_trx_abort();
mp_current_rx_buffer->free = false;
nrf_802154_core_hooks_tx_ack_failed(mp_ack, NRF_802154_RX_ERROR_TIMESLOT_ENDED);
switch_to_idle();
received_frame_notify_and_nesting_allow(p_received_data);
}
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}
@ -2523,7 +2555,6 @@ static bool core_sleep(nrf_802154_term_t term_lvl, req_originator_t req_orig, bo
}
else
{
sleep_init();
state_set(RADIO_STATE_SLEEP);
}
}

View File

@ -47,7 +47,6 @@
#include "nrf_802154_config.h"
#include "nrf_802154_core.h"
#include "nrf_802154_critical_section.h"
#include "nrf_802154_debug.h"
#include "nrf_802154_queue.h"
#include "nrf_802154_rx_buffer.h"
#include "nrf_802154_swi.h"

View File

@ -612,9 +612,9 @@ static void fem_for_lna_reset(void)
*
* @note This function must be called before ramp up PPIs are configured.
*/
static void fem_for_pa_set(const mpsl_fem_gain_t * p_fem_gain_data)
static void fem_for_pa_set(mpsl_fem_pa_power_control_t pa_power_control)
{
(void)mpsl_fem_pa_gain_set(p_fem_gain_data);
(void)mpsl_fem_pa_power_control_set(pa_power_control);
if (mpsl_fem_pa_configuration_set(&m_activate_tx_cc0, NULL) == 0)
{
nrf_timer_shorts_enable(m_activate_tx_cc0.event.timer.p_timer_instance,
@ -641,11 +641,11 @@ static void fem_for_pa_reset(void)
*
* @note This function must be called before ramp up PPIs are configured.
*/
static void fem_for_tx_set(bool cca, const mpsl_fem_gain_t * p_fem_gain_data)
static void fem_for_tx_set(bool cca, mpsl_fem_pa_power_control_t pa_power_control)
{
bool success;
(void)mpsl_fem_pa_gain_set(p_fem_gain_data);
(void)mpsl_fem_pa_power_control_set(pa_power_control);
if (cca)
{
@ -764,11 +764,11 @@ static void pa_modulation_fix_apply(bool enable)
if (enable)
{
int8_t pa_gain = 0;
mpsl_fem_caps_t fem_caps = {};
mpsl_fem_pa_is_configured(&pa_gain);
mpsl_fem_caps_get(&fem_caps);
if (pa_gain > 0)
if ((fem_caps.flags & MPSL_FEM_CAPS_FLAG_PA_SETUP_REQUIRED) != 0)
{
m_pa_mod_filter_latched = *(p_radio_reg);
m_pa_mod_filter_is_latched = true;
@ -1401,7 +1401,7 @@ void nrf_802154_trx_receive_frame(uint8_t bcc,
}
// Set FEM PA gain for ACK transmission
mpsl_fem_pa_gain_set(&p_ack_tx_power->fem);
mpsl_fem_pa_power_control_set(p_ack_tx_power->fem_pa_power_control);
m_timer_value_on_radio_end_event = delta_time;
@ -1607,7 +1607,7 @@ void nrf_802154_trx_transmit_frame(const void * p_tra
nrf_radio_int_enable(NRF_RADIO, ints_to_enable);
fem_for_tx_set(cca, &p_tx_power->fem);
fem_for_tx_set(cca, p_tx_power->fem_pa_power_control);
nrf_802154_trx_antenna_update();
nrf_802154_trx_ppi_for_ramp_up_set(cca ? NRF_RADIO_TASK_RXEN : NRF_RADIO_TASK_TXEN,
rampup_trigg_mode,
@ -2180,7 +2180,7 @@ void nrf_802154_trx_continuous_carrier(const nrf_802154_fal_tx_power_split_t * p
txpower_set(p_tx_power->radio_tx_power);
// Set FEM
fem_for_pa_set(&p_tx_power->fem);
fem_for_pa_set(p_tx_power->fem_pa_power_control);
// Select antenna
nrf_802154_trx_antenna_update();
@ -2242,7 +2242,7 @@ void nrf_802154_trx_modulated_carrier(const void * p_
nrf_radio_shorts_set(NRF_RADIO, SHORTS_MOD_CARRIER);
// Set FEM
fem_for_pa_set(&p_tx_power->fem);
fem_for_pa_set(p_tx_power->fem_pa_power_control);
// Select antenna
nrf_802154_trx_antenna_update();

View File

@ -161,7 +161,7 @@ void nrf_802154_trx_ppi_for_ramp_up_propagation_delay_wait(void);
* @brief Detect if PPIs configured to start radio operation were triggered.
*
* In TRX_RAMP_UP_SW_TRIGGER mode, radio ramp up starts by design from RADIO DISABLED event.
* This functions verifies occurrence of this event and PPIs status.
* This function verifies occurrence of this event and PPIs status.
*
* The function is intended to be used only when all of the following conditions apply:
* - the connections are already made with @ref nrf_802154_trx_ppi_for_ramp_up_set

View File

@ -2339,7 +2339,7 @@ enum
*
* Specifies the preferred Router Id. Upon becoming a router/leader the node
* attempts to use this Router Id. If the preferred Router Id is not set or
* if it can not be used, a randomly generated router id is picked. This
* if it cannot be used, a randomly generated router id is picked. This
* property can be set only when the device role is either detached or
* disabled.
*

View File

@ -47,24 +47,26 @@
extern "C" {
#endif
/**
* @brief Represents components of tx_power to be applied for stages on transmit path.
*/
/** @brief Represents components of tx_power to be applied for stages on transmit path. */
typedef struct
{
int8_t radio_tx_power; // !< TX power in dBm to be applied to the RADIO peripheral.
mpsl_fem_gain_t fem; // !< Data needed to set the FEM gain
/** TX power to be applied to the RADIO peripheral. */
int8_t radio_tx_power;
/** PA power control to be applied to Front-End Module. */
mpsl_fem_pa_power_control_t fem_pa_power_control;
} nrf_802154_fal_tx_power_split_t;
/** @brief Splits transmit power value into components to be applied on each stage on the transmit path.
/** @brief Splits transmit power value into components to be applied on each stage on transmit path.
*
* @note This is a stub implementation used when MPSL is not linked.
* @note If the exact value of @p power cannot be achieved this function attempts to use less
* power to not exceed constraint.
*
* @param[in] channel Ignored.
* @param[in] channel 802.15.4 channel.
* @param[in] power TX power in dBm requested for transmission on air.
* @param[out] p_tx_power_split Components of tx_power to be applied for stages on transmit path.
*
* @returns The real achieved total transmission power in dBm.
* @return Actually achieved power in dBm.
*/
int8_t nrf_802154_fal_tx_power_split(const uint8_t channel,
const int8_t power,

View File

@ -58,6 +58,8 @@
#define NRF_802154_EGU_INSTANCE_NO 0
#elif defined(NRF54L_SERIES)
#define NRF_802154_EGU_INSTANCE_NO 10
#elif defined(NRF54H_SERIES)
#define NRF_802154_EGU_INSTANCE_NO 020
#endif
#endif
@ -222,4 +224,26 @@
#endif // defined(NRF54L_SERIES)
#if defined(NRF54H_SERIES)
/**
* @def NRF_802154_SL_DPPIC_INSTANCE_NO
*
* Id of the DPPIC instance used by the driver to connect peripherals to radio.
*
*/
#ifndef NRF_802154_SL_DPPIC_INSTANCE_NO
#define NRF_802154_SL_DPPIC_INSTANCE_NO 020
#endif
/**
* @def NRF_802154_SL_DPPIC_INSTANCE
*
* The DPPIC instance used by the driver to connect peripherals to radio.
*
*/
#define NRF_802154_SL_DPPIC_INSTANCE NRFX_CONCAT_2(NRF_DPPIC, NRF_802154_SL_DPPIC_INSTANCE_NO)
#endif // defined(NRF54H_SERIES)
#endif // NRF_802154_SL_PERIPHS_H__

View File

@ -0,0 +1,102 @@
/*
* Copyright (c) 2024, Nordic Semiconductor ASA
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
/**
* @brief Abstraction layer for peripheral-to-peripheral hardware connections needed for timestamping.
*/
#ifndef NRF_802154_PLATFORM_TIMESTAMPER_H_
#define NRF_802154_PLATFORM_TIMESTAMPER_H_
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Initializes the timestamper platform.
*/
void nrf_802154_platform_timestamper_init(void);
/**
* @brief Sets up cross-domain hardware connections necessary to capture a timestamp.
*
* This function configures cross-domain hardware connections necessary to capture a timestamp of
* an event from the local domain. These connections are identical for all local domain events.
*
* @note Every call to this function must be paired with a call to @ref
* nrf_802154_platform_timestamper_cross_domain_connections_clear.
*/
void nrf_802154_platform_timestamper_cross_domain_connections_setup(void);
/**
* @brief Clears cross-domain hardware connections necessary to capture a timestamp.
*/
void nrf_802154_platform_timestamper_cross_domain_connections_clear(void);
/**
* @brief Sets up local domain hardware connections necessary to capture a timestamp.
*
* This function configures local domain hardware connections necessary to capture a timestamp of
* an event from the local domain. These connections must be setup separately for every local domain
* event.
*
* @param[in] dppi_ch Local domain DPPI channel that the event to be timestamped publishes to.
*/
void nrf_802154_platform_timestamper_local_domain_connections_setup(uint32_t dppi_ch);
/**
* @brief Clears local domain hardware connections necessary to capture a timestamp.
*
* @param[in] dppi_ch Local domain DPPI channel that the event to be timestamped publishes to.
*/
void nrf_802154_platform_timestamper_local_domain_connections_clear(uint32_t dppi_ch);
/**
* @brief Reads timestamp captured using the configured hardware connections.
*
* @param[out] p_timestamp Captured timestamp. Only valid if @c true is returned, undefined otherwise.
*
* @retval true The timestamp was captured and read successfully.
* @retval false The timestamp could not be retrieved.
*/
bool nrf_802154_platform_timestamper_captured_timestamp_read(uint64_t * p_captured);
#ifdef __cplusplus
}
#endif
#endif // NRF_802154_PLATFORM_TIMESTAMPER_H_

View File

@ -32,7 +32,7 @@ typedef int8_t mpsl_tx_power_t;
* @param[in] req_radio_power Requested TX power desired for RADIO peripheral.
* The power value in dBm must be supported by the RADIO peripheral.
*
* @return RADIO.TXPOWER register code corrensponding to a radio power in dBm.
* @return RADIO.TXPOWER register code corresponding to radio power in dBm.
*/
uint32_t mpsl_tx_power_dbm_to_radio_register_convert(mpsl_tx_power_t req_radio_power);

View File

@ -49,13 +49,17 @@ typedef enum
* published to multiple PPI channels by hardware design, which makes it possible
* for multiple tasks to subscribe to it.
*
* For nRF53 series this is a number of a DPPI channel which is configured
* For nRF53 series, this is a number of a DPPI channel which is configured
* in such a way that certain event publishes to the DPPI channel and the
* DPPI channel is enabled. Ensuring above is responsibility of an user
* DPPI channel is enabled. Ensuring above is responsibility of a user
* of the provided API. Multiple tasks can then subscribe to the DPPI channel
* (by hardware design) thus indirectly to the event.
* (by hardware design), thus indirectly to the event.
*/
#ifdef PPI_PRESENT
typedef uint32_t mpsl_subscribable_hw_event_t;
#else
typedef uint8_t mpsl_subscribable_hw_event_t;
#endif
/** @brief MPSL Front End Module event. */
typedef struct
@ -83,15 +87,15 @@ typedef struct
/** Mask of the compare channels that can be used by the Front End Module to schedule its own tasks. */
uint8_t compare_channel_mask;
/** Event generated by the timer, used in case of type equal to @ref mpsl_fem_event_type_t::MPSL_FEM_EVENT_TYPE_TIMER. */
/** Event generated by the timer, used in case of type equal to @ref MPSL_FEM_EVENT_TYPE_TIMER. */
} timer;
/** Parameters when type is @ref MPSL_FEM_EVENT_TYPE_GENERIC. */
struct
{
/** Event triggerring required FEM operation. */
/** Event triggering required FEM operation. */
mpsl_subscribable_hw_event_t event;
/** Generic event, used in case of type equal to @ref mpsl_fem_event_type_t::MPSL_FEM_EVENT_TYPE_GENERIC. */
/** Generic event, used in case of type equal to @ref MPSL_FEM_EVENT_TYPE_GENERIC. */
} generic;
} event;
@ -106,15 +110,12 @@ typedef struct
/** TX power, dBm. */
typedef int8_t mpsl_tx_power_t;
/**
* @brief Represents data needed to set the FEM gain.
/** Type for PA power control to be applied to Front-End Module, depending on its type.
*
* The meaning of this type is FEM type-specific.
*/
typedef struct
{
int8_t gain_db; // !< Gain in dB.
uint8_t private_setting; // !< Setting of the Front-End Module. The interpretation of this field is specific
// for a given Front-End Module implementation.
} mpsl_fem_gain_t;
typedef uint8_t mpsl_fem_pa_power_control_t;
/** @brief Represents components of tx_power to be applied for stages on transmit path. */
typedef struct
@ -122,10 +123,43 @@ typedef struct
/** TX power to be applied to the RADIO peripheral. */
mpsl_tx_power_t radio_tx_power;
/** Data needed to set the FEM gain. */
mpsl_fem_gain_t fem;
/** FEM PA power control.*/
mpsl_fem_pa_power_control_t fem_pa_power_control;
} mpsl_tx_power_split_t;
/** @brief PA setup is required before starting a transmission.
*
* This flag applies to @ref mpsl_fem_caps_t::flags.
*
* If it is set, then @ref mpsl_fem_pa_configuration_set must be called before transmission starts.
*/
#define MPSL_FEM_CAPS_FLAG_PA_SETUP_REQUIRED (1U << 0)
/** @brief LNA setup is required before starting a reception.
*
* This flag applies to @ref mpsl_fem_caps_t::flags.
*
* If it is set, then @ref mpsl_fem_lna_configuration_set must be called before reception starts.
*/
#define MPSL_FEM_CAPS_FLAG_LNA_SETUP_REQUIRED (1U << 1)
/** @brief Structure representing capabilities and characteristics of the FEM in use. */
typedef struct
{
/** Flags informing about the FEM in use.
*
* The following flags apply:
* @ref MPSL_FEM_CAPS_FLAG_PA_SETUP_REQUIRED, @ref MPSL_FEM_CAPS_FLAG_LNA_SETUP_REQUIRED
*/
uint32_t flags;
} mpsl_fem_caps_t;
/** @brief Gets the capabilities of the FEM in use.
*
* @param[out] p_caps Pointer to the capabilities structure to be filled in.
*/
void mpsl_fem_caps_get(mpsl_fem_caps_t * p_caps);
/** @brief Disable Front End Module.
*
* Some Front End Module devices can be explicitly disabled after PA and LNA activities are
@ -153,13 +187,13 @@ int32_t mpsl_fem_disable(void);
* I.e. if you want to listen first and then send the frame, you need first to
* issue @ref mpsl_fem_lna_configuration_set and only after @ref mpsl_fem_pa_configuration_set.
*
* If a @ref mpsl_fem_event_type_t::MPSL_FEM_EVENT_TYPE_TIMER timer event is
* If a @ref MPSL_FEM_EVENT_TYPE_TIMER timer event is
* provided, the PA will be configured to activate or deactivate at the
* application-configured time gap before the timer instance reaches the given
* register_value. The time gap is set via the corresponding configuration setter
* of the selected Front End Module.
*
* If a @ref mpsl_fem_event_type_t::MPSL_FEM_EVENT_TYPE_GENERIC event is provided,
* If a @ref MPSL_FEM_EVENT_TYPE_GENERIC event is provided,
* the PA will be configured to activate or deactivate when an event occurs.
*
* The function sets up the PPIs and the GPIOTE channel to activate PA for the
@ -179,7 +213,7 @@ int32_t mpsl_fem_disable(void);
* the compare channel of the lowest ID among the provided ones does expire.
*
* @note The activation event can be only of type
* @ref mpsl_fem_event_type_t::MPSL_FEM_EVENT_TYPE_TIMER.
* @ref MPSL_FEM_EVENT_TYPE_TIMER.
* Using other activation event type leads to undefined module behavior.
*
* @retval 0 PA activation setup is successful.
@ -209,13 +243,13 @@ int32_t mpsl_fem_pa_configuration_clear(void);
* frame, you need first to issue @ref mpsl_fem_lna_configuration_set and only
* after @ref mpsl_fem_pa_configuration_set.
*
* If a @ref mpsl_fem_event_type_t::MPSL_FEM_EVENT_TYPE_TIMER timer event is
* If a @ref MPSL_FEM_EVENT_TYPE_TIMER timer event is
* provided, the LNA will be configured to activate or deactivate at the
* application-configured time gap before the timer instance reaches the given
* register_value. The time gap is set via the corresponding configuration setter
* of the selected Front End Module.
*
* If a @ref mpsl_fem_event_type_t::MPSL_FEM_EVENT_TYPE_GENERIC event is provided,
* If a @ref MPSL_FEM_EVENT_TYPE_GENERIC event is provided,
* the LNA will be configured to activate or deactivate when an event occurs.
*
* The function sets up the PPIs and the GPIOTE channel to activate LNA for the
@ -235,7 +269,7 @@ int32_t mpsl_fem_pa_configuration_clear(void);
* the compare channel of the lowest ID among the provided ones does expire.
*
* @note The activation event can be only of type
* @ref mpsl_fem_event_type_t::MPSL_FEM_EVENT_TYPE_TIMER. Using other activation
* @ref MPSL_FEM_EVENT_TYPE_TIMER. Using other activation
* event type leads to undefined module behavior.
*
* @retval 0 LNA activation setup is successful.
@ -317,40 +351,51 @@ void mpsl_fem_cleanup(void);
/** @brief Splits transmit power value into components to be applied on each stage on transmit path.
*
* @note If the exact value of @p power cannot be achieved, this function attempts to use less
* power to not exceed constraint.
* @note If the exact value of @p power cannot be achieved, this function attempts to either use
* available level lower than the requested level to not exceed constraint, or use the lowest
* available level greater than the requested level, depending on @p tx_power_ceiling.
*
* @param[in] power TX power requested for transmission on air.
* @param[out] p_tx_power_split Components of tx_power to be applied for stages on transmit path.
* If requested @p power is too high, the split will be set to
* a value representing maximum achievable power. If the requested
* @p power is too low, the split will be set to a value representing
* minimum achievable power.
* @param[in] power TX power requested for transmission on air.
* @param[out] p_tx_power_split Components of tx_power to be applied for stages on transmit path.
* If requested @p power is too high, the split will be set to
* a value representing maximum achievable power. If the requested
* @p power is too low, the split will be set to a value representing
* minimum achievable power.
* @param[in] freq_mhz Frequency in MHz to calculate the split for.
* @param[in] tx_power_ceiling Flag to get ceiling or floor of requested transmit power level.
*
* @return The power in dBm that will be achieved if values returned through @p p_tx_power_split
* are applied.
*/
void mpsl_fem_tx_power_split(const mpsl_tx_power_t power,
mpsl_tx_power_split_t *const p_tx_power_split);
int8_t mpsl_fem_tx_power_split(const mpsl_tx_power_t power,
mpsl_tx_power_split_t * const p_tx_power_split,
uint16_t freq_mhz,
bool tx_power_ceiling);
/** @brief Sets PA gain.
/** @brief Sets the PA power control.
*
* Setting the PA power control informs the FEM implementation how the PA is to be controlled
* before the next transmission.
*
* The PA power control set by this function is to be applied to control signals or
* parameters. What signals and parameters are controlled and how does it happen depends on
* implementation of given FEM. The meaning of @p pa_power_control parameter is
* fully FEM type-dependent. For FEM type-independent protocol implementation please
* use the function @ref mpsl_fem_tx_power_split and provide outcome of this function
* returned by the parameter @c p_tx_power_split to the call to @ref mpsl_fem_pa_power_control_set.
* For applications intended for testing the FEM itself when @ref mpsl_fem_tx_power_split is not used
* you must make the @p pa_power_control parameter on your own.
*
* @note The gain set by this function will be applied to radio transmissions
* @note The PA power control set by this function will be applied to radio transmissions
* following the call. If the function is called during radio transmission
* or during ramp-up for transmission it is unspecified if the gain is applied.
* or during ramp-up for transmission it is unspecified if the control is applied.
*
* @param[in] gain Gain in dB to be set.
* @param[in] pa_power_control PA power control to be applied to the FEM.
*
* @retval 0 Gain has been set successfully.
* @retval -NRF_EINVAL Gain could not be set. Provided @p gain is invalid.
* @retval 0 PA power control has been applied successfully.
* @retval -NRF_EINVAL PA power control could not be applied. Provided @p pa_power_control is invalid.
*/
int32_t mpsl_fem_pa_gain_set(const mpsl_fem_gain_t * p_gain);
/** @brief Checks if the PA signaling is configured and enabled, and gets
* the configured gain in dB.
*
* @param[out] p_gain The configured gain in dB if PA is configured and enabled.
* If there is no PA present or the PA does not affect
* the signal gain, returns 0 dB.
*/
void mpsl_fem_pa_is_configured(int8_t * const p_gain);
int32_t mpsl_fem_pa_power_control_set(mpsl_fem_pa_power_control_t pa_power_control);
/** @brief Prepares the Front End Module to switch to the Power Down state.
*

View File

@ -118,6 +118,11 @@ static radio_tx_power_t to_radio_tx_power_convert(int8_t integer_tx_power)
return allowed_values[0];
}
void mpsl_fem_caps_get(mpsl_fem_caps_t * p_caps)
{
*p_caps = (mpsl_fem_caps_t){};
}
int32_t mpsl_fem_disable(void)
{
return 0;
@ -156,7 +161,7 @@ void mpsl_fem_deactivate_now(mpsl_fem_functionality_t type)
(void)type;
}
int32_t mpsl_fem_abort_set(uint32_t event, uint32_t group)
int32_t mpsl_fem_abort_set(mpsl_subscribable_hw_event_t event, uint32_t group)
{
return -NRF_EPERM;
}
@ -185,26 +190,27 @@ void mpsl_fem_cleanup(void)
// Intentionally empty
}
void mpsl_fem_tx_power_split(const mpsl_tx_power_t power,
mpsl_tx_power_split_t * const p_tx_power_split)
int8_t mpsl_fem_tx_power_split(const mpsl_tx_power_t power,
mpsl_tx_power_split_t * const p_tx_power_split,
uint16_t freq_mhz,
bool tx_power_ceiling)
{
p_tx_power_split->radio_tx_power = to_radio_tx_power_convert(power).dbm;
p_tx_power_split->fem.gain_db = 0;
p_tx_power_split->fem.private_setting = 0;
(void)freq_mhz;
(void)tx_power_ceiling;
p_tx_power_split->radio_tx_power = to_radio_tx_power_convert(power).dbm;
p_tx_power_split->fem_pa_power_control = 0;
return p_tx_power_split->radio_tx_power;
}
int32_t mpsl_fem_pa_gain_set(const mpsl_fem_gain_t * p_gain)
int32_t mpsl_fem_pa_power_control_set(mpsl_fem_pa_power_control_t pa_power_control)
{
(void)p_gain;
(void)pa_power_control;
return 0;
}
void mpsl_fem_pa_is_configured(int8_t * const p_gain)
{
(void)p_gain;
}
bool mpsl_fem_prepare_powerdown(NRF_TIMER_Type * p_instance,
uint32_t compare_channel,
uint32_t ppi_id,
@ -228,9 +234,8 @@ int8_t nrf_802154_fal_tx_power_split(const uint8_t cha
{
(void)channel;
p_tx_power_split->radio_tx_power = to_radio_tx_power_convert(power).dbm;
p_tx_power_split->fem.gain_db = 0;
p_tx_power_split->fem.private_setting = 0;
p_tx_power_split->radio_tx_power = to_radio_tx_power_convert(power).dbm;
p_tx_power_split->fem_pa_power_control = 0;
return p_tx_power_split->radio_tx_power;
}