#include #include #include #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(objects::TC_STORE); if (tcStore == nullptr) { sif::error << "PdecHandler::initialize: Invalid TC store" << std::endl; return ObjectManagerIF::CHILD_INIT_FAILED; } tcDestination = ObjectManager::instance()->get( 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(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(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)); // uint8_t routeToPm = calcMapAddrEntry(PM_BUFFER); // 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; // *(memoryBaseAddress + MAP_ADDR_LUT_OFFSET + idx / 4) = routeToPm << 24 // | routeToPm << 16 | routeToPm << 8 | routeToPm; } // 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: 0x" << std::hex << static_cast(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); sif::debug << "PdecHandler::newTcReceived: pdecFar 0x" << std::hex << static_cast(pdecFar) << std::endl; 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((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((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(*(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; }