/***************************************************************************//** * @file * @brief Source file for RAIL Ram Modem Reconfiguration functionality ******************************************************************************* * # License * Copyright 2020 Silicon Laboratories Inc. www.silabs.com ******************************************************************************* * * SPDX-License-Identifier: Zlib * * The licensor of this software is Silicon Laboratories Inc. * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. * ******************************************************************************/ #include #include #include "rail.h" #include "em_common.h" #include "em_core.h" #include "em_device.h" #include "sli_rail_util_callbacks.h" #include "railapp_rmr.h" #include "railapp_malloc.h" #include "app_common.h" #include "response_print.h" typedef struct RMR_State{ uint32_t phyInfo[RMR_PHY_INFO_LEN]; uint8_t irCalConfig[RMR_IRCAL_LEN]; uint32_t modemConfigEntry[RMR_MODEM_CONFIG_LEN]; RAIL_FrameType_t frameTypeConfig; uint16_t frameLenList[RMR_FRAME_LENGTH_LIST_LEN]; uint32_t frameCodingTable[RMR_FRAME_CODING_TABLE_LEN]; RAIL_ChannelConfigEntryAttr_t generatedEntryAttr; RAIL_ChannelConfigEntry_t generatedChannels[1]; __ALIGNED(4) uint8_t convDecodeBuffer[RMR_CONV_DECODE_BUFFER_LEN]; RAIL_ChannelConfig_t channelConfig; #if (_SILICON_LABS_32B_SERIES_2_CONFIG >= 3) uint8_t dcdcRetimingConfig[RMR_DCDC_RETIMING_LEN]; #endif #if (_SILICON_LABS_32B_SERIES_2_CONFIG >= 2) uint8_t hfxoRetimingConfig[RMR_HFXO_RETIMING_LEN]; #endif } RMR_State_t; static RMR_State_t *rmrState = NULL; // Internal commands RAIL_Status_t Rmr_writeRmrStructure(RAIL_RMR_StructureIndex_t structure, uint16_t offset, uint8_t count, uint8_t *dataPtr); RAIL_Status_t Rmr_updateConfigurationPointer(uint8_t structToModify, uint16_t offset, uint8_t structToPointTo); RAIL_Status_t Rmr_reconfigureModem(RAIL_Handle_t railHandle); //---------------------------------------------------------------------------- // Ram Modem Reconfiguration Commands //----------------------------------------------------------------------------- RAIL_Status_t Rmr_updateConfigurationPointer(uint8_t structToModify, uint16_t offset, uint8_t structToPointTo) { uint32_t structPointer = 0u; // NULL // First get the addres of the structure we are trying to reference switch (structToPointTo) { case (RMR_STRUCT_PHY_INFO): { structPointer = (uint32_t)&(rmrState->phyInfo); break; } case (RMR_STRUCT_IRCAL_CONFIG): { structPointer = (uint32_t)&(rmrState->irCalConfig); break; } case (RMR_STRUCT_FRAME_TYPE_CONFIG): { structPointer = (uint32_t)&(rmrState->frameTypeConfig); break; } case (RMR_STRUCT_FRAME_LENGTH_LIST): { structPointer = (uint32_t)&(rmrState->frameLenList); break; } case (RMR_STRUCT_FRAME_CODING_TABLE): { structPointer = (uint32_t)&(rmrState->frameCodingTable); break; } case (RMR_STRUCT_CONV_DECODE_BUFFER): { structPointer = (uint32_t)&(rmrState->convDecodeBuffer); break; } case (RMR_STRUCT_NULL): { structPointer = 0u; // NULL break; } #if (_SILICON_LABS_32B_SERIES_2_CONFIG >= 3) case (RMR_STRUCT_DCDC_RETIMING_CONFIG): { structPointer = (uint32_t)&(rmrState->dcdcRetimingConfig); break; } #endif #if (_SILICON_LABS_32B_SERIES_2_CONFIG >= 2) case (RMR_STRUCT_HFXO_RETIMING_CONFIG): { structPointer = (uint32_t)&(rmrState->hfxoRetimingConfig); break; } #endif default: { // Error, unrecognized structure return RAIL_STATUS_INVALID_PARAMETER; } } switch (structToModify) { case (RMR_STRUCT_MODEM_CONFIG): { if (offset >= RMR_MODEM_CONFIG_LEN) { return RAIL_STATUS_INVALID_PARAMETER; } rmrState->modemConfigEntry[offset] = structPointer; break; } case (RMR_STRUCT_PHY_INFO): { if (offset >= RMR_PHY_INFO_LEN) { return RAIL_STATUS_INVALID_PARAMETER; } rmrState->phyInfo[offset] = structPointer; break; } case (RMR_STRUCT_FRAME_TYPE_CONFIG): { if (offset != 0) { return RAIL_STATUS_INVALID_PARAMETER; } rmrState->frameTypeConfig.frameLen = (uint16_t *)structPointer; break; } default: { // Error unrecognized structure return RAIL_STATUS_INVALID_PARAMETER; } } return RAIL_STATUS_NO_ERROR; } RAIL_Status_t Rmr_reconfigureModem(RAIL_Handle_t railHandle) { RAIL_RadioState_t currentState = RAIL_GetRadioState(railHandle); if (currentState != RAIL_RF_STATE_IDLE) { return RAIL_STATUS_INVALID_STATE; } disableIncompatibleProtocols(RAIL_PTI_PROTOCOL_CUSTOM); // Always include frame type length functionality when using RMR or config // channels won't work for frame type based lengths. RAIL_IncludeFrameTypeLength(railHandle); // Configure with the downloaded channel configuration. RAIL_ConfigChannels(railHandle, &rmrState->channelConfig, &sli_rail_util_on_channel_config_change); // Make sure that we stay in idle after the reconfiguration. RAIL_Idle(railHandle, RAIL_IDLE_FORCE_SHUTDOWN_CLEAR_FLAGS, false); return RAIL_STATUS_NO_ERROR; } //----------------------------------------------------------------------------- // RMR CI Commands //----------------------------------------------------------------------------- RAIL_Status_t Rmr_writeRmrStructure(RAIL_RMR_StructureIndex_t structure, uint16_t offset, uint8_t count, uint8_t *dataPtr) { uint8_t *targetStruct; uint32_t size; switch (structure) { case (RMR_STRUCT_PHY_INFO): { size = sizeof(rmrState->phyInfo); targetStruct = (uint8_t *) &(rmrState->phyInfo); break; } case (RMR_STRUCT_IRCAL_CONFIG): { size = sizeof(rmrState->irCalConfig); targetStruct = (uint8_t *) &(rmrState->irCalConfig); break; } case (RMR_STRUCT_MODEM_CONFIG): { size = sizeof(rmrState->modemConfigEntry); targetStruct = (uint8_t *) &(rmrState->modemConfigEntry); break; } case (RMR_STRUCT_FRAME_TYPE_CONFIG): { size = sizeof(rmrState->frameTypeConfig); targetStruct = (uint8_t *) &(rmrState->frameTypeConfig); break; } case (RMR_STRUCT_FRAME_LENGTH_LIST): { size = sizeof(rmrState->frameLenList); targetStruct = (uint8_t *) &(rmrState->frameLenList); break; } case (RMR_STRUCT_FRAME_CODING_TABLE): { size = RMR_FRAME_CODING_TABLE_LEN * sizeof(uint32_t); targetStruct = (uint8_t *) &(rmrState->frameCodingTable); break; } case (RMR_STRUCT_CHANNEL_CONFIG_ATTRIBUTES): { size = sizeof(rmrState->generatedEntryAttr); targetStruct = (uint8_t *) &(rmrState->generatedEntryAttr); break; } case (RMR_STRUCT_CHANNEL_CONFIG_ENTRY): { size = sizeof(rmrState->generatedChannels); targetStruct = (uint8_t *) &(rmrState->generatedChannels); break; } #if (_SILICON_LABS_32B_SERIES_2_CONFIG >= 3) case (RMR_STRUCT_DCDC_RETIMING_CONFIG): { size = sizeof(rmrState->dcdcRetimingConfig); targetStruct = (uint8_t *) &(rmrState->dcdcRetimingConfig); break; } #endif #if (_SILICON_LABS_32B_SERIES_2_CONFIG >= 2) case (RMR_STRUCT_HFXO_RETIMING_CONFIG): { size = sizeof(rmrState->hfxoRetimingConfig); targetStruct = (uint8_t *) &(rmrState->hfxoRetimingConfig); break; } #endif default: { return RAIL_STATUS_INVALID_PARAMETER; break; } } // Check that we are not writing out of bounds if ((offset + count) > size) { return RAIL_STATUS_INVALID_PARAMETER; } targetStruct += offset; while (count-- != 0u) { *targetStruct = *dataPtr; targetStruct++; dataPtr++; } return RAIL_STATUS_NO_ERROR; } static bool rmrInit(sl_cli_command_arg_t *args) { if (rmrState == NULL) { rmrState = RAILAPP_Malloc(sizeof(RMR_State_t)); if (rmrState == NULL) { responsePrintError(sl_cli_get_command_string(args, 0), 0x86, "Error allocating RMR memory."); return false; } rmrState->channelConfig.phyConfigBase = &(rmrState->modemConfigEntry[0]); rmrState->channelConfig.phyConfigDeltaSubtract = NULL; rmrState->channelConfig.configs = rmrState->generatedChannels; rmrState->channelConfig.length = 1; rmrState->channelConfig.signature = 0U; rmrState->generatedChannels[0].phyConfigDeltaAdd = NULL; rmrState->generatedChannels[0].attr = &rmrState->generatedEntryAttr; } return true; } void CI_writeRmrStructure(sl_cli_command_arg_t *args) { uint8_t count = sl_cli_get_argument_uint8(args, RMR_CI_COUNT); uint8_t bufferedData[RMR_ARGUMENT_BUFFER_SIZE]; if (!rmrInit(args)) { return; } if (sl_cli_get_argument_count(args) != (count + RMR_CI_DATA_START)) { responsePrintError(sl_cli_get_command_string(args, 0), 0x80, "Argument count does not match number of arguments."); return; } if (count > RMR_ARGUMENT_BUFFER_SIZE) { responsePrintError(sl_cli_get_command_string(args, 0), 0x81, "Number of arguments greater than local buffer."); return; } RAIL_RMR_StructureIndex_t structure = sl_cli_get_argument_uint8(args, RMR_CI_RMR_STRUCTURE); uint16_t offset = sl_cli_get_argument_uint16(args, RMR_CI_OFFSET); for (uint8_t i = 0; i < count; i++) { bufferedData[i] = sl_cli_get_argument_uint8(args, RMR_CI_DATA_START + i); } if (Rmr_writeRmrStructure(structure, offset, count, bufferedData) != RAIL_STATUS_NO_ERROR) { responsePrintError(sl_cli_get_command_string(args, 0), 0x82, "Error writing to structure."); return; } responsePrint(sl_cli_get_command_string(args, 0), "CommandStatus:Success"); return; } void CI_updateConfigurationPointer(sl_cli_command_arg_t *args) { if (!rmrInit(args)) { return; } if (sl_cli_get_argument_count(args) != 3) { responsePrintError(sl_cli_get_command_string(args, 0), 0x83, "Incorrect number of arguments"); return; } uint8_t structure = sl_cli_get_argument_uint8(args, 0); uint16_t location = sl_cli_get_argument_uint16(args, 1); uint8_t pointer = sl_cli_get_argument_uint8(args, 2); if (Rmr_updateConfigurationPointer(structure, location, pointer) != RAIL_STATUS_NO_ERROR) { responsePrintError(sl_cli_get_command_string(args, 0), 0x84, "Error updating structure"); return; } responsePrint(sl_cli_get_command_string(args, 0), "CommandStatus:Success"); } void CI_reconfigureModem(sl_cli_command_arg_t *args) { if (!rmrInit(args)) { return; } if (Rmr_reconfigureModem(railHandle) == RAIL_STATUS_NO_ERROR) { responsePrint(sl_cli_get_command_string(args, 0), "CommandStatus:Success"); } else { responsePrintError(sl_cli_get_command_string(args, 0), 0x85, "Need to be in Idle radio state for this command"); } }