eive-obsw/linux/obc/PdecHandler.cpp
2021-11-01 12:41:20 +01:00

380 lines
13 KiB
C++

#include <cstring>
#include <sys/mman.h>
#include <fcntl.h>
#include "PdecHandler.h"
#include "OBSWConfig.h"
#include "fsfw/serviceinterface/ServiceInterface.h"
#include "fsfw/tmtcservices/TmTcMessage.h"
#include "fsfw/objectmanager/ObjectManager.h"
PdecHandler::PdecHandler(object_id_t objectId, object_id_t tcDestinationId, LinuxLibgpioIF* gpioComIF,
gpioId_t pdecReset) :
SystemObject(objectId), tcDestinationId(tcDestinationId), gpioComIF(gpioComIF), pdecReset(
pdecReset) {
}
PdecHandler::~PdecHandler() {
}
ReturnValue_t PdecHandler::initialize() {
tcStore = ObjectManager::instance()->get<StorageManagerIF>(objects::TC_STORE);
if (tcStore == nullptr) {
sif::error << "PdecHandler::initialize: Invalid TC store" << std::endl;
return ObjectManagerIF::CHILD_INIT_FAILED;
}
tcDestination = ObjectManager::instance()->get<AcceptsTelecommandsIF>(
tcDestinationId);
if (tcDestination == nullptr) {
sif::error << "PdecHandler::initialize: Invalid tc destination specified" << std::endl;
return ObjectManagerIF::CHILD_INIT_FAILED;
}
ReturnValue_t result = RETURN_OK;
result = getRegisterAddress();
if (result != RETURN_OK) {
return ObjectManagerIF::CHILD_INIT_FAILED;
}
result = getMemoryBaseAddress();
if (result != RETURN_OK) {
return ObjectManagerIF::CHILD_INIT_FAILED;
}
writePdecConfig();
result = releasePdec();
if (result != RETURN_OK) {
return ObjectManagerIF::CHILD_INIT_FAILED;
}
return RETURN_OK;
}
ReturnValue_t PdecHandler::getRegisterAddress() {
int fd = open(PdecConfig::UIO_PDEC_REGISTERS, O_RDWR);
if (fd < 1) {
sif::warning << "PdecHandler::getRegisterAddress: Invalid UIO device file" << std::endl;
return RETURN_FAILED;
}
registerBaseAddress = static_cast<uint32_t*>(mmap(NULL, REGISTER_MAP_SIZE,
PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0));
if (registerBaseAddress == MAP_FAILED) {
sif::error << "PdecHandler::getRegisterAddress: Failed to map uio address" << std::endl;
return RETURN_FAILED;
}
return RETURN_OK;
}
ReturnValue_t PdecHandler::getMemoryBaseAddress() {
int fd = open(PdecConfig::UIO_PDEC_MEMORY, O_RDWR);
if (fd < 1) {
sif::warning << "PdecHandler::getMemoryBaseAddress: Invalid UIO device file" << std::endl;
return RETURN_FAILED;
}
memoryBaseAddress = static_cast<uint32_t*>(mmap(NULL, MEMORY_MAP_SIZE, PROT_WRITE | PROT_READ,
MAP_SHARED, fd, 0));
if (memoryBaseAddress == MAP_FAILED) {
sif::error << "PdecHandler::getMemoryBaseAddress: Failed to map uio address" << std::endl;
return RETURN_FAILED;
}
return RETURN_OK;
}
void PdecHandler::writePdecConfig() {
PdecParams_t pdecParams;
pdecParams.versionId = 0;
pdecParams.bypassFlag = PdecConfig::BYPASS_FLAG;
pdecParams.controlCommandFlag = PdecConfig::CONTROL_COMMAND_FLAG;
pdecParams.reservedFieldA = 0;
pdecParams.spacecraftId = PdecConfig::SPACECRAFT_ID;
pdecParams.virtualChannelId = PdecConfig::VIRTUAL_CHANNEL;
pdecParams.dummy = 0;
pdecParams.positiveWindow = PdecConfig::POSITIVE_WINDOW;
pdecParams.negativeWindow = PdecConfig::NEGATIVE_WINDOW;
std::memcpy(memoryBaseAddress, &pdecParams, sizeof(pdecParams));
// Configure all MAP IDs as invalid
for (int idx = 0; idx <= MAX_MAP_ADDR; idx += 4) {
*(memoryBaseAddress + MAP_ADDR_LUT_OFFSET + idx / 4) = NO_DESTINATION << 24
| NO_DESTINATION << 16 | NO_DESTINATION << 8 | NO_DESTINATION;
}
// All TCs with MAP ID 7 will be routed to the PM module (can then be read from memory)
uint8_t routeToPm = calcMapAddrEntry(PM_BUFFER);
*(memoryBaseAddress + 1) = NO_DESTINATION << 24 | NO_DESTINATION << 16 | NO_DESTINATION << 8
| routeToPm;
}
ReturnValue_t PdecHandler::resetFarStatFlag() {
uint32_t pdecFar = *(registerBaseAddress + PDEC_FAR_OFFSET);
if (pdecFar != FAR_RESET) {
sif::warning << "PdecHandler::resetFarStatFlag: FAR register did not match expected value."
<< " Read value:" << static_cast<unsigned int>(pdecFar) << std::endl;
return RETURN_FAILED;
}
#if OBSW_DEBUG_PDEC_HANDLER == 1
sif::debug << "PdecHandler::resetFarStatFlag: read FAR with value: 0x" << std::hex << pdecFar
<< std::endl;
#endif /* OBSW_DEBUG_PDEC_HANDLER == 1 */
return RETURN_OK;
}
ReturnValue_t PdecHandler::releasePdec() {
ReturnValue_t result = RETURN_OK;
result = gpioComIF->pullHigh(pdecReset);
if (result != RETURN_OK) {
sif::error << "PdecHandler::releasePdec: Failed to release PDEC reset signal" << std::endl;
}
return result;
}
ReturnValue_t PdecHandler::performOperation(uint8_t operationCode) {
ReturnValue_t result = RETURN_OK;
switch(state) {
case State::INIT:
resetFarStatFlag();
if (result != RETURN_OK) {
// Requires reconfiguration and reinitialization of PDEC
triggerEvent(INVALID_FAR);
state = State::WAIT_FOR_RECOVERY;
return result;
}
state = State::RUNNING;
break;
case State::RUNNING:
if (newTcReceived()) {
handleNewTc();
}
break;
case State::WAIT_FOR_RECOVERY:
break;
default:
sif::debug << "PdecHandler::performOperation: Invalid state" << std::endl;
break;
}
return RETURN_OK;
}
bool PdecHandler::newTcReceived() {
uint32_t pdecFar = *(registerBaseAddress + PDEC_FAR_OFFSET);
if (pdecFar >> STAT_POSITION != NEW_FAR_RECEIVED) {
return false;
}
if (!checkFrameAna(pdecFar)) {
return false;
}
return true;
}
bool PdecHandler::checkFrameAna(uint32_t pdecFar) {
bool frameValid = false;
FrameAna_t frameAna = static_cast<FrameAna_t>((pdecFar & FRAME_ANA_MASK) >> FRAME_ANA_POSITION);
switch(frameAna) {
case(FrameAna_t::ABANDONED_CLTU): {
triggerEvent(INVALID_TC_FRAME, ABANDONED_CLTU);
sif::debug << "PdecHandler::checkFrameAna: Abondoned CLTU" << std::endl;
break;
}
case(FrameAna_t::FRAME_DIRTY): {
triggerEvent(INVALID_TC_FRAME, FRAME_DIRTY);
sif::debug << "PdecHandler::checkFrameAna: Frame dirty" << std::endl;
break;
}
case(FrameAna_t::FRAME_ILLEGAL): {
sif::debug << "PdecHandler::checkFrameAna: Frame illegal for one reason" << std::endl;
handleIReason(pdecFar, FRAME_ILLEGAL_ONE_REASON);
break;
}
case(FrameAna_t::FRAME_ILLEGAL_MULTI_REASON): {
sif::debug << "PdecHandler::checkFrameAna: Frame illegal for multiple reasons"
<< std::endl;
handleIReason(pdecFar, FRAME_ILLEGAL_MULTIPLE_REASONS);
break;
}
case(FrameAna_t::AD_DISCARDED_LOCKOUT): {
triggerEvent(INVALID_TC_FRAME, AD_DISCARDED_LOCKOUT);
sif::debug << "PdecHandler::checkFrameAna: AD frame discarded because of lockout"
<< std::endl;
break;
}
case(FrameAna_t::AD_DISCARDED_WAIT): {
triggerEvent(INVALID_TC_FRAME, AD_DISCARDED_LOCKOUT);
sif::debug << "PdecHandler::checkFrameAna: AD frame discarded because of wait"
<< std::endl;
break;
}
case(FrameAna_t::AD_DISCARDED_NS_VR): {
triggerEvent(INVALID_TC_FRAME, AD_DISCARDED_NS_VS);
sif::debug << "PdecHandler::checkFrameAna: AD frame discarded because N(S) or V(R)"
<< std::endl;
break;
}
case(FrameAna_t::FRAME_ACCEPTED): {
sif::debug << "PdecHandler::checkFrameAna: Accepted TC frame" << std::endl;
frameValid = true;
break;
}
default: {
sif::debug << "PdecHandler::checkFrameAna: Invaid frame analysis report" << std::endl;
break;
}
}
return frameValid;
}
void PdecHandler::handleIReason(uint32_t pdecFar, ReturnValue_t parameter1) {
IReason_t ireason = static_cast<IReason_t>((pdecFar & IREASON_MASK) >> IREASON_POSITION);
switch(ireason) {
case(IReason_t::NO_REPORT): {
triggerEvent(INVALID_TC_FRAME, parameter1, NO_REPORT);
sif::debug << "PdecHandler::handleIReason: No illegal report" << std::endl;
break;
}
case(IReason_t::ERROR_VERSION_NUMBER): {
triggerEvent(INVALID_TC_FRAME, parameter1, ERROR_VERSION_NUMBER);
sif::debug << "PdecHandler::handleIReason: Error in version number and reserved A and B "
<< "fields" << std::endl;
break;
}
case(IReason_t::ILLEGAL_COMBINATION): {
triggerEvent(INVALID_TC_FRAME, parameter1, ILLEGAL_COMBINATION);
sif::debug << "PdecHandler::handleIReason: Illegal combination (AC) of bypass and control "
<< "command flags" << std::endl;
break;
}
case(IReason_t::INVALID_SC_ID): {
triggerEvent(INVALID_TC_FRAME, parameter1, INVALID_SC_ID);
sif::debug << "PdecHandler::handleIReason: Invalid spacecraft identifier " << std::endl;
break;
}
case(IReason_t::INVALID_VC_ID_MSB): {
triggerEvent(INVALID_TC_FRAME, parameter1, INVALID_VC_ID_MSB);
sif::debug << "PdecHandler::handleIReason: VC identifier bit 0 to 4 did not match "
<< std::endl;
break;
}
case(IReason_t::INVALID_VC_ID_LSB): {
triggerEvent(INVALID_TC_FRAME, parameter1, INVALID_VC_ID_LSB);
sif::debug << "PdecHandler::handleIReason: VC identifier bit 5 did not match " << std::endl;
break;
}
case(IReason_t::NS_NOT_ZERO): {
triggerEvent(INVALID_TC_FRAME, parameter1, NS_NOT_ZERO);
sif::debug << "PdecHandler::handleIReason: N(S) of BC or BD frame not set to all zeros"
<< std::endl;
break;
}
case(IReason_t::INCORRECT_BC_CC): {
triggerEvent(INVALID_TC_FRAME, parameter1, INVALID_BC_CC);
sif::debug << "PdecHandler::handleIReason: Invalid BC control command format" << std::endl;
break;
}
default: {
sif::debug << "PdecHandler::handleIReason: Invaid reason id" << std::endl;
break;
}
}
}
void PdecHandler::handleNewTc() {
ReturnValue_t result = RETURN_OK;
uint32_t tcLength = 0;
result = readTc(tcLength);
if (result != RETURN_OK) {
return;
}
#if OBSW_DEBUG_PDEC == 1
unsigned int mapId = tcSegment[0] & MAP_ID_MASK;
sif::debug << "PdecHandler::handleNewTc: Received TC segment with map ID" << mapId
<< std::endl;
#endif /* OBSW_DEBUG_PDEC */
store_address_t storeId;
result = tcStore->addData(&storeId, tcSegment + 1, tcLength - 1);
if (result != RETURN_OK) {
sif::warning << "PdecHandler::handleNewTc: Failed to add received space packet to store"
<< std::endl;
return;
}
TmTcMessage message(storeId);
result = MessageQueueSenderIF::sendMessage(tcDestination->getRequestQueue(), &message);
if (result != HasReturnvaluesIF::RETURN_OK) {
sif::warning << "PdecHandler::handleNewTc: Failed to send message to TC destination"
<< std::endl;
tcStore->deleteData(storeId);
return;
}
return;
}
ReturnValue_t PdecHandler::readTc(uint32_t& tcLength) {
uint32_t tcOffset = *(registerBaseAddress + PDEC_BPTR_OFFSET) - PHYSICAL_BASE_ADDRESS;
#if OBSW_DEBUG_PDEC == 1
sif::debug << "PdecHandler::readTc: TC offset: " << std::hex << tcOffset << std::endl;
#endif /* OBSW_DEBUG_PDEC */
uint32_t* tcPtr = reinterpret_cast<uint32_t*>(*(registerBaseAddress + tcOffset) / 4);
tcLength = *(registerBaseAddress + PDEC_SLEN_OFFSET);
#if OBSW_DEBUG_PDEC == 1
sif::debug << "PdecHandler::readTc: TC length: " << tcLength << std::endl;
#endif /* OBSW_DEBUG_PDEC */
if (tcLength > MAX_TC_SEGMENT_SIZE) {
sif::warning << "PdecHandler::handleNewTc: Read invalid TC length from PDEC register"
<< std::endl;
return RETURN_FAILED;
}
uint32_t idx = 0;
uint32_t tcData = 0;
for (idx = 0; idx < tcLength; idx = idx + 4) {
tcData = *(tcPtr + idx);
std::memcpy(tcSegment + idx, &tcData, sizeof(tcData));
}
// Backend buffer is handled back to PDEC3
*(registerBaseAddress + PDEC_BFREE_OFFSET) = 0;
return RETURN_OK;
}
uint8_t PdecHandler::calcMapAddrEntry(uint8_t moduleId) {
uint8_t lutEntry = 0;
uint8_t parity = getOddParity(moduleId | (1 << VALID_POSITION));
lutEntry = (parity << PARITY_POSITION) | (1 << VALID_POSITION) | moduleId;
return lutEntry;
}
uint8_t PdecHandler::getOddParity(uint8_t number) {
uint8_t parityBit = 0;
uint8_t countBits = 0;
for (unsigned int idx = 0; idx < sizeof(number) * 8; idx++) {
countBits += (number >> idx) & 0x1;
}
parityBit = ~(countBits & 0x1) & 0x1;
return parityBit;
}