diff --git a/modules/cfe_testcase/src/sb_sendrecv_test.c b/modules/cfe_testcase/src/sb_sendrecv_test.c index 4f8be7ac2..3ba2cf747 100644 --- a/modules/cfe_testcase/src/sb_sendrecv_test.c +++ b/modules/cfe_testcase/src/sb_sendrecv_test.c @@ -145,7 +145,7 @@ void TestBasicTransmitRecv(void) UtAssert_INT32_EQ(CFE_SB_ReceiveBuffer(&MsgBuf, PipeId1, -100), CFE_SB_BAD_ARGUMENT); /* - * Note, the CFE_SB_TransmitMsg now adheres to the "UpdateHeader" flag. + * Note, the CFE_SB_TransmitMsg now adheres to the "IsOrigination" flag. * Thus, the sequence numbers should come back with the value from the Route (1-2) * rather than the value the message was filled with initially. * diff --git a/modules/core_api/fsw/inc/cfe_msg.h b/modules/core_api/fsw/inc/cfe_msg.h index ccd43bdb7..7a25771f2 100644 --- a/modules/core_api/fsw/inc/cfe_msg.h +++ b/modules/core_api/fsw/inc/cfe_msg.h @@ -60,30 +60,6 @@ */ CFE_Status_t CFE_MSG_Init(CFE_MSG_Message_t *MsgPtr, CFE_SB_MsgId_t MsgId, CFE_MSG_Size_t Size); -/*****************************************************************************/ -/** - * \brief Set/compute all dynamically-updated headers on a message - * - * \par Description - * This routine updates all dynamic header fields on a message, and is typically - * invoked via SB just prior to broadcasting the message. Dynamic headers include - * are values that should be computed/updated per message, including: - * - the sequence number - * - the timestamp, if present - * - any error control or checksum fields, if present - * - * The MSG module implementation determines which header fields meet this criteria - * and how they should be computed. - * - * \param[inout] MsgPtr A pointer to the buffer that contains the message @nonnull. - * \param[in] SeqCnt The current sequence number from the message route - * - * \return Execution status, see \ref CFEReturnCodes - * \retval #CFE_SUCCESS \copybrief CFE_SUCCESS - * \retval #CFE_MSG_BAD_ARGUMENT \copybrief CFE_MSG_BAD_ARGUMENT - */ -CFE_Status_t CFE_MSG_UpdateHeader(CFE_MSG_Message_t *MsgPtr, CFE_MSG_SequenceCount_t SeqCnt); - /**\}*/ /** \defgroup CFEAPIMSGHeaderPri cFE Message Primary Header APIs @@ -725,12 +701,44 @@ CFE_Status_t CFE_MSG_GetTypeFromMsgId(CFE_SB_MsgId_t MsgId, CFE_MSG_Type_t *Type /**\}*/ -/** \defgroup CFEAPIMSGMsgVerify cFE Message Checking APIs +/** \defgroup CFEAPIMSGMsgIntegrity cFE Message Integrity APIs * \{ */ + /*****************************************************************************/ /** - * \brief Checks message headers against expected values + * \brief Perform any necessary actions on a newly-created message, prior to sending + * + * \par Description + * This routine updates and/or appends any necessary fields on a message, is + * invoked via SB just prior to broadcasting the message. The actions include + * updating any values that should be computed/updated per message, including: + * - setting the sequence number + * - updating the timestamp, if present + * - computing any error control or checksum fields, if present + * + * The MSG module implementation determines which header fields meet this criteria + * and how they should be computed. + * + * The BufferSize parameter indicates the allocation size message of the buffer that + * holds the message (i.e. the message envelope size). In some implementations, the + * allocated buffer may include extra space in order to append a CRC or digital signature. + * + * \sa CFE_MSG_VerificationAction + * + * \param[inout] MsgPtr A pointer to the buffer that contains the message @nonnull. + * \param[in] BufferSize The size of the buffer encapsulating the message + * \param[out] IsAcceptable Output variable to be set, indicates message acceptability @nonnull + * + * \return Execution status, see \ref CFEReturnCodes + * \retval #CFE_SUCCESS \copybrief CFE_SUCCESS + * \retval #CFE_MSG_BAD_ARGUMENT \copybrief CFE_MSG_BAD_ARGUMENT + */ +CFE_Status_t CFE_MSG_OriginationAction(CFE_MSG_Message_t *MsgPtr, size_t BufferSize, bool *IsAcceptable); + +/*****************************************************************************/ +/** + * \brief Checks message integrity/acceptability * * \par Description * This routine validates that any error-control field(s) in the message header @@ -740,14 +748,23 @@ CFE_Status_t CFE_MSG_GetTypeFromMsgId(CFE_SB_MsgId_t MsgId, CFE_MSG_Type_t *Type * and may be a no-op if no error checking is implemented. In that case, it * will always output "true". * + * \note Due to the fact that software bus uses a multicast architecture, this function + * must not modify the message, as the buffer may be shared among multiple receivers. + * This should generally be the inverse of CFE_MSG_OriginationAction(), but on the + * origination side it may update header fields and/or modify the message, on + * the verification/receive side it must only check those fields, not modify them. + * + * \sa CFE_MSG_OriginationAction + * * \param[in] MsgPtr Message Pointer @nonnull - * \param[out] VerifyStatus Output variable to be set to verification result @nonnull + * \param[in] BufferSize The size of the buffer encapsulating the message + * \param[out] IsAcceptable Output variable to be set, indicates message acceptability @nonnull * * \return Execution status, see \ref CFEReturnCodes * \retval #CFE_SUCCESS \copybrief CFE_SUCCESS * \retval #CFE_MSG_BAD_ARGUMENT \copybrief CFE_MSG_BAD_ARGUMENT */ -CFE_Status_t CFE_MSG_Verify(const CFE_MSG_Message_t *MsgPtr, bool *VerifyStatus); +CFE_Status_t CFE_MSG_VerificationAction(const CFE_MSG_Message_t *MsgPtr, size_t BufferSize, bool *IsAcceptable); /**\}*/ diff --git a/modules/core_api/fsw/inc/cfe_sb.h b/modules/core_api/fsw/inc/cfe_sb.h index a670d0120..d08b5aca5 100644 --- a/modules/core_api/fsw/inc/cfe_sb.h +++ b/modules/core_api/fsw/inc/cfe_sb.h @@ -402,9 +402,15 @@ CFE_Status_t CFE_SB_UnsubscribeLocal(CFE_SB_MsgId_t MsgId, CFE_SB_PipeId_t PipeI ** software bus will read the message ID from the message header to ** determine which pipes should receive the message. ** -** In general, the "UpdateHeader" parameter should be passed as "true" -** if the message was newly constructed by the sender and is being sent -** for the first time. When forwarding a message that originated from +** The IsOrigination parameter should be passed as "true" if the message was +** newly constructed by the sender and is being sent for the first time. This +** enables the message origination actions as determined by the CFE MSG module, +** which may include (but not limited to): +** - Updating sequence number +** - Updating timestamp +** - Calcualating a CRC, checksum, or other message error control field +** +** Conversely, when forwarding a message that originated from ** an external entity (e.g. messages passing through CI or SBN), the ** parameter should be passed as "false" to not overwrite existing data. ** @@ -421,7 +427,7 @@ CFE_Status_t CFE_SB_UnsubscribeLocal(CFE_SB_MsgId_t MsgId, CFE_SB_PipeId_t PipeI ** ** \param[in] MsgPtr A pointer to the message to be sent @nonnull. This must point ** to the first byte of the message header. -** \param[in] UpdateHeader Update the headers of the message +** \param[in] IsOrigination Update the headers of the message ** ** \return Execution status, see \ref CFEReturnCodes ** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS @@ -429,7 +435,7 @@ CFE_Status_t CFE_SB_UnsubscribeLocal(CFE_SB_MsgId_t MsgId, CFE_SB_PipeId_t PipeI ** \retval #CFE_SB_MSG_TOO_BIG \copybrief CFE_SB_MSG_TOO_BIG ** \retval #CFE_SB_BUF_ALOC_ERR \covtest \copybrief CFE_SB_BUF_ALOC_ERR **/ -CFE_Status_t CFE_SB_TransmitMsg(const CFE_MSG_Message_t *MsgPtr, bool UpdateHeader); +CFE_Status_t CFE_SB_TransmitMsg(const CFE_MSG_Message_t *MsgPtr, bool IsOrigination); /*****************************************************************************/ /** @@ -471,6 +477,7 @@ CFE_Status_t CFE_SB_TransmitMsg(const CFE_MSG_Message_t *MsgPtr, bool UpdateHead ** \retval #CFE_SB_NO_MESSAGE \copybrief CFE_SB_NO_MESSAGE **/ CFE_Status_t CFE_SB_ReceiveBuffer(CFE_SB_Buffer_t **BufPtr, CFE_SB_PipeId_t PipeId, int32 TimeOut); + /** @} */ /** @defgroup CFEAPISBZeroCopy cFE Zero Copy APIs @@ -544,9 +551,15 @@ CFE_Status_t CFE_SB_ReleaseMessageBuffer(CFE_SB_Buffer_t *BufPtr); ** internal buffer. The "zero copy" interface can be used to improve ** performance in high-rate, high-volume software bus traffic. ** -** In general, the "UpdateHeader" parameter should be passed as "true" -** if the message was newly constructed by the sender and is being sent -** for the first time. When forwarding a message that originated from +** The IsOrigination parameter should be passed as "true" if the message was +** newly constructed by the sender and is being sent for the first time. This +** enables the message origination actions as determined by the CFE MSG module, +** which may include (but not limited to): +** - Updating sequence number +** - Updating timestamp +** - Calcualating a CRC, checksum, or other message error control field +** +** Conversely, when forwarding a message that originated from ** an external entity (e.g. messages passing through CI or SBN), the ** parameter should be passed as "false" to not overwrite existing data. ** @@ -567,15 +580,15 @@ CFE_Status_t CFE_SB_ReleaseMessageBuffer(CFE_SB_Buffer_t *BufPtr); ** -# This function will increment and apply the internally tracked ** sequence counter if set to do so. ** -** \param[in] BufPtr A pointer to the buffer to be sent @nonnull. -** \param[in] UpdateHeader Update the headers of the message +** \param[in] BufPtr A pointer to the buffer to be sent @nonnull. +** \param[in] IsOrigination Update applicable header field(s) of a newly constructed message ** ** \return Execution status, see \ref CFEReturnCodes ** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS ** \retval #CFE_SB_BAD_ARGUMENT \copybrief CFE_SB_BAD_ARGUMENT ** \retval #CFE_SB_MSG_TOO_BIG \copybrief CFE_SB_MSG_TOO_BIG **/ -CFE_Status_t CFE_SB_TransmitBuffer(CFE_SB_Buffer_t *BufPtr, bool UpdateHeader); +CFE_Status_t CFE_SB_TransmitBuffer(CFE_SB_Buffer_t *BufPtr, bool IsOrigination); /** @} */ diff --git a/modules/core_api/ut-stubs/src/cfe_msg_handlers.c b/modules/core_api/ut-stubs/src/cfe_msg_handlers.c index fee87345c..e9228a998 100644 --- a/modules/core_api/ut-stubs/src/cfe_msg_handlers.c +++ b/modules/core_api/ut-stubs/src/cfe_msg_handlers.c @@ -376,3 +376,35 @@ void UT_DefaultHandler_CFE_MSG_GetNextSequenceCount(void *UserObj, UT_EntryKey_t UT_Stub_SetReturnValue(FuncKey, return_value); } + +/*------------------------------------------------------------ + * + * Default handler for CFE_MSG_OriginationAction coverage stub function + * + *------------------------------------------------------------*/ +void UT_DefaultHandler_CFE_MSG_OriginationAction(void *UserObj, UT_EntryKey_t FuncKey, const UT_StubContext_t *Context) +{ + bool *IsAcceptable = UT_Hook_GetArgValueByName(Context, "IsAcceptable", bool *); + + /* by default just always return true -- a UT case that needs something else can override this handler */ + if (IsAcceptable != NULL) + { + *IsAcceptable = true; + } +} + +/*------------------------------------------------------------ + * + * Default handler for CFE_MSG_VerificationAction coverage stub function + * + *------------------------------------------------------------*/ +void UT_DefaultHandler_CFE_MSG_VerificationAction(void *UserObj, UT_EntryKey_t FuncKey, const UT_StubContext_t *Context) +{ + bool *IsAcceptable = UT_Hook_GetArgValueByName(Context, "IsAcceptable", bool *); + + /* by default just always return true -- a UT case that needs something else can override this handler */ + if (IsAcceptable != NULL) + { + *IsAcceptable = true; + } +} diff --git a/modules/core_api/ut-stubs/src/cfe_msg_stubs.c b/modules/core_api/ut-stubs/src/cfe_msg_stubs.c index 066f8e8d1..ea39c87b3 100644 --- a/modules/core_api/ut-stubs/src/cfe_msg_stubs.c +++ b/modules/core_api/ut-stubs/src/cfe_msg_stubs.c @@ -42,7 +42,9 @@ void UT_DefaultHandler_CFE_MSG_GetSubsystem(void *, UT_EntryKey_t, const UT_Stub void UT_DefaultHandler_CFE_MSG_GetSystem(void *, UT_EntryKey_t, const UT_StubContext_t *); void UT_DefaultHandler_CFE_MSG_GetType(void *, UT_EntryKey_t, const UT_StubContext_t *); void UT_DefaultHandler_CFE_MSG_GetTypeFromMsgId(void *, UT_EntryKey_t, const UT_StubContext_t *); +void UT_DefaultHandler_CFE_MSG_OriginationAction(void *, UT_EntryKey_t, const UT_StubContext_t *); void UT_DefaultHandler_CFE_MSG_ValidateChecksum(void *, UT_EntryKey_t, const UT_StubContext_t *); +void UT_DefaultHandler_CFE_MSG_VerificationAction(void *, UT_EntryKey_t, const UT_StubContext_t *); /* * ---------------------------------------------------- @@ -366,6 +368,24 @@ CFE_Status_t CFE_MSG_Init(CFE_MSG_Message_t *MsgPtr, CFE_SB_MsgId_t MsgId, CFE_M return UT_GenStub_GetReturnValue(CFE_MSG_Init, CFE_Status_t); } +/* + * ---------------------------------------------------- + * Generated stub function for CFE_MSG_OriginationAction() + * ---------------------------------------------------- + */ +CFE_Status_t CFE_MSG_OriginationAction(CFE_MSG_Message_t *MsgPtr, size_t BufferSize, bool *IsAcceptable) +{ + UT_GenStub_SetupReturnBuffer(CFE_MSG_OriginationAction, CFE_Status_t); + + UT_GenStub_AddParam(CFE_MSG_OriginationAction, CFE_MSG_Message_t *, MsgPtr); + UT_GenStub_AddParam(CFE_MSG_OriginationAction, size_t, BufferSize); + UT_GenStub_AddParam(CFE_MSG_OriginationAction, bool *, IsAcceptable); + + UT_GenStub_Execute(CFE_MSG_OriginationAction, Basic, UT_DefaultHandler_CFE_MSG_OriginationAction); + + return UT_GenStub_GetReturnValue(CFE_MSG_OriginationAction, CFE_Status_t); +} + /* * ---------------------------------------------------- * Generated stub function for CFE_MSG_SetApId() @@ -623,34 +643,35 @@ CFE_Status_t CFE_MSG_SetType(CFE_MSG_Message_t *MsgPtr, CFE_MSG_Type_t Type) /* * ---------------------------------------------------- - * Generated stub function for CFE_MSG_UpdateHeader() + * Generated stub function for CFE_MSG_ValidateChecksum() * ---------------------------------------------------- */ -CFE_Status_t CFE_MSG_UpdateHeader(CFE_MSG_Message_t *MsgPtr, CFE_MSG_SequenceCount_t SeqCnt) +CFE_Status_t CFE_MSG_ValidateChecksum(const CFE_MSG_Message_t *MsgPtr, bool *IsValid) { - UT_GenStub_SetupReturnBuffer(CFE_MSG_UpdateHeader, CFE_Status_t); + UT_GenStub_SetupReturnBuffer(CFE_MSG_ValidateChecksum, CFE_Status_t); - UT_GenStub_AddParam(CFE_MSG_UpdateHeader, CFE_MSG_Message_t *, MsgPtr); - UT_GenStub_AddParam(CFE_MSG_UpdateHeader, CFE_MSG_SequenceCount_t, SeqCnt); + UT_GenStub_AddParam(CFE_MSG_ValidateChecksum, const CFE_MSG_Message_t *, MsgPtr); + UT_GenStub_AddParam(CFE_MSG_ValidateChecksum, bool *, IsValid); - UT_GenStub_Execute(CFE_MSG_UpdateHeader, Basic, NULL); + UT_GenStub_Execute(CFE_MSG_ValidateChecksum, Basic, UT_DefaultHandler_CFE_MSG_ValidateChecksum); - return UT_GenStub_GetReturnValue(CFE_MSG_UpdateHeader, CFE_Status_t); + return UT_GenStub_GetReturnValue(CFE_MSG_ValidateChecksum, CFE_Status_t); } /* * ---------------------------------------------------- - * Generated stub function for CFE_MSG_ValidateChecksum() + * Generated stub function for CFE_MSG_VerificationAction() * ---------------------------------------------------- */ -CFE_Status_t CFE_MSG_ValidateChecksum(const CFE_MSG_Message_t *MsgPtr, bool *IsValid) +CFE_Status_t CFE_MSG_VerificationAction(const CFE_MSG_Message_t *MsgPtr, size_t BufferSize, bool *IsAcceptable) { - UT_GenStub_SetupReturnBuffer(CFE_MSG_ValidateChecksum, CFE_Status_t); + UT_GenStub_SetupReturnBuffer(CFE_MSG_VerificationAction, CFE_Status_t); - UT_GenStub_AddParam(CFE_MSG_ValidateChecksum, const CFE_MSG_Message_t *, MsgPtr); - UT_GenStub_AddParam(CFE_MSG_ValidateChecksum, bool *, IsValid); + UT_GenStub_AddParam(CFE_MSG_VerificationAction, const CFE_MSG_Message_t *, MsgPtr); + UT_GenStub_AddParam(CFE_MSG_VerificationAction, size_t, BufferSize); + UT_GenStub_AddParam(CFE_MSG_VerificationAction, bool *, IsAcceptable); - UT_GenStub_Execute(CFE_MSG_ValidateChecksum, Basic, UT_DefaultHandler_CFE_MSG_ValidateChecksum); + UT_GenStub_Execute(CFE_MSG_VerificationAction, Basic, UT_DefaultHandler_CFE_MSG_VerificationAction); - return UT_GenStub_GetReturnValue(CFE_MSG_ValidateChecksum, CFE_Status_t); + return UT_GenStub_GetReturnValue(CFE_MSG_VerificationAction, CFE_Status_t); } diff --git a/modules/core_api/ut-stubs/src/cfe_sb_stubs.c b/modules/core_api/ut-stubs/src/cfe_sb_stubs.c index 3f541d54c..369394492 100644 --- a/modules/core_api/ut-stubs/src/cfe_sb_stubs.c +++ b/modules/core_api/ut-stubs/src/cfe_sb_stubs.c @@ -384,12 +384,12 @@ void CFE_SB_TimeStampMsg(CFE_MSG_Message_t *MsgPtr) * Generated stub function for CFE_SB_TransmitBuffer() * ---------------------------------------------------- */ -CFE_Status_t CFE_SB_TransmitBuffer(CFE_SB_Buffer_t *BufPtr, bool UpdateHeader) +CFE_Status_t CFE_SB_TransmitBuffer(CFE_SB_Buffer_t *BufPtr, bool IsOrigination) { UT_GenStub_SetupReturnBuffer(CFE_SB_TransmitBuffer, CFE_Status_t); UT_GenStub_AddParam(CFE_SB_TransmitBuffer, CFE_SB_Buffer_t *, BufPtr); - UT_GenStub_AddParam(CFE_SB_TransmitBuffer, bool, UpdateHeader); + UT_GenStub_AddParam(CFE_SB_TransmitBuffer, bool, IsOrigination); UT_GenStub_Execute(CFE_SB_TransmitBuffer, Basic, UT_DefaultHandler_CFE_SB_TransmitBuffer); @@ -401,12 +401,12 @@ CFE_Status_t CFE_SB_TransmitBuffer(CFE_SB_Buffer_t *BufPtr, bool UpdateHeader) * Generated stub function for CFE_SB_TransmitMsg() * ---------------------------------------------------- */ -CFE_Status_t CFE_SB_TransmitMsg(const CFE_MSG_Message_t *MsgPtr, bool UpdateHeader) +CFE_Status_t CFE_SB_TransmitMsg(const CFE_MSG_Message_t *MsgPtr, bool IsOrigination) { UT_GenStub_SetupReturnBuffer(CFE_SB_TransmitMsg, CFE_Status_t); UT_GenStub_AddParam(CFE_SB_TransmitMsg, const CFE_MSG_Message_t *, MsgPtr); - UT_GenStub_AddParam(CFE_SB_TransmitMsg, bool, UpdateHeader); + UT_GenStub_AddParam(CFE_SB_TransmitMsg, bool, IsOrigination); UT_GenStub_Execute(CFE_SB_TransmitMsg, Basic, UT_DefaultHandler_CFE_SB_TransmitMsg); diff --git a/modules/msg/CMakeLists.txt b/modules/msg/CMakeLists.txt index 4cc69762c..c4af293b0 100644 --- a/modules/msg/CMakeLists.txt +++ b/modules/msg/CMakeLists.txt @@ -18,7 +18,7 @@ set(${DEP}_SRC fsw/src/cfe_msg_ccsdspri.c fsw/src/cfe_msg_init.c - fsw/src/cfe_msg_verify.c + fsw/src/cfe_msg_integrity.c fsw/src/cfe_msg_msgid_shared.c fsw/src/cfe_msg_sechdr_checksum.c fsw/src/cfe_msg_sechdr_fc.c diff --git a/modules/msg/fsw/src/cfe_msg_init.c b/modules/msg/fsw/src/cfe_msg_init.c index ab988f4bb..d67fc643d 100644 --- a/modules/msg/fsw/src/cfe_msg_init.c +++ b/modules/msg/fsw/src/cfe_msg_init.c @@ -53,34 +53,3 @@ CFE_Status_t CFE_MSG_Init(CFE_MSG_Message_t *MsgPtr, CFE_SB_MsgId_t MsgId, CFE_M return status; } - -/*---------------------------------------------------------------- - * - * Implemented per public API - * See description in header file for argument/return detail - * - *-----------------------------------------------------------------*/ -CFE_Status_t CFE_MSG_UpdateHeader(CFE_MSG_Message_t *MsgPtr, CFE_MSG_SequenceCount_t SeqCnt) -{ - if (MsgPtr == NULL) - { - return CFE_MSG_BAD_ARGUMENT; - } - - /* Sequence count is in the basic CCSDS Primary Hdr, so all msgs have it */ - CFE_MSG_SetSequenceCount(MsgPtr, SeqCnt); - - /* - * TLM packets have a timestamp in the secondary header. - * This may fail if this is not a TLM packet (that is OK) - */ - CFE_MSG_SetMsgTime(MsgPtr, CFE_TIME_GetTime()); - - /* - * CMD packets have a checksum in the secondary header. - * This may fail if this is not a CMD packet (that is OK) - */ - CFE_MSG_GenerateChecksum(MsgPtr); - - return CFE_SUCCESS; -} \ No newline at end of file diff --git a/modules/msg/fsw/src/cfe_msg_verify.c b/modules/msg/fsw/src/cfe_msg_integrity.c similarity index 58% rename from modules/msg/fsw/src/cfe_msg_verify.c rename to modules/msg/fsw/src/cfe_msg_integrity.c index b712ff3f6..bf1c61bd0 100644 --- a/modules/msg/fsw/src/cfe_msg_verify.c +++ b/modules/msg/fsw/src/cfe_msg_integrity.c @@ -27,22 +27,50 @@ * See description in header file for argument/return detail * *-----------------------------------------------------------------*/ -CFE_Status_t CFE_MSG_Verify(const CFE_MSG_Message_t *MsgPtr, bool *VerifyStatus) +CFE_Status_t CFE_MSG_OriginationAction(CFE_MSG_Message_t *MsgPtr, size_t BufferSize, bool *IsAcceptable) { - if (MsgPtr == NULL || VerifyStatus == NULL) + if (MsgPtr == NULL || IsAcceptable == NULL) + { + return CFE_MSG_BAD_ARGUMENT; + } + + /* + * TLM packets have a timestamp in the secondary header. + * This may fail if this is not a TLM packet (that is OK) + */ + CFE_MSG_SetMsgTime(MsgPtr, CFE_TIME_GetTime()); + + /* + * CMD packets have a checksum in the secondary header. + * This may fail if this is not a CMD packet (that is OK) + */ + CFE_MSG_GenerateChecksum(MsgPtr); + + /* This implementation permits all outgoing messages */ + *IsAcceptable = true; + + return CFE_SUCCESS; +} +/*---------------------------------------------------------------- + * + * Implemented per public API + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +CFE_Status_t CFE_MSG_VerificationAction(const CFE_MSG_Message_t *MsgPtr, size_t BufferSize, bool *IsAcceptable) +{ + if (MsgPtr == NULL || IsAcceptable == NULL) { return CFE_MSG_BAD_ARGUMENT; } /* * In the default implementation, there is not anything to check here. - * Only commands have a checksum, but the value of that checksum was historically - * not enforced by CFE. * * This is mainly a hook for user expansion, in case a custom implementation * has message verification capability. */ - *VerifyStatus = true; + *IsAcceptable = true; return CFE_SUCCESS; } \ No newline at end of file diff --git a/modules/msg/ut-coverage/CMakeLists.txt b/modules/msg/ut-coverage/CMakeLists.txt index dd2c3bad1..a2c5eed17 100644 --- a/modules/msg/ut-coverage/CMakeLists.txt +++ b/modules/msg/ut-coverage/CMakeLists.txt @@ -18,7 +18,7 @@ set (ut_${DEP}_tests test_msg_not.c test_msg_pri_not.c test_cfe_msg_init.c - test_cfe_msg_verify.c + test_cfe_msg_integrity.c test_cfe_msg_ccsdspri.c test_cfe_msg_msgid_shared.c test_cfe_msg_checksum.c diff --git a/modules/msg/ut-coverage/msg_UT.c b/modules/msg/ut-coverage/msg_UT.c index 0ad1b30bb..48d8f9259 100644 --- a/modules/msg/ut-coverage/msg_UT.c +++ b/modules/msg/ut-coverage/msg_UT.c @@ -44,11 +44,11 @@ void UtTest_Setup(void) UtPrintf("Message header coverage test..."); UT_ADD_TEST(Test_MSG_Init); - UT_ADD_TEST(Test_MSG_UpdateHeader); + UT_ADD_TEST(Test_MSG_OriginationAction); Test_MSG_CCSDSPri(); Test_MSG_CCSDSExt(); Test_MSG_MsgId_Shared(); - UT_ADD_TEST(Test_MSG_Verify); + UT_ADD_TEST(Test_MSG_VerificationAction); UT_ADD_TEST(Test_MSG_MsgId); UT_ADD_TEST(Test_MSG_Checksum); UT_ADD_TEST(Test_MSG_FcnCode); diff --git a/modules/msg/ut-coverage/test_cfe_msg_init.c b/modules/msg/ut-coverage/test_cfe_msg_init.c index a707c3618..a072566ae 100644 --- a/modules/msg/ut-coverage/test_cfe_msg_init.c +++ b/modules/msg/ut-coverage/test_cfe_msg_init.c @@ -124,40 +124,3 @@ void Test_MSG_Init(void) UtAssert_UINT32_EQ(Test_MSG_Pri_NotZero(CFE_MSG_PTR(cmd)) & ~MSG_HDRVER_FLAG, MSG_APID_FLAG | MSG_HASSEC_FLAG | MSG_TYPE_FLAG | MSG_LENGTH_FLAG | MSG_SEGMENT_FLAG); } - -/* - * Test MSG Update Header - */ -void Test_MSG_UpdateHeader(void) -{ - union - { - CFE_MSG_Message_t msg; - CFE_MSG_CommandHeader_t cmd; - CFE_MSG_TelemetryHeader_t tlm; - - } LocalBuf; - CFE_MSG_SequenceCount_t SeqCnt; - CFE_MSG_SequenceCount_t CheckCnt; - - memset(&LocalBuf, 0, sizeof(LocalBuf)); - SeqCnt = 1; - CheckCnt = 0; - - /* bad buffer */ - UtAssert_INT32_EQ(CFE_MSG_UpdateHeader(NULL, SeqCnt), CFE_MSG_BAD_ARGUMENT); - - /* nominal, cmd */ - CFE_MSG_SetType(&LocalBuf.msg, CFE_MSG_Type_Cmd); - CFE_UtAssert_SUCCESS(CFE_MSG_UpdateHeader(&LocalBuf.msg, SeqCnt)); - CFE_MSG_GetSequenceCount(&LocalBuf.msg, &CheckCnt); - UtAssert_UINT32_EQ(CheckCnt, SeqCnt); - ++SeqCnt; - - /* nominal, tlm */ - CFE_MSG_SetType(&LocalBuf.msg, CFE_MSG_Type_Tlm); - CFE_UtAssert_SUCCESS(CFE_MSG_UpdateHeader(&LocalBuf.msg, SeqCnt)); - CFE_MSG_GetSequenceCount(&LocalBuf.msg, &CheckCnt); - UtAssert_UINT32_EQ(CheckCnt, SeqCnt); - ++SeqCnt; -} diff --git a/modules/msg/ut-coverage/test_cfe_msg_init.h b/modules/msg/ut-coverage/test_cfe_msg_init.h index 3c5b25f62..6e465b03d 100644 --- a/modules/msg/ut-coverage/test_cfe_msg_init.h +++ b/modules/msg/ut-coverage/test_cfe_msg_init.h @@ -33,6 +33,6 @@ */ /* Test extended header mission functionality */ void Test_MSG_Init(void); -void Test_MSG_UpdateHeader(void); +void Test_MSG_OriginationAction(void); #endif /* TEST_CFE_MSG_INIT_H */ diff --git a/modules/msg/ut-coverage/test_cfe_msg_integrity.c b/modules/msg/ut-coverage/test_cfe_msg_integrity.c new file mode 100644 index 000000000..bc3aa7311 --- /dev/null +++ b/modules/msg/ut-coverage/test_cfe_msg_integrity.c @@ -0,0 +1,91 @@ +/************************************************************************ + * NASA Docket No. GSC-18,719-1, and identified as “core Flight System: Bootes” + * + * Copyright (c) 2020 United States Government as represented by the + * Administrator of the National Aeronautics and Space Administration. + * All Rights Reserved. + * + * Licensed 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. + ************************************************************************/ + +/* + * Test message verify + */ + +/* + * Includes + */ +#include "utassert.h" +#include "ut_support.h" +#include "cfe_msg.h" +#include "test_cfe_msg_verify.h" +#include "cfe_error.h" +#include "cfe_msg_defaults.h" + +#include + +/* + * Test MSG integrity origination action(s) + */ +void Test_MSG_OriginationAction(void) +{ + union + { + CFE_MSG_Message_t msg; + CFE_MSG_CommandHeader_t cmd; + CFE_MSG_TelemetryHeader_t tlm; + + } LocalBuf; + bool IsAcceptable; + + memset(&LocalBuf, 0, sizeof(LocalBuf)); + + /* bad buffer */ + UtAssert_INT32_EQ(CFE_MSG_OriginationAction(NULL, sizeof(LocalBuf), &IsAcceptable), CFE_MSG_BAD_ARGUMENT); + UtAssert_INT32_EQ(CFE_MSG_OriginationAction(&LocalBuf.msg, sizeof(LocalBuf), NULL), CFE_MSG_BAD_ARGUMENT); + + /* nominal, cmd */ + IsAcceptable = false; + CFE_MSG_SetType(&LocalBuf.msg, CFE_MSG_Type_Cmd); + CFE_UtAssert_SUCCESS(CFE_MSG_OriginationAction(&LocalBuf.msg, sizeof(LocalBuf), &IsAcceptable)); + UtAssert_BOOL_TRUE(IsAcceptable); + + /* nominal, tlm */ + IsAcceptable = false; + CFE_MSG_SetType(&LocalBuf.msg, CFE_MSG_Type_Tlm); + CFE_UtAssert_SUCCESS(CFE_MSG_OriginationAction(&LocalBuf.msg, sizeof(LocalBuf), &IsAcceptable)); + UtAssert_BOOL_TRUE(IsAcceptable); +} + +/* + * Test MSG integrity verification action(s) + */ +void Test_MSG_VerificationAction(void) +{ + union + { + CFE_MSG_Message_t msg; + CFE_MSG_CommandHeader_t cmd; + CFE_MSG_TelemetryHeader_t tlm; + } LocalBuf; + bool IsAcceptable; + + memset(&LocalBuf, 0, sizeof(LocalBuf)); + + /* bad buffer */ + UtAssert_INT32_EQ(CFE_MSG_VerificationAction(NULL, sizeof(LocalBuf), &IsAcceptable), CFE_MSG_BAD_ARGUMENT); + UtAssert_INT32_EQ(CFE_MSG_VerificationAction(&LocalBuf.msg, sizeof(LocalBuf), NULL), CFE_MSG_BAD_ARGUMENT); + + /* nominal */ + IsAcceptable = false; + CFE_UtAssert_SUCCESS(CFE_MSG_VerificationAction(&LocalBuf.msg, sizeof(LocalBuf), &IsAcceptable)); + UtAssert_BOOL_TRUE(IsAcceptable); +} diff --git a/modules/msg/ut-coverage/test_cfe_msg_verify.c b/modules/msg/ut-coverage/test_cfe_msg_verify.c deleted file mode 100644 index 87c5faa73..000000000 --- a/modules/msg/ut-coverage/test_cfe_msg_verify.c +++ /dev/null @@ -1,59 +0,0 @@ -/************************************************************************ - * NASA Docket No. GSC-18,719-1, and identified as “core Flight System: Bootes” - * - * Copyright (c) 2020 United States Government as represented by the - * Administrator of the National Aeronautics and Space Administration. - * All Rights Reserved. - * - * Licensed 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. - ************************************************************************/ - -/* - * Test message verify - */ - -/* - * Includes - */ -#include "utassert.h" -#include "ut_support.h" -#include "cfe_msg.h" -#include "test_cfe_msg_verify.h" -#include "cfe_error.h" -#include "cfe_msg_defaults.h" - -#include - -/* - * Test MSG Verify - */ -void Test_MSG_Verify(void) -{ - union - { - CFE_MSG_Message_t msg; - CFE_MSG_CommandHeader_t cmd; - CFE_MSG_TelemetryHeader_t tlm; - } LocalBuf; - bool Result; - - memset(&LocalBuf, 0, sizeof(LocalBuf)); - Result = false; - - /* bad buffer */ - UtAssert_INT32_EQ(CFE_MSG_Verify(NULL, &Result), CFE_MSG_BAD_ARGUMENT); - UtAssert_INT32_EQ(CFE_MSG_Verify(&LocalBuf.msg, NULL), CFE_MSG_BAD_ARGUMENT); - - /* nominal */ - Result = false; - CFE_UtAssert_SUCCESS(CFE_MSG_Verify(&LocalBuf.msg, &Result)); - UtAssert_BOOL_TRUE(Result); -} diff --git a/modules/msg/ut-coverage/test_cfe_msg_verify.h b/modules/msg/ut-coverage/test_cfe_msg_verify.h index f017560e2..024412ea4 100644 --- a/modules/msg/ut-coverage/test_cfe_msg_verify.h +++ b/modules/msg/ut-coverage/test_cfe_msg_verify.h @@ -31,6 +31,6 @@ /* * Functions */ -void Test_MSG_Verify(void); +void Test_MSG_VerificationAction(void); #endif /* TEST_CFE_MSG_VERIFY_H */ diff --git a/modules/sb/fsw/inc/cfe_sb_eventids.h b/modules/sb/fsw/inc/cfe_sb_eventids.h index 326542f9c..0f59304a2 100644 --- a/modules/sb/fsw/inc/cfe_sb_eventids.h +++ b/modules/sb/fsw/inc/cfe_sb_eventids.h @@ -759,6 +759,31 @@ * #CFE_SB_CreatePipe API failure due to no free queues. */ #define CFE_SB_CR_PIPE_NO_FREE_EID 70 + +/** + * \brief SB integrity actions on transmit message failure event + * + * \par Type: ERROR + * + * \par Cause: + * + * A CFE SB transmit transaction has rejected a message due + * to failure of the associated message integrity action(s). + */ +#define CFE_SB_SEND_MESSAGE_INTEGRITY_FAIL_EID 71 + +/** + * \brief SB validation of received message failure event + * + * \par Type: ERROR + * + * \par Cause: + * + * A CFE SB receive transaction has rejected a message due + * to failure of the associated message integrity action(s). + */ +#define CFE_SB_RCV_MESSAGE_INTEGRITY_FAIL_EID 72 + /**\}*/ #endif /* CFE_SB_EVENTS_H */ diff --git a/modules/sb/fsw/src/cfe_sb_api.c b/modules/sb/fsw/src/cfe_sb_api.c index b4747f452..f2feffd31 100644 --- a/modules/sb/fsw/src/cfe_sb_api.c +++ b/modules/sb/fsw/src/cfe_sb_api.c @@ -1281,460 +1281,6 @@ int32 CFE_SB_UnsubscribeFull(CFE_SB_MsgId_t MsgId, CFE_SB_PipeId_t PipeId, uint8 return Status; } -/*---------------------------------------------------------------- - * - * Implemented per public API - * See description in header file for argument/return detail - * - *-----------------------------------------------------------------*/ -CFE_Status_t CFE_SB_TransmitMsg(const CFE_MSG_Message_t *MsgPtr, bool UpdateHeader) -{ - int32 Status; - CFE_MSG_Size_t Size = 0; - CFE_SB_MsgId_t MsgId = CFE_SB_INVALID_MSG_ID; - CFE_ES_TaskId_t TskId; - char FullName[(OS_MAX_API_NAME * 2)]; - CFE_SB_BufferD_t *BufDscPtr; - CFE_SBR_RouteId_t RouteId; - uint16 PendingEventID; - - PendingEventID = 0; - BufDscPtr = NULL; - RouteId = CFE_SBR_INVALID_ROUTE_ID; - - Status = CFE_SB_TransmitMsgValidate(MsgPtr, &MsgId, &Size, &RouteId); - - CFE_SB_LockSharedData(__func__, __LINE__); - - if (Status == CFE_SUCCESS && CFE_SBR_IsValidRouteId(RouteId)) - { - /* Get buffer - note this pre-initializes the returned buffer with - * a use count of 1, which refers to this task as it fills the buffer. */ - BufDscPtr = CFE_SB_GetBufferFromPool(Size); - if (BufDscPtr == NULL) - { - PendingEventID = CFE_SB_GET_BUF_ERR_EID; - Status = CFE_SB_BUF_ALOC_ERR; - } - } - - /* - * Increment the MsgSendErrorCounter only if there was a real error, - * such as a validation issue or failure to allocate a buffer. - * - * (This should NOT be done if simply no route) - */ - if (Status != CFE_SUCCESS) - { - CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter++; - } - - CFE_SB_UnlockSharedData(__func__, __LINE__); - - /* - * If a buffer was obtained above, then copy the content into it - * and broadcast it to all subscribers in the route. - * - * Note - if there is no route / no subscribers, the "Status" will - * be CFE_SUCCESS because CFE_SB_TransmitMsgValidate() succeeded, - * but there will be no buffer because CFE_SBR_IsValidRouteId() returned - * false. - * - * But if the descriptor is non-null it means the message is valid and - * there is a route to send it to. - */ - if (BufDscPtr != NULL) - { - /* Copy actual message content into buffer and set its metadata */ - memcpy(&BufDscPtr->Content, MsgPtr, Size); - BufDscPtr->MsgId = MsgId; - BufDscPtr->ContentSize = Size; - BufDscPtr->NeedsUpdate = UpdateHeader; - CFE_MSG_GetType(MsgPtr, &BufDscPtr->ContentType); - - /* - * This routine will use best-effort to send to all subscribers, - * increment the buffer use count for every successful delivery, - * and send an event/increment counter for any unsuccessful delivery. - */ - CFE_SB_BroadcastBufferToRoute(BufDscPtr, RouteId); - - /* - * The broadcast function consumes the buffer, so it should not be - * accessed in this function anymore - */ - BufDscPtr = NULL; - } - - if (PendingEventID == CFE_SB_GET_BUF_ERR_EID) - { - /* Get task id for events and Sender Info*/ - CFE_ES_GetTaskID(&TskId); - - if (CFE_SB_RequestToSendEvent(TskId, CFE_SB_GET_BUF_ERR_EID_BIT) == CFE_SB_GRANTED) - { - CFE_EVS_SendEventWithAppID(CFE_SB_GET_BUF_ERR_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, - "Send Err:Request for Buffer Failed. MsgId 0x%x,app %s,size %d", - (unsigned int)CFE_SB_MsgIdToValue(MsgId), CFE_SB_GetAppTskName(TskId, FullName), - (int)Size); - - /* clear the bit so the task may send this event again */ - CFE_SB_FinishSendEvent(TskId, CFE_SB_GET_BUF_ERR_EID_BIT); - } - } - - return Status; -} - -/*---------------------------------------------------------------- - * - * Application-scope internal function - * See description in header file for argument/return detail - * - *-----------------------------------------------------------------*/ -int32 CFE_SB_TransmitMsgValidate(const CFE_MSG_Message_t *MsgPtr, CFE_SB_MsgId_t *MsgIdPtr, CFE_MSG_Size_t *SizePtr, - CFE_SBR_RouteId_t *RouteIdPtr) -{ - CFE_ES_TaskId_t TskId; - char FullName[(OS_MAX_API_NAME * 2)]; - uint16 PendingEventID; - int32 Status; - - PendingEventID = 0; - Status = CFE_SUCCESS; - - /* check input parameter */ - if (MsgPtr == NULL) - { - PendingEventID = CFE_SB_SEND_BAD_ARG_EID; - Status = CFE_SB_BAD_ARGUMENT; - } - - if (Status == CFE_SUCCESS) - { - CFE_MSG_GetMsgId(MsgPtr, MsgIdPtr); - - /* validate the msgid in the message */ - if (!CFE_SB_IsValidMsgId(*MsgIdPtr)) - { - PendingEventID = CFE_SB_SEND_INV_MSGID_EID; - Status = CFE_SB_BAD_ARGUMENT; - } - } - - if (Status == CFE_SUCCESS) - { - CFE_MSG_GetSize(MsgPtr, SizePtr); - - /* Verify the size of the pkt is < or = the mission defined max */ - if (*SizePtr > CFE_MISSION_SB_MAX_SB_MSG_SIZE) - { - PendingEventID = CFE_SB_MSG_TOO_BIG_EID; - Status = CFE_SB_MSG_TOO_BIG; - } - } - - if (Status == CFE_SUCCESS) - { - /* check the route, which should be done while locked */ - CFE_SB_LockSharedData(__func__, __LINE__); - - /* Get the routing id */ - *RouteIdPtr = CFE_SBR_GetRouteId(*MsgIdPtr); - - /* if there have been no subscriptions for this pkt, */ - /* increment the dropped pkt cnt, send event and return success */ - if (!CFE_SBR_IsValidRouteId(*RouteIdPtr)) - { - CFE_SB_Global.HKTlmMsg.Payload.NoSubscribersCounter++; - PendingEventID = CFE_SB_SEND_NO_SUBS_EID; - } - - CFE_SB_UnlockSharedData(__func__, __LINE__); - } - - if (PendingEventID != 0) - { - /* get task id for events */ - CFE_ES_GetTaskID(&TskId); - - switch (PendingEventID) - { - case CFE_SB_SEND_BAD_ARG_EID: - if (CFE_SB_RequestToSendEvent(TskId, CFE_SB_SEND_BAD_ARG_EID_BIT) == CFE_SB_GRANTED) - { - CFE_EVS_SendEventWithAppID(CFE_SB_SEND_BAD_ARG_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, - "Send Err:Bad input argument,Arg 0x%lx,App %s", (unsigned long)MsgPtr, - CFE_SB_GetAppTskName(TskId, FullName)); - - /* clear the bit so the task may send this event again */ - CFE_SB_FinishSendEvent(TskId, CFE_SB_SEND_BAD_ARG_EID_BIT); - } - break; - - case CFE_SB_SEND_INV_MSGID_EID: - if (CFE_SB_RequestToSendEvent(TskId, CFE_SB_SEND_INV_MSGID_EID_BIT) == CFE_SB_GRANTED) - { - CFE_EVS_SendEventWithAppID(CFE_SB_SEND_INV_MSGID_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, - "Send Err:Invalid MsgId(0x%x)in msg,App %s", - (unsigned int)CFE_SB_MsgIdToValue(*MsgIdPtr), - CFE_SB_GetAppTskName(TskId, FullName)); - - /* clear the bit so the task may send this event again */ - CFE_SB_FinishSendEvent(TskId, CFE_SB_SEND_INV_MSGID_EID_BIT); - } - break; - - case CFE_SB_MSG_TOO_BIG_EID: - if (CFE_SB_RequestToSendEvent(TskId, CFE_SB_MSG_TOO_BIG_EID_BIT) == CFE_SB_GRANTED) - { - CFE_EVS_SendEventWithAppID(CFE_SB_MSG_TOO_BIG_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, - "Send Err:Msg Too Big MsgId=0x%x,app=%s,size=%d,MaxSz=%d", - (unsigned int)CFE_SB_MsgIdToValue(*MsgIdPtr), - CFE_SB_GetAppTskName(TskId, FullName), (int)*SizePtr, - CFE_MISSION_SB_MAX_SB_MSG_SIZE); - - /* clear the bit so the task may send this event again */ - CFE_SB_FinishSendEvent(TskId, CFE_SB_MSG_TOO_BIG_EID_BIT); - } - break; - - case CFE_SB_SEND_NO_SUBS_EID: - /* Determine if event can be sent without causing recursive event problem */ - if (CFE_SB_RequestToSendEvent(TskId, CFE_SB_SEND_NO_SUBS_EID_BIT) == CFE_SB_GRANTED) - { - CFE_EVS_SendEventWithAppID(CFE_SB_SEND_NO_SUBS_EID, CFE_EVS_EventType_INFORMATION, - CFE_SB_Global.AppId, "No subscribers for MsgId 0x%x,sender %s", - (unsigned int)CFE_SB_MsgIdToValue(*MsgIdPtr), - CFE_SB_GetAppTskName(TskId, FullName)); - - /* clear the bit so the task may send this event again */ - CFE_SB_FinishSendEvent(TskId, CFE_SB_SEND_NO_SUBS_EID_BIT); - } - break; - } - } - - return Status; -} - -/*---------------------------------------------------------------- - * - * Application-scope internal function - * See description in header file for argument/return detail - * - *-----------------------------------------------------------------*/ -void CFE_SB_BroadcastBufferToRoute(CFE_SB_BufferD_t *BufDscPtr, CFE_SBR_RouteId_t RouteId) -{ - CFE_ES_AppId_t AppId; - CFE_ES_TaskId_t TskId; - CFE_SB_DestinationD_t *DestPtr; - CFE_SB_PipeD_t * PipeDscPtr; - CFE_SB_EventBuf_t SBSndErr; - int32 OsStatus; - uint32 i; - char FullName[(OS_MAX_API_NAME * 2)]; - char PipeName[OS_MAX_API_NAME]; - - SBSndErr.EvtsToSnd = 0; - - /* get app id for loopback testing */ - CFE_ES_GetAppID(&AppId); - - /* get task id for events and Sender Info*/ - CFE_ES_GetTaskID(&TskId); - - /* take semaphore to prevent a task switch during processing */ - CFE_SB_LockSharedData(__func__, __LINE__); - - /* For an invalid route / no subscribers this whole logic can be skipped */ - if (CFE_SBR_IsValidRouteId(RouteId)) - { - /* Set the seq count if requested (while locked) before actually sending */ - if (BufDscPtr->NeedsUpdate) - { - CFE_SBR_IncrementSequenceCounter(RouteId); - - /* Update all MSG headers based on the current sequence */ - CFE_MSG_UpdateHeader(&BufDscPtr->Content.Msg, CFE_SBR_GetSequenceCounter(RouteId)); - - /* Clear the flag, just in case */ - BufDscPtr->NeedsUpdate = false; - } - - /* Send the packet to all destinations */ - for (DestPtr = CFE_SBR_GetDestListHeadPtr(RouteId); DestPtr != NULL; DestPtr = DestPtr->Next) - { - if (DestPtr->Active == CFE_SB_ACTIVE) /* destination is active */ - { - PipeDscPtr = CFE_SB_LocatePipeDescByID(DestPtr->PipeId); - } - else - { - PipeDscPtr = NULL; - } - - if (!CFE_SB_PipeDescIsMatch(PipeDscPtr, DestPtr->PipeId)) - { - continue; - } - - if ((PipeDscPtr->Opts & CFE_SB_PIPEOPTS_IGNOREMINE) != 0 && - CFE_RESOURCEID_TEST_EQUAL(PipeDscPtr->AppId, AppId)) - { - continue; - } - - /* if Msg limit exceeded, log event, increment counter */ - /* and go to next destination */ - if (DestPtr->BuffCount >= DestPtr->MsgId2PipeLim) - { - SBSndErr.EvtBuf[SBSndErr.EvtsToSnd].PipeId = DestPtr->PipeId; - SBSndErr.EvtBuf[SBSndErr.EvtsToSnd].EventId = CFE_SB_MSGID_LIM_ERR_EID; - SBSndErr.EvtsToSnd++; - CFE_SB_Global.HKTlmMsg.Payload.MsgLimitErrorCounter++; - PipeDscPtr->SendErrors++; - - continue; - } - - /* - ** Write the buffer descriptor to the queue of the pipe. If the write - ** failed, log info and increment the pipe's error counter. - */ - OsStatus = OS_QueuePut(PipeDscPtr->SysQueueId, &BufDscPtr, sizeof(BufDscPtr), 0); - - if (OsStatus == OS_SUCCESS) - { - /* The queue now holds a ref to the buffer, so increment its ref count. */ - CFE_SB_IncrBufUseCnt(BufDscPtr); - - DestPtr->BuffCount++; /* used for checking MsgId2PipeLimit */ - DestPtr->DestCnt++; /* used for statistics */ - ++PipeDscPtr->CurrentQueueDepth; - if (PipeDscPtr->CurrentQueueDepth >= PipeDscPtr->PeakQueueDepth) - { - PipeDscPtr->PeakQueueDepth = PipeDscPtr->CurrentQueueDepth; - } - } - else - { - SBSndErr.EvtBuf[SBSndErr.EvtsToSnd].PipeId = DestPtr->PipeId; - if (OsStatus == OS_QUEUE_FULL) - { - SBSndErr.EvtBuf[SBSndErr.EvtsToSnd].EventId = CFE_SB_Q_FULL_ERR_EID; - CFE_SB_Global.HKTlmMsg.Payload.PipeOverflowErrorCounter++; - } - else - { - /* Unexpected error while writing to queue. */ - SBSndErr.EvtBuf[SBSndErr.EvtsToSnd].EventId = CFE_SB_Q_WR_ERR_EID; - SBSndErr.EvtBuf[SBSndErr.EvtsToSnd].OsStatus = OsStatus; - CFE_SB_Global.HKTlmMsg.Payload.InternalErrorCounter++; - } - SBSndErr.EvtsToSnd++; - PipeDscPtr->SendErrors++; - } /*end if */ - - } /* end loop over destinations */ - } - - /* - * If any specific delivery issues occurred, also increment the - * general error count before releasing the lock. - */ - if (SBSndErr.EvtsToSnd > 0) - { - CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter++; - } - - /* - * Remove this from whatever list it was in - * - * If it was a singleton/new buffer this has no effect. - * If it was a zero-copy buffer this removes it from the ZeroCopyList. - */ - CFE_SB_TrackingListRemove(&BufDscPtr->Link); - - /* clear the AppID field in case it was a zero copy buffer, - * as it is no longer owned by that app after broadcasting */ - BufDscPtr->AppId = CFE_ES_APPID_UNDEFINED; - - /* track the buffer as an in-transit message */ - CFE_SB_TrackingListAdd(&CFE_SB_Global.InTransitList, &BufDscPtr->Link); - - /* - ** Decrement the buffer UseCount and free buffer if cnt=0. This decrement is done - ** because the use cnt is initialized to 1 in CFE_SB_GetBufferFromPool. - ** Initializing the count to 1 (as opposed to zero) and decrementing it here are - ** done to ensure the buffer gets released when there are destinations that have - ** been disabled via ground command. - */ - CFE_SB_DecrBufUseCnt(BufDscPtr); - - /* release the semaphore */ - CFE_SB_UnlockSharedData(__func__, __LINE__); - - /* send an event for each pipe write error that may have occurred */ - for (i = 0; i < SBSndErr.EvtsToSnd; i++) - { - if (SBSndErr.EvtBuf[i].EventId == CFE_SB_MSGID_LIM_ERR_EID) - { - /* Determine if event can be sent without causing recursive event problem */ - if (CFE_SB_RequestToSendEvent(TskId, CFE_SB_MSGID_LIM_ERR_EID_BIT) == CFE_SB_GRANTED) - { - CFE_SB_GetPipeName(PipeName, sizeof(PipeName), SBSndErr.EvtBuf[i].PipeId); - - CFE_ES_PerfLogEntry(CFE_MISSION_SB_MSG_LIM_PERF_ID); - CFE_ES_PerfLogExit(CFE_MISSION_SB_MSG_LIM_PERF_ID); - - CFE_EVS_SendEventWithAppID(CFE_SB_MSGID_LIM_ERR_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, - "Msg Limit Err,MsgId 0x%x,pipe %s,sender %s", - (unsigned int)CFE_SB_MsgIdToValue(BufDscPtr->MsgId), PipeName, - CFE_SB_GetAppTskName(TskId, FullName)); - - /* clear the bit so the task may send this event again */ - CFE_SB_FinishSendEvent(TskId, CFE_SB_MSGID_LIM_ERR_EID_BIT); - } - } - else if (SBSndErr.EvtBuf[i].EventId == CFE_SB_Q_FULL_ERR_EID) - { - /* Determine if event can be sent without causing recursive event problem */ - if (CFE_SB_RequestToSendEvent(TskId, CFE_SB_Q_FULL_ERR_EID_BIT) == CFE_SB_GRANTED) - { - CFE_SB_GetPipeName(PipeName, sizeof(PipeName), SBSndErr.EvtBuf[i].PipeId); - - CFE_ES_PerfLogEntry(CFE_MISSION_SB_PIPE_OFLOW_PERF_ID); - CFE_ES_PerfLogExit(CFE_MISSION_SB_PIPE_OFLOW_PERF_ID); - - CFE_EVS_SendEventWithAppID(CFE_SB_Q_FULL_ERR_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, - "Pipe Overflow,MsgId 0x%x,pipe %s,sender %s", - (unsigned int)CFE_SB_MsgIdToValue(BufDscPtr->MsgId), PipeName, - CFE_SB_GetAppTskName(TskId, FullName)); - - /* clear the bit so the task may send this event again */ - CFE_SB_FinishSendEvent(TskId, CFE_SB_Q_FULL_ERR_EID_BIT); - } - } - else - { - /* Determine if event can be sent without causing recursive event problem */ - if (CFE_SB_RequestToSendEvent(TskId, CFE_SB_Q_WR_ERR_EID_BIT) == CFE_SB_GRANTED) - { - CFE_SB_GetPipeName(PipeName, sizeof(PipeName), SBSndErr.EvtBuf[i].PipeId); - - CFE_EVS_SendEventWithAppID(CFE_SB_Q_WR_ERR_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, - "Pipe Write Err,MsgId 0x%x,pipe %s,sender %s,stat %ld", - (unsigned int)CFE_SB_MsgIdToValue(BufDscPtr->MsgId), PipeName, - CFE_SB_GetAppTskName(TskId, FullName), (long)(SBSndErr.EvtBuf[i].OsStatus)); - - /* clear the bit so the task may send this event again */ - CFE_SB_FinishSendEvent(TskId, CFE_SB_Q_WR_ERR_EID_BIT); - } - } - } -} - /*---------------------------------------------------------------- * * Implemented per public API @@ -1743,251 +1289,41 @@ void CFE_SB_BroadcastBufferToRoute(CFE_SB_BufferD_t *BufDscPtr, CFE_SBR_RouteId_ *-----------------------------------------------------------------*/ CFE_Status_t CFE_SB_ReceiveBuffer(CFE_SB_Buffer_t **BufPtr, CFE_SB_PipeId_t PipeId, int32 TimeOut) { - int32 Status; - int32 OsStatus; - CFE_SB_BufferD_t * BufDscPtr; - size_t BufDscSize; - CFE_SB_PipeD_t * PipeDscPtr; - CFE_SB_DestinationD_t *DestPtr; - CFE_SBR_RouteId_t RouteId; - CFE_ES_TaskId_t TskId; - uint16 PendingEventID; - osal_id_t SysQueueId; - int32 SysTimeout; - char FullName[(OS_MAX_API_NAME * 2)]; + CFE_SB_ReceiveTxn_State_t TxnBuf; + CFE_SB_MessageTxn_State_t *Txn; - PendingEventID = 0; - Status = CFE_SUCCESS; - SysTimeout = OS_PEND; - SysQueueId = OS_OBJECT_ID_UNDEFINED; - PipeDscPtr = NULL; - BufDscPtr = NULL; - DestPtr = NULL; - BufDscSize = 0; - OsStatus = OS_SUCCESS; + Txn = CFE_SB_ReceiveTxn_Init(&TxnBuf, BufPtr); - /* - * Check input args and see if any are bad, which require - * a "BAD_ARG_EID" to be generated. - * - * Also translate the timeout here. Timeouts greater than 0 - * may be passed to OSAL directly, but the two fixed constants - * CFE_SB_PEND_FOREVER and CFE_SB_POLL are checked explicitly, - * to maintain API independence - even though the values are - * currently defined the same. - */ - - if (BufPtr == NULL) - { - PendingEventID = CFE_SB_RCV_BAD_ARG_EID; - Status = CFE_SB_BAD_ARGUMENT; - } - else if (TimeOut > 0) - { - /* time outs greater than 0 can be passed to OSAL directly */ - SysTimeout = TimeOut; - } - else if (TimeOut == CFE_SB_POLL) - { - SysTimeout = OS_CHECK; - } - else if (TimeOut != CFE_SB_PEND_FOREVER) - { - /* any other timeout value is invalid */ - PendingEventID = CFE_SB_RCV_BAD_ARG_EID; - Status = CFE_SB_BAD_ARGUMENT; - } - - /* If OK, then lock and pull relevant info from Pipe Descriptor */ - if (Status == CFE_SUCCESS) + if (CFE_SB_MessageTxn_IsOK(Txn)) { - CFE_SB_LockSharedData(__func__, __LINE__); - - PipeDscPtr = CFE_SB_LocatePipeDescByID(PipeId); - - /* If the pipe does not exist or PipeId is out of range... */ - if (!CFE_SB_PipeDescIsMatch(PipeDscPtr, PipeId)) - { - PendingEventID = CFE_SB_BAD_PIPEID_EID; - Status = CFE_SB_BAD_ARGUMENT; - } - else - { - /* Grab the queue ID */ - SysQueueId = PipeDscPtr->SysQueueId; - - /* - * Un-reference any previous buffer from the last call. - * - * NOTE: This is historical behavior where apps call CFE_SB_ReceiveBuffer() - * in the loop within the app's main task. There is currently no separate - * API to "free" or unreference a buffer that was returned from SB. - * - * Instead, each time this function is invoked, it is implicitly interpreted - * as an indication that the caller is done with the previous buffer. - * - * Unfortunately this prevents pipe IDs from being serviced/shared across - * multiple child tasks in a worker pattern design. This may be changed - * in a future version of CFE to decouple these actions, to allow for - * multiple workers to service the same pipe. - */ - if (PipeDscPtr->LastBuffer != NULL) - { - /* Decrement the Buffer Use Count, which will Free buffer if it becomes 0 */ - CFE_SB_DecrBufUseCnt(PipeDscPtr->LastBuffer); - PipeDscPtr->LastBuffer = NULL; - } - } - - CFE_SB_UnlockSharedData(__func__, __LINE__); + CFE_SB_MessageTxn_SetTimeout(Txn, TimeOut); } - /* - * If everything validated, then proceed to get a buffer from the queue. - * This must be done OUTSIDE the SB lock, as this call likely blocks. - */ - if (Status == CFE_SUCCESS) + if (CFE_SB_MessageTxn_IsOK(Txn)) { - /* Read the buffer descriptor address from the queue. */ - OsStatus = OS_QueueGet(SysQueueId, &BufDscPtr, sizeof(BufDscPtr), &BufDscSize, SysTimeout); + CFE_SB_ReceiveTxn_SetPipeId(Txn, PipeId); /* - * translate the return value - - * - * CFE functions have their own set of RC values should not directly return OSAL codes - * The size should always match. If it does not, then generate CFE_SB_Q_RD_ERR_EID. + * Set the verify flag true by default - + * in the default impl verify is a no-op that always succeeds. + * If an actual implementation is provided, it will be used here. */ - if (OsStatus == OS_SUCCESS && BufDscPtr != NULL && BufDscSize == sizeof(BufDscPtr)) - { - /* Pass through */ - } - else if (OsStatus == OS_QUEUE_EMPTY) - { - /* normal if using CFE_SB_POLL */ - Status = CFE_SB_NO_MESSAGE; - } - else if (OsStatus == OS_QUEUE_TIMEOUT) - { - /* normal if using a nonzero timeout */ - Status = CFE_SB_TIME_OUT; - } - else - { - /* off-nominal condition, report an error event */ - PendingEventID = CFE_SB_Q_RD_ERR_EID; - Status = CFE_SB_PIPE_RD_ERR; - } + CFE_SB_MessageTxn_SetEndpoint(Txn, true); } - /* Now re-lock to store the buffer in the pipe descriptor */ - CFE_SB_LockSharedData(__func__, __LINE__); - - if (Status == CFE_SUCCESS) + if (BufPtr != NULL) { /* - * NOTE: This uses the same PipeDscPtr that was found earlier. - * Technically it is possible that the pipe was changed between now and then, - * but the current PipeID definition doesn't really allow this to be detected. + * Note - the API should qualify the parameter as "const", but this is + * kept non-const for backward compatibility. Callers should never write to + * the returned buffer, it is const in practice. */ - if (CFE_SB_PipeDescIsMatch(PipeDscPtr, PipeId)) - { - /* - ** Load the pipe tables 'CurrentBuff' with the buffer descriptor - ** ptr corresponding to the message just read. This is done so that - ** the buffer can be released on the next receive call for this pipe. - ** - ** This counts as a new reference as it is being stored in the PipeDsc - */ - CFE_SB_IncrBufUseCnt(BufDscPtr); - PipeDscPtr->LastBuffer = BufDscPtr; - - /* - * Also set the Receivers pointer to the address of the actual message - * (currently this is "borrowing" the ref above, not its own ref) - */ - *BufPtr = &BufDscPtr->Content; - - /* get pointer to destination to be used in decrementing msg limit cnt*/ - RouteId = CFE_SBR_GetRouteId(BufDscPtr->MsgId); - DestPtr = CFE_SB_GetDestPtr(RouteId, PipeId); - - /* - ** DestPtr would be NULL if the msg is unsubscribed to while it is on - ** the pipe. The BuffCount may be zero if the msg is unsubscribed to and - ** then resubscribed to while it is on the pipe. Both of these cases are - ** considered nominal and are handled by the code below. - */ - if (DestPtr != NULL && DestPtr->BuffCount > 0) - { - DestPtr->BuffCount--; - } - - if (PipeDscPtr->CurrentQueueDepth > 0) - { - --PipeDscPtr->CurrentQueueDepth; - } - } - else - { - /* should send the bad pipe ID event here too */ - PendingEventID = CFE_SB_BAD_PIPEID_EID; - Status = CFE_SB_PIPE_RD_ERR; - } - - /* Always decrement the use count, for the ref that was in the queue */ - CFE_SB_DecrBufUseCnt(BufDscPtr); - } - - /* Before unlocking, increment relevant error counter if needed */ - if (Status != CFE_SUCCESS && Status != CFE_SB_NO_MESSAGE && Status != CFE_SB_TIME_OUT) - { - if (PendingEventID == CFE_SB_RCV_BAD_ARG_EID || PendingEventID == CFE_SB_BAD_PIPEID_EID) - { - ++CFE_SB_Global.HKTlmMsg.Payload.MsgReceiveErrorCounter; - } - else - { - /* For any other unexpected error (e.g. CFE_SB_Q_RD_ERR_EID) */ - ++CFE_SB_Global.HKTlmMsg.Payload.InternalErrorCounter; - } - } - - CFE_SB_UnlockSharedData(__func__, __LINE__); - - /* Now actually send the event, after unlocking (do not call EVS with SB locked) */ - if (PendingEventID != 0) - { - /* get task id for events */ - CFE_ES_GetTaskID(&TskId); - - switch (PendingEventID) - { - case CFE_SB_Q_RD_ERR_EID: - CFE_EVS_SendEventWithAppID(CFE_SB_Q_RD_ERR_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, - "Pipe Read Err,pipe %lu,app %s,stat %ld", CFE_RESOURCEID_TO_ULONG(PipeId), - CFE_SB_GetAppTskName(TskId, FullName), (long)OsStatus); - break; - case CFE_SB_RCV_BAD_ARG_EID: - CFE_EVS_SendEventWithAppID(CFE_SB_RCV_BAD_ARG_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, - "Rcv Err:Bad Input Arg:BufPtr 0x%lx,pipe %lu,t/o %d,app %s", - (unsigned long)BufPtr, CFE_RESOURCEID_TO_ULONG(PipeId), (int)TimeOut, - CFE_SB_GetAppTskName(TskId, FullName)); - break; - case CFE_SB_BAD_PIPEID_EID: - CFE_EVS_SendEventWithAppID(CFE_SB_BAD_PIPEID_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, - "Rcv Err:PipeId %lu does not exist,app %s", CFE_RESOURCEID_TO_ULONG(PipeId), - CFE_SB_GetAppTskName(TskId, FullName)); - break; - } + *BufPtr = (CFE_SB_Buffer_t *)CFE_SB_ReceiveTxn_Execute(Txn); } - /* If not successful, set the output pointer to NULL */ - if (Status != CFE_SUCCESS && BufPtr != NULL) - { - *BufPtr = NULL; - } + CFE_SB_MessageTxn_ReportEvents(Txn); - return Status; + return CFE_SB_MessageTxn_GetStatus(Txn); } /*---------------------------------------------------------------- @@ -2050,13 +1386,14 @@ CFE_SB_Buffer_t *CFE_SB_AllocateMessageBuffer(size_t MsgSize) /*---------------------------------------------------------------- * - * Application-scope internal function + * Implemented per public API * See description in header file for argument/return detail * *-----------------------------------------------------------------*/ -int32 CFE_SB_ZeroCopyBufferValidate(CFE_SB_Buffer_t *BufPtr, CFE_SB_BufferD_t **BufDscPtr) +CFE_Status_t CFE_SB_ReleaseMessageBuffer(CFE_SB_Buffer_t *BufPtr) { - cpuaddr BufDscAddr; + CFE_SB_BufferD_t *BufDscPtr; + int32 Status; /* * Sanity Check that the pointers are not NULL @@ -2066,23 +1403,19 @@ int32 CFE_SB_ZeroCopyBufferValidate(CFE_SB_Buffer_t *BufPtr, CFE_SB_BufferD_t ** return CFE_SB_BAD_ARGUMENT; } - /* - * Calculate descriptor pointer from buffer pointer - - * The buffer is just a member (offset) in the descriptor - */ - BufDscAddr = (cpuaddr)BufPtr - offsetof(CFE_SB_BufferD_t, Content); - *BufDscPtr = (CFE_SB_BufferD_t *)BufDscAddr; + CFE_SB_LockSharedData(__func__, __LINE__); - /* - * Check that the descriptor is actually a "zero copy" type, - */ - if (!CFE_RESOURCEID_TEST_DEFINED((*BufDscPtr)->AppId)) + Status = CFE_SB_ZeroCopyBufferValidate(BufPtr, &BufDscPtr); + if (Status == CFE_SUCCESS) { - return CFE_SB_BUFFER_INVALID; + /* Clear the ownership app ID and decrement use count (may also free) */ + BufDscPtr->AppId = CFE_ES_APPID_UNDEFINED; + CFE_SB_DecrBufUseCnt(BufDscPtr); } - /* Basic sanity check passed */ - return CFE_SUCCESS; + CFE_SB_UnlockSharedData(__func__, __LINE__); + + return Status; } /*---------------------------------------------------------------- @@ -2091,25 +1424,31 @@ int32 CFE_SB_ZeroCopyBufferValidate(CFE_SB_Buffer_t *BufPtr, CFE_SB_BufferD_t ** * See description in header file for argument/return detail * *-----------------------------------------------------------------*/ -CFE_Status_t CFE_SB_ReleaseMessageBuffer(CFE_SB_Buffer_t *BufPtr) +CFE_Status_t CFE_SB_TransmitBuffer(CFE_SB_Buffer_t *BufPtr, bool IsOrigination) { - CFE_SB_BufferD_t *BufDscPtr; - int32 Status; + CFE_SB_TransmitTxn_State_t TxnBuf; + CFE_SB_MessageTxn_State_t *Txn; - Status = CFE_SB_ZeroCopyBufferValidate(BufPtr, &BufDscPtr); + Txn = CFE_SB_TransmitTxn_Init(&TxnBuf, BufPtr); - CFE_SB_LockSharedData(__func__, __LINE__); + /* In this context, the user should have set the the size and MsgId in the content */ + if (CFE_SB_MessageTxn_IsOK(Txn)) + { + CFE_SB_TransmitTxn_SetupFromMsg(Txn, &BufPtr->Msg); + } - if (Status == CFE_SUCCESS) + if (CFE_SB_MessageTxn_IsOK(Txn)) { - /* Clear the ownership app ID and decrement use count (may also free) */ - BufDscPtr->AppId = CFE_ES_APPID_UNDEFINED; - CFE_SB_DecrBufUseCnt(BufDscPtr); + /* Save passed-in parameters */ + CFE_SB_MessageTxn_SetEndpoint(Txn, IsOrigination); + + CFE_SB_TransmitTxn_Execute(Txn, BufPtr); } - CFE_SB_UnlockSharedData(__func__, __LINE__); + /* send an event for each pipe write error that may have occurred */ + CFE_SB_MessageTxn_ReportEvents(Txn); - return Status; + return CFE_SB_MessageTxn_GetStatus(Txn); } /*---------------------------------------------------------------- @@ -2118,52 +1457,63 @@ CFE_Status_t CFE_SB_ReleaseMessageBuffer(CFE_SB_Buffer_t *BufPtr) * See description in header file for argument/return detail * *-----------------------------------------------------------------*/ -CFE_Status_t CFE_SB_TransmitBuffer(CFE_SB_Buffer_t *BufPtr, bool UpdateHeader) +CFE_Status_t CFE_SB_TransmitMsg(const CFE_MSG_Message_t *MsgPtr, bool IsOrigination) { - int32 Status; - CFE_SB_BufferD_t *BufDscPtr; - CFE_SBR_RouteId_t RouteId; + CFE_SB_TransmitTxn_State_t TxnBuf; + CFE_SB_MessageTxn_State_t *Txn; + CFE_SB_Buffer_t * BufPtr; - Status = CFE_SB_ZeroCopyBufferValidate(BufPtr, &BufDscPtr); + BufPtr = NULL; + Txn = CFE_SB_TransmitTxn_Init(&TxnBuf, MsgPtr); - if (Status == CFE_SUCCESS) + /* In this context, the user should have set the the size and MsgId in the content */ + if (CFE_SB_MessageTxn_IsOK(Txn)) { - /* Validate the content and get the MsgId, store it in the descriptor */ - Status = CFE_SB_TransmitMsgValidate(&BufPtr->Msg, &BufDscPtr->MsgId, &BufDscPtr->ContentSize, &RouteId); + CFE_SB_TransmitTxn_SetupFromMsg(Txn, MsgPtr); + } - /* - * Broadcast the message if validation succeeded. - * - * Note that for the case of no subscribers, the validation returns CFE_SUCCESS - * but the actual route ID may be invalid. This is OK and considered normal- - * the validation will increment the NoSubscribers count, but we should NOT - * increment the MsgSendErrorCounter here - it is not really a sending error to - * have no subscribers. CFE_SB_BroadcastBufferToRoute() will not send to - * anything if the route is not valid (benign). - */ - if (Status == CFE_SUCCESS) + if (CFE_SB_MessageTxn_IsOK(Txn)) + { + /* Get buffer - note this pre-initializes the returned buffer with + * a use count of 1, which refers to this task as it fills the buffer. */ + BufPtr = CFE_SB_AllocateMessageBuffer(CFE_SB_MessageTxn_GetContentSize(Txn)); + if (BufPtr == NULL) { - BufDscPtr->NeedsUpdate = UpdateHeader; - CFE_MSG_GetType(&BufPtr->Msg, &BufDscPtr->ContentType); - - /* Now broadcast the message, which consumes the buffer */ - CFE_SB_BroadcastBufferToRoute(BufDscPtr, RouteId); - - /* - * IMPORTANT - the descriptor might be freed at any time after this, - * so the descriptor should not be accessed again after this point. - */ - BufDscPtr = NULL; + CFE_SB_MessageTxn_SetEventAndStatus(Txn, CFE_SB_GET_BUF_ERR_EID, CFE_SB_BUF_ALOC_ERR); } } - if (Status != CFE_SUCCESS) + /* + * If a buffer was obtained above, then copy the content into it + * and broadcast it to all subscribers in the route. + * + * Note - if there is no route / no subscribers, the "Status" will + * be CFE_SUCCESS because CFE_SB_TransmitMsgValidate() succeeded, + * but there will be no buffer because CFE_SBR_IsValidRouteId() returned + * false. + * + * But if the descriptor is non-null it means the message is valid and + * there is a route to send it to. + */ + if (CFE_SB_MessageTxn_IsOK(Txn)) { - /* Increment send error counter for validation failure */ - CFE_SB_LockSharedData(__func__, __LINE__); - CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter++; - CFE_SB_UnlockSharedData(__func__, __LINE__); + /* Copy actual message content into buffer */ + memcpy(&BufPtr->Msg, MsgPtr, CFE_SB_MessageTxn_GetContentSize(Txn)); + + /* Save passed-in parameters */ + CFE_SB_MessageTxn_SetEndpoint(Txn, IsOrigination); + + CFE_SB_TransmitTxn_Execute(Txn, BufPtr); + + /* + * The broadcast function consumes the buffer, so it should not be + * accessed in this function anymore + */ + BufPtr = NULL; } - return Status; + /* send an event for each pipe write error that may have occurred */ + CFE_SB_MessageTxn_ReportEvents(Txn); + + return CFE_SB_MessageTxn_GetStatus(Txn); } diff --git a/modules/sb/fsw/src/cfe_sb_buf.c b/modules/sb/fsw/src/cfe_sb_buf.c index 1f5dd0156..5853cefb6 100644 --- a/modules/sb/fsw/src/cfe_sb_buf.c +++ b/modules/sb/fsw/src/cfe_sb_buf.c @@ -128,7 +128,6 @@ CFE_SB_BufferD_t *CFE_SB_GetBufferFromPool(size_t MaxMsgSize) bd = (CFE_SB_BufferD_t *)addr; memset(bd, 0, CFE_SB_BUFFERD_CONTENT_OFFSET); - bd->MsgId = CFE_SB_INVALID_MSG_ID; bd->UseCount = 1; bd->AllocatedSize = AllocSize; diff --git a/modules/sb/fsw/src/cfe_sb_priv.c b/modules/sb/fsw/src/cfe_sb_priv.c index a48f77481..d6f424d68 100644 --- a/modules/sb/fsw/src/cfe_sb_priv.c +++ b/modules/sb/fsw/src/cfe_sb_priv.c @@ -291,10 +291,15 @@ char *CFE_SB_GetAppTskName(CFE_ES_TaskId_t TaskId, char *FullName) * See description in header file for argument/return detail * *-----------------------------------------------------------------*/ -uint32 CFE_SB_RequestToSendEvent(CFE_ES_TaskId_t TaskId, uint32 Bit) +uint32 CFE_SB_RequestToSendEvent(CFE_ES_TaskId_t TaskId, int32 Bit) { uint32 Indx; + if (Bit < 0) + { + return CFE_SB_GRANTED; + } + if (CFE_ES_TaskID_ToIndex(TaskId, &Indx) != CFE_SUCCESS) { return CFE_SB_DENIED; @@ -318,10 +323,15 @@ uint32 CFE_SB_RequestToSendEvent(CFE_ES_TaskId_t TaskId, uint32 Bit) * See description in header file for argument/return detail * *-----------------------------------------------------------------*/ -void CFE_SB_FinishSendEvent(CFE_ES_TaskId_t TaskId, uint32 Bit) +void CFE_SB_FinishSendEvent(CFE_ES_TaskId_t TaskId, int32 Bit) { uint32 Indx; + if (Bit < 0) + { + return; + } + if (CFE_ES_TaskID_ToIndex(TaskId, &Indx) != CFE_SUCCESS) { return; @@ -470,3 +480,1060 @@ int32 CFE_SB_ZeroCopyReleaseAppId(CFE_ES_AppId_t AppId) return CFE_SUCCESS; } + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +int32 CFE_SB_ZeroCopyBufferValidate(CFE_SB_Buffer_t *BufPtr, CFE_SB_BufferD_t **BufDscPtr) +{ + cpuaddr BufDscAddr; + + /* + * Calculate descriptor pointer from buffer pointer - + * The buffer is just a member (offset) in the descriptor + */ + BufDscAddr = (cpuaddr)BufPtr - offsetof(CFE_SB_BufferD_t, Content); + *BufDscPtr = (CFE_SB_BufferD_t *)BufDscAddr; + + /* + * Check that the descriptor is actually a "zero copy" type, + */ + if (!CFE_RESOURCEID_TEST_DEFINED((*BufDscPtr)->AppId)) + { + return CFE_SB_BUFFER_INVALID; + } + + /* Basic sanity check passed */ + return CFE_SUCCESS; +} + +/****************************************************************** + * + * MESSAGE TRANSACTION IMPLEMENTATION FUNCTIONS + * + ******************************************************************/ + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +void CFE_SB_MessageTxn_Init(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_PipeSetEntry_t *PipeSet, uint16 MaxPipes, + const void *RefMemPtr) +{ + memset(TxnPtr, 0, offsetof(CFE_SB_TransmitTxn_State_t, DestSet)); + TxnPtr->PipeSet = PipeSet; + TxnPtr->MaxPipes = MaxPipes; + + /* The reference pointer is kept just for (potential) reporting in events, etc. + * It is not dereferenced from here. */ + TxnPtr->RefMemPtr = RefMemPtr; +} + +/*---------------------------------------------------------------- + * + * Local Helper function + * Not invoked outside of this unit + * + *-----------------------------------------------------------------*/ +void CFE_SB_MessageTxn_SetTimeout(CFE_SB_MessageTxn_State_t *TxnPtr, int32 Timeout) +{ + TxnPtr->UserTimeoutParam = Timeout; + if (Timeout > 0) + { + /* Convert to an absolute timeout */ + TxnPtr->TimeoutMode = CFE_SB_MessageTxn_TimeoutMode_TIMED; + CFE_PSP_GetTime(&TxnPtr->AbsTimeout); + TxnPtr->AbsTimeout = OS_TimeAdd(TxnPtr->AbsTimeout, OS_TimeFromTotalMilliseconds(Timeout)); + } + else if (Timeout == CFE_SB_POLL) + { + TxnPtr->TimeoutMode = CFE_SB_MessageTxn_TimeoutMode_POLL; + } + else if (Timeout == CFE_SB_PEND_FOREVER) + { + TxnPtr->TimeoutMode = CFE_SB_MessageTxn_TimeoutMode_PEND; + } + else + { + CFE_SB_MessageTxn_SetEventAndStatus(TxnPtr, CFE_SB_RCV_BAD_ARG_EID, CFE_SB_BAD_ARGUMENT); + } +} + +/*---------------------------------------------------------------- + * + * Local Helper function + * Not invoked outside of this unit + * + *-----------------------------------------------------------------*/ +void CFE_SB_MessageTxn_GetEventDetails(const CFE_SB_MessageTxn_State_t *TxnPtr, const CFE_SB_PipeSetEntry_t *ContextPtr, + uint16 EventId, CFE_ES_TaskId_t TskId, char *EvtMsg, size_t EvtMsgSize, + CFE_EVS_EventType_Enum_t *EventType, int32 *ReqBit) +{ + char FullName[(OS_MAX_API_NAME * 2)]; + char PipeName[OS_MAX_API_NAME]; + int32 LocalOsStatus; + + if (ContextPtr == NULL) + { + LocalOsStatus = INT32_MIN; /* should not be used; do not alias any actual OS status */ + } + else + { + LocalOsStatus = ContextPtr->OsStatus; + if (ContextPtr->PendingEventId == CFE_SB_BAD_PIPEID_EID) + { + /* do not attempt to map to a pipe name if reporting a bad pipeID event - use its ID */ + snprintf(PipeName, sizeof(PipeName), "%lu", CFE_RESOURCEID_TO_ULONG(ContextPtr->PipeId)); + } + else + { + CFE_SB_GetPipeName(PipeName, sizeof(PipeName), ContextPtr->PipeId); + } + } + + switch (EventId) + { + case CFE_SB_SEND_BAD_ARG_EID: + *ReqBit = CFE_SB_SEND_BAD_ARG_EID_BIT; + *EventType = CFE_EVS_EventType_ERROR; + + snprintf(EvtMsg, EvtMsgSize, "Send Err:Bad input argument,Arg 0x%lx,App %s", + (unsigned long)TxnPtr->RefMemPtr, CFE_SB_GetAppTskName(TskId, FullName)); + break; + + case CFE_SB_SEND_INV_MSGID_EID: + *ReqBit = CFE_SB_SEND_INV_MSGID_EID_BIT; + *EventType = CFE_EVS_EventType_ERROR; + + snprintf(EvtMsg, EvtMsgSize, "Send Err:Invalid MsgId(0x%x)in msg,App %s", + (unsigned int)CFE_SB_MsgIdToValue(TxnPtr->RoutingMsgId), CFE_SB_GetAppTskName(TskId, FullName)); + break; + + case CFE_SB_MSG_TOO_BIG_EID: + *ReqBit = CFE_SB_MSG_TOO_BIG_EID_BIT; + *EventType = CFE_EVS_EventType_ERROR; + + snprintf(EvtMsg, EvtMsgSize, "Send Err:Msg Too Big MsgId=0x%x,app=%s,size=%d,MaxSz=%d", + (unsigned int)CFE_SB_MsgIdToValue(TxnPtr->RoutingMsgId), CFE_SB_GetAppTskName(TskId, FullName), + (int)TxnPtr->ContentSize, CFE_MISSION_SB_MAX_SB_MSG_SIZE); + break; + + case CFE_SB_SEND_NO_SUBS_EID: + *ReqBit = CFE_SB_SEND_NO_SUBS_EID_BIT; + *EventType = CFE_EVS_EventType_INFORMATION; + + snprintf(EvtMsg, EvtMsgSize, "No subscribers for MsgId 0x%x,sender %s", + (unsigned int)CFE_SB_MsgIdToValue(TxnPtr->RoutingMsgId), CFE_SB_GetAppTskName(TskId, FullName)); + break; + + case CFE_SB_GET_BUF_ERR_EID: + *ReqBit = CFE_SB_GET_BUF_ERR_EID_BIT; + *EventType = CFE_EVS_EventType_ERROR; + + snprintf(EvtMsg, EvtMsgSize, "Send Err:Request for Buffer Failed. MsgId 0x%x,app %s,size %d", + (unsigned int)CFE_SB_MsgIdToValue(TxnPtr->RoutingMsgId), CFE_SB_GetAppTskName(TskId, FullName), + (int)TxnPtr->ContentSize); + break; + + case CFE_SB_MSGID_LIM_ERR_EID: + *ReqBit = CFE_SB_MSGID_LIM_ERR_EID_BIT; + *EventType = CFE_EVS_EventType_ERROR; + + snprintf(EvtMsg, EvtMsgSize, "Msg Limit Err,MsgId 0x%x,pipe %s,sender %s", + (unsigned int)CFE_SB_MsgIdToValue(TxnPtr->RoutingMsgId), PipeName, + CFE_SB_GetAppTskName(TskId, FullName)); + break; + + case CFE_SB_Q_FULL_ERR_EID: + *ReqBit = CFE_SB_Q_FULL_ERR_EID_BIT; + *EventType = CFE_EVS_EventType_ERROR; + + snprintf(EvtMsg, EvtMsgSize, "Pipe Overflow,MsgId 0x%x,pipe %s,sender %s", + (unsigned int)CFE_SB_MsgIdToValue(TxnPtr->RoutingMsgId), PipeName, + CFE_SB_GetAppTskName(TskId, FullName)); + break; + + case CFE_SB_Q_WR_ERR_EID: + *ReqBit = CFE_SB_Q_WR_ERR_EID_BIT; + *EventType = CFE_EVS_EventType_ERROR; + + snprintf(EvtMsg, EvtMsgSize, "Pipe Write Err,MsgId 0x%x,pipe %s,sender %s,stat %ld", + (unsigned int)CFE_SB_MsgIdToValue(TxnPtr->RoutingMsgId), PipeName, + CFE_SB_GetAppTskName(TskId, FullName), (long)LocalOsStatus); + break; + + case CFE_SB_Q_RD_ERR_EID: + *ReqBit = -1; + *EventType = CFE_EVS_EventType_ERROR; + + snprintf(EvtMsg, EvtMsgSize, "Pipe Read Err,pipe %s,app %s,stat %ld", PipeName, + CFE_SB_GetAppTskName(TskId, FullName), (long)LocalOsStatus); + break; + case CFE_SB_RCV_BAD_ARG_EID: + *ReqBit = -1; + *EventType = CFE_EVS_EventType_ERROR; + + snprintf(EvtMsg, EvtMsgSize, "Rcv Err:Bad Input Arg:BufPtr 0x%lx,pipe %s,t/o %d,app %s", + (unsigned long)TxnPtr->RefMemPtr, PipeName, (int)TxnPtr->UserTimeoutParam, + CFE_SB_GetAppTskName(TskId, FullName)); + break; + case CFE_SB_BAD_PIPEID_EID: + *ReqBit = -1; + *EventType = CFE_EVS_EventType_ERROR; + + snprintf(EvtMsg, EvtMsgSize, "Rcv Err:PipeId %s does not exist,app %s", PipeName, + CFE_SB_GetAppTskName(TskId, FullName)); + break; + + default: + EvtMsg[0] = 0; + *ReqBit = 0; + *EventType = 0; + break; + } +} + +/*---------------------------------------------------------------- + * + * Local Helper function + * Not invoked outside of this unit + * + *-----------------------------------------------------------------*/ +bool CFE_SB_MessageTxn_ReportSingleEvent(const CFE_SB_MessageTxn_State_t *TxnPtr, + const CFE_SB_PipeSetEntry_t *ContextPtr, uint16 EventId) +{ + CFE_ES_TaskId_t TskId; + CFE_EVS_EventType_Enum_t EventType; + char Message[CFE_MISSION_EVS_MAX_MESSAGE_LENGTH]; + int32 ReqBit; + + /* get task id for events and Sender Info*/ + CFE_ES_GetTaskID(&TskId); + + CFE_SB_MessageTxn_GetEventDetails(TxnPtr, ContextPtr, EventId, TskId, Message, sizeof(Message), &EventType, + &ReqBit); + + if (EventType > 0 && CFE_SB_RequestToSendEvent(TskId, ReqBit) == CFE_SB_GRANTED) + { + CFE_EVS_SendEventWithAppID(EventId, EventType, CFE_SB_Global.AppId, "%s", Message); + + /* clear the bit so the task may send this event again */ + CFE_SB_FinishSendEvent(TskId, ReqBit); + } + + /* If the event type was an error, this should increment the general error counter */ + return (EventType >= CFE_EVS_EventType_ERROR); +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +void CFE_SB_MessageTxn_ReportEvents(const CFE_SB_MessageTxn_State_t *TxnPtr) +{ + uint32 i; + uint32 NumErrors; + bool IsError; + + NumErrors = 0; + + if (TxnPtr->TransactionEventId != 0) + { + IsError = CFE_SB_MessageTxn_ReportSingleEvent(TxnPtr, NULL, TxnPtr->TransactionEventId); + if (IsError) + { + ++NumErrors; + } + } + + for (i = 0; i < TxnPtr->NumPipes; ++i) + { + if (TxnPtr->PipeSet[i].PendingEventId != 0) + { + IsError = + CFE_SB_MessageTxn_ReportSingleEvent(TxnPtr, &TxnPtr->PipeSet[i], TxnPtr->PipeSet[i].PendingEventId); + if (IsError) + { + ++NumErrors; + } + } + } + + if (NumErrors > 0) + { + /* + * Increment the error only if there was a real error, + * such as a validation issue or failure to allocate a buffer. + * + * Note that transmit side just has one error counter, whereas + * receive side has two - these differeniate between a bad passed-in + * arg vs some other internal error such as queue access. + */ + CFE_SB_LockSharedData(__func__, __LINE__); + + if (TxnPtr->IsTransmit) + { + CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter++; + } + else if (TxnPtr->Status == CFE_SB_BAD_ARGUMENT) + { + ++CFE_SB_Global.HKTlmMsg.Payload.MsgReceiveErrorCounter; + } + else + { + /* For any other unexpected error (e.g. CFE_SB_Q_RD_ERR_EID) */ + ++CFE_SB_Global.HKTlmMsg.Payload.InternalErrorCounter; + } + + CFE_SB_UnlockSharedData(__func__, __LINE__); + } +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +void CFE_SB_MessageTxn_SetEventAndStatus(CFE_SB_MessageTxn_State_t *TxnPtr, uint16 EventId, CFE_Status_t Status) +{ + if (TxnPtr->TransactionEventId == 0) + { + TxnPtr->TransactionEventId = EventId; + } + if (TxnPtr->Status == CFE_SUCCESS) + { + TxnPtr->Status = Status; + } +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +bool CFE_SB_MessageTxn_IsOK(const CFE_SB_MessageTxn_State_t *TxnPtr) +{ + return (TxnPtr->Status == CFE_SUCCESS && TxnPtr->TransactionEventId == 0); +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +void CFE_SB_MessageTxn_SetRoutingMsgId(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_MsgId_t RoutingMsgId) +{ + /* validate the msgid in the message */ + if (!CFE_SB_IsValidMsgId(RoutingMsgId)) + { + CFE_SB_MessageTxn_SetEventAndStatus(TxnPtr, CFE_SB_SEND_INV_MSGID_EID, CFE_SB_BAD_ARGUMENT); + } + else + { + TxnPtr->RoutingMsgId = RoutingMsgId; + } +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +void CFE_SB_MessageTxn_SetContentSize(CFE_SB_MessageTxn_State_t *TxnPtr, size_t ContentSize) +{ + if (ContentSize > CFE_MISSION_SB_MAX_SB_MSG_SIZE) + { + CFE_SB_MessageTxn_SetEventAndStatus(TxnPtr, CFE_SB_MSG_TOO_BIG_EID, CFE_SB_MSG_TOO_BIG); + } + else + { + TxnPtr->ContentSize = ContentSize; + } +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +void CFE_SB_TransmitTxn_SetupFromMsg(CFE_SB_MessageTxn_State_t *TxnPtr, const CFE_MSG_Message_t *MsgPtr) +{ + CFE_Status_t Status; + CFE_MSG_Size_t MsgSize; + CFE_SB_MsgId_t MsgId; + + if (CFE_SB_MessageTxn_IsOK(TxnPtr)) + { + /* In this context, the user should have set the the size and MsgId in the content */ + Status = CFE_MSG_GetMsgId(MsgPtr, &MsgId); + if (Status == CFE_SUCCESS) + { + CFE_SB_MessageTxn_SetRoutingMsgId(TxnPtr, MsgId); + } + else + { + CFE_SB_MessageTxn_SetEventAndStatus(TxnPtr, CFE_SB_SEND_BAD_ARG_EID, Status); + } + } + + if (CFE_SB_MessageTxn_IsOK(TxnPtr)) + { + Status = CFE_MSG_GetSize(MsgPtr, &MsgSize); + if (Status == CFE_SUCCESS) + { + CFE_SB_MessageTxn_SetContentSize(TxnPtr, MsgSize); + } + else + { + CFE_SB_MessageTxn_SetEventAndStatus(TxnPtr, CFE_SB_SEND_BAD_ARG_EID, Status); + } + } +} + +/****************************************************************** + * + * TRANSMIT TRANSACTION IMPLEMENTATION FUNCTIONS + * + ******************************************************************/ + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +CFE_SB_MessageTxn_State_t *CFE_SB_TransmitTxn_Init(CFE_SB_TransmitTxn_State_t *TxnPtr, const void *RefMemPtr) +{ + CFE_SB_MessageTxn_Init(&TxnPtr->MessageTxn_State, TxnPtr->DestSet, CFE_PLATFORM_SB_MAX_DEST_PER_PKT, RefMemPtr); + TxnPtr->MessageTxn_State.IsTransmit = true; + + /* No matter what, the mem pointer from the caller should not be NULL */ + if (RefMemPtr == NULL) + { + CFE_SB_MessageTxn_SetEventAndStatus(&TxnPtr->MessageTxn_State, CFE_SB_SEND_BAD_ARG_EID, CFE_SB_BAD_ARGUMENT); + } + + return &TxnPtr->MessageTxn_State; +} + +/*---------------------------------------------------------------- + * + * Local Helper function + * Not invoked outside of this unit + * + *-----------------------------------------------------------------*/ +void CFE_SB_TransmitTxn_FindDestinations(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_BufferD_t *BufDscPtr) +{ + CFE_SB_PipeD_t * PipeDscPtr; + CFE_SB_DestinationD_t *DestPtr; + CFE_SB_PipeSetEntry_t *ContextPtr; + CFE_ES_AppId_t AppId; + bool IsAcceptable; + CFE_Status_t Status; + + /* + * get app id for loopback testing - + * This is only used if one or more of the destinations has its "IGNOREMINE" option set, + * but it should NOT be gotten while locked. So since we do not know (yet) if we need it, + * it is better to get it and not need it than need it and not have it. + */ + CFE_ES_GetAppID(&AppId); + + /* take semaphore to prevent a task switch during processing */ + CFE_SB_LockSharedData(__func__, __LINE__); + + /* Get the routing id */ + BufDscPtr->DestRouteId = CFE_SBR_GetRouteId(TxnPtr->RoutingMsgId); + + /* For an invalid route / no subscribers this whole logic can be skipped */ + if (CFE_SBR_IsValidRouteId(BufDscPtr->DestRouteId)) + { + /* If this is the origination, then update the message content (while locked) before actually sending */ + if (TxnPtr->IsEndpoint) + { + CFE_SBR_IncrementSequenceCounter(BufDscPtr->DestRouteId); + + /* Set the sequence count from the route */ + Status = + CFE_MSG_SetSequenceCount(&BufDscPtr->Content.Msg, CFE_SBR_GetSequenceCounter(BufDscPtr->DestRouteId)); + } + + /* Send the packet to all destinations */ + DestPtr = CFE_SBR_GetDestListHeadPtr(BufDscPtr->DestRouteId); + while (DestPtr != NULL && TxnPtr->NumPipes < TxnPtr->MaxPipes) + { + ContextPtr = NULL; + + if (DestPtr->Active == CFE_SB_ACTIVE) /* destination is active */ + { + PipeDscPtr = CFE_SB_LocatePipeDescByID(DestPtr->PipeId); + } + else + { + PipeDscPtr = NULL; + } + + if (CFE_SB_PipeDescIsMatch(PipeDscPtr, DestPtr->PipeId)) + { + if ((PipeDscPtr->Opts & CFE_SB_PIPEOPTS_IGNOREMINE) == 0 || + !CFE_RESOURCEID_TEST_EQUAL(PipeDscPtr->AppId, AppId)) + { + ContextPtr = &TxnPtr->PipeSet[TxnPtr->NumPipes]; + ++TxnPtr->NumPipes; + } + } + + if (ContextPtr != NULL) + { + memset(ContextPtr, 0, sizeof(*ContextPtr)); + + ContextPtr->PipeId = DestPtr->PipeId; + ContextPtr->SysQueueId = PipeDscPtr->SysQueueId; + + /* if Msg limit exceeded, log event, increment counter */ + /* and go to next destination */ + if (DestPtr->BuffCount >= DestPtr->MsgId2PipeLim) + { + ContextPtr->PendingEventId = CFE_SB_MSGID_LIM_ERR_EID; + ++CFE_SB_Global.HKTlmMsg.Payload.MsgLimitErrorCounter; + ++PipeDscPtr->SendErrors; + ++TxnPtr->NumPipeErrs; + } + else + { + CFE_SB_IncrBufUseCnt(BufDscPtr); + ++DestPtr->BuffCount; + + ++PipeDscPtr->CurrentQueueDepth; + if (PipeDscPtr->CurrentQueueDepth > PipeDscPtr->PeakQueueDepth) + { + PipeDscPtr->PeakQueueDepth = PipeDscPtr->CurrentQueueDepth; + } + } + } + + DestPtr = DestPtr->Next; + } + } + else + { + /* if there have been no subscriptions for this pkt, */ + /* increment the dropped pkt cnt, send event and return success */ + CFE_SB_Global.HKTlmMsg.Payload.NoSubscribersCounter++; + CFE_SB_MessageTxn_SetEventAndStatus(TxnPtr, CFE_SB_SEND_NO_SUBS_EID, CFE_SUCCESS); + } + + /* + * Remove this from whatever list it was in + * + * If it was a singleton/new buffer this has no effect. + * If it was a zero-copy buffer this removes it from the ZeroCopyList. + */ + CFE_SB_TrackingListRemove(&BufDscPtr->Link); + + /* clear the AppID field in case it was a zero copy buffer, + * as it is no longer owned by that app after broadcasting */ + BufDscPtr->AppId = CFE_ES_APPID_UNDEFINED; + + /* track the buffer as an in-transit message */ + CFE_SB_TrackingListAdd(&CFE_SB_Global.InTransitList, &BufDscPtr->Link); + + CFE_SB_UnlockSharedData(__func__, __LINE__); + + /* + * Lastly, if this is the origination point, now that all headers should + * have known values (including sequence) - invoke the mission-specific + * message origination action. This may update timestamps and/or compute + * any required error control fields. + */ + if (CFE_SB_MessageTxn_IsOK(TxnPtr) && TxnPtr->IsEndpoint) + { + /* Update any other system-specific MSG headers based on the current sequence */ + Status = CFE_MSG_OriginationAction(&BufDscPtr->Content.Msg, BufDscPtr->AllocatedSize, &IsAcceptable); + if (Status != CFE_SUCCESS || !IsAcceptable) + { + CFE_SB_MessageTxn_SetEventAndStatus(TxnPtr, CFE_SB_SEND_MESSAGE_INTEGRITY_FAIL_EID, Status); + } + } +} + +/*---------------------------------------------------------------- + * + * Local Helper function + * Not invoked outside of this unit + * + *-----------------------------------------------------------------*/ +int32 CFE_SB_MessageTxn_GetOsTimeout(const CFE_SB_MessageTxn_State_t *TxnPtr) +{ + int32 OsTimeout; + OS_time_t TimeNow; + + switch (TxnPtr->TimeoutMode) + { + case CFE_SB_MessageTxn_TimeoutMode_PEND: + OsTimeout = OS_PEND; + break; + case CFE_SB_MessageTxn_TimeoutMode_TIMED: + CFE_PSP_GetTime(&TimeNow); + OsTimeout = OS_TimeGetTotalMilliseconds(OS_TimeSubtract(TxnPtr->AbsTimeout, TimeNow)); + if (OsTimeout < 0) + { + /* timeout has already expired, so all remaining pipes should be CHECK only */ + OsTimeout = OS_CHECK; + } + break; + case CFE_SB_MessageTxn_TimeoutMode_POLL: + default: + OsTimeout = OS_CHECK; + break; + } + + return OsTimeout; +} + +/*---------------------------------------------------------------- + * + * Local Helper function + * Not invoked outside of this unit + * + *-----------------------------------------------------------------*/ +bool CFE_SB_TransmitTxn_PipeHandler(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_PipeSetEntry_t *ContextPtr, void *Arg) +{ + CFE_SB_DestinationD_t *DestPtr; + CFE_SB_PipeD_t * PipeDscPtr; + CFE_SB_BufferD_t * BufDscPtr; + + BufDscPtr = Arg; + + /* + * Write the buffer descriptor to the queue of the pipe. Note that + * accounting for depth and buffer limits was already done as part + * of "FindDestinations" assuming this write will be successful - which + * is the expected/typical result here. + */ + ContextPtr->OsStatus = + OS_QueuePut(ContextPtr->SysQueueId, &BufDscPtr, sizeof(BufDscPtr), CFE_SB_MessageTxn_GetOsTimeout(TxnPtr)); + + /* + * If it succeeded, nothing else to do. But if it fails then we must undo the + * optimistic depth accounting done earlier. + */ + if (ContextPtr->OsStatus != OS_SUCCESS) + { + ++TxnPtr->NumPipeErrs; + + CFE_SB_LockSharedData(__func__, __LINE__); + + if (ContextPtr->OsStatus == OS_QUEUE_FULL) + { + ContextPtr->PendingEventId = CFE_SB_Q_FULL_ERR_EID; + CFE_SB_Global.HKTlmMsg.Payload.PipeOverflowErrorCounter++; + } + else + { + /* Unexpected error while writing to queue. */ + ContextPtr->PendingEventId = CFE_SB_Q_WR_ERR_EID; + CFE_SB_Global.HKTlmMsg.Payload.InternalErrorCounter++; + } + + PipeDscPtr = CFE_SB_LocatePipeDescByID(ContextPtr->PipeId); + if (CFE_SB_PipeDescIsMatch(PipeDscPtr, ContextPtr->PipeId) && PipeDscPtr->CurrentQueueDepth > 0) + { + --PipeDscPtr->CurrentQueueDepth; + } + + DestPtr = CFE_SB_GetDestPtr(BufDscPtr->DestRouteId, ContextPtr->PipeId); + if (DestPtr != NULL && DestPtr->BuffCount > 0) + { + DestPtr->BuffCount--; + } + + CFE_SB_DecrBufUseCnt(BufDscPtr); + + CFE_SB_UnlockSharedData(__func__, __LINE__); + } + + /* always keep going when sending (broadcast) */ + return true; +} + +/*---------------------------------------------------------------- + * + * Local Helper function + * Not invoked outside of this unit + * + *-----------------------------------------------------------------*/ +void CFE_SB_MessageTxn_ProcessPipes(CFE_SB_MessageTxn_PipeHandler_t HandlerFunc, CFE_SB_MessageTxn_State_t *TxnPtr, + void *Arg) +{ + uint32 i; + CFE_SB_PipeSetEntry_t *ContextPtr; + bool should_continue; + + should_continue = true; + for (i = 0; should_continue && i < TxnPtr->NumPipes; ++i) + { + ContextPtr = &TxnPtr->PipeSet[i]; + + if (ContextPtr->PendingEventId == 0) + { + should_continue = HandlerFunc(TxnPtr, ContextPtr, Arg); + } + } +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +void CFE_SB_TransmitTxn_Execute(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_Buffer_t *BufPtr) +{ + int32 Status; + CFE_SB_BufferD_t *BufDscPtr; + + /* Sanity check on the input buffer - if this doesn't work, stop now */ + Status = CFE_SB_ZeroCopyBufferValidate(BufPtr, &BufDscPtr); + if (Status != CFE_SUCCESS) + { + /* There is currently no event defined for this */ + CFE_SB_MessageTxn_SetEventAndStatus(TxnPtr, 0, Status); + return; + } + + /* Save passed-in routing parameters into the descriptor */ + BufDscPtr->ContentSize = CFE_SB_MessageTxn_GetContentSize(TxnPtr); + BufDscPtr->MsgId = CFE_SB_MessageTxn_GetRoutingMsgId(TxnPtr); + + /* Convert the route to a set of pipes/destinations */ + CFE_SB_TransmitTxn_FindDestinations(TxnPtr, BufDscPtr); + + /* Note the above function always succeeds - even if no pipes are subscribed, + * the transaction will simply have 0 pipes and this next call becomes a no-op */ + CFE_SB_MessageTxn_ProcessPipes(CFE_SB_TransmitTxn_PipeHandler, TxnPtr, BufDscPtr); + + /* + * Decrement the buffer UseCount - This means that the caller + * should not use the buffer anymore after this call. + */ + CFE_SB_LockSharedData(__func__, __LINE__); + CFE_SB_DecrBufUseCnt(BufDscPtr); + CFE_SB_UnlockSharedData(__func__, __LINE__); +} + +/****************************************************************** + * + * RECEIVE TRANSACTION IMPLEMENTATION FUNCTIONS + * + ******************************************************************/ + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +CFE_SB_MessageTxn_State_t *CFE_SB_ReceiveTxn_Init(CFE_SB_ReceiveTxn_State_t *TxnPtr, const void *RefMemPtr) +{ + CFE_SB_MessageTxn_Init(&TxnPtr->MessageTxn_State, &TxnPtr->Source, 1, RefMemPtr); + TxnPtr->MessageTxn_State.IsTransmit = false; + + /* No matter what, the mem pointer from the caller should not be NULL */ + /* note that the event ID is different between send and recv */ + if (RefMemPtr == NULL) + { + CFE_SB_MessageTxn_SetEventAndStatus(&TxnPtr->MessageTxn_State, CFE_SB_RCV_BAD_ARG_EID, CFE_SB_BAD_ARGUMENT); + } + + return &TxnPtr->MessageTxn_State; +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +void CFE_SB_MessageTxn_SetEndpoint(CFE_SB_MessageTxn_State_t *TxnPtr, bool IsEndpoint) +{ + TxnPtr->IsEndpoint = IsEndpoint; +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +void CFE_SB_ReceiveTxn_SetPipeId(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_PipeId_t PipeId) +{ + CFE_SB_PipeD_t * PipeDscPtr; + CFE_SB_PipeSetEntry_t *ContextPtr; + + /* For now, there is just one of these */ + ContextPtr = TxnPtr->PipeSet; + memset(ContextPtr, 0, sizeof(*ContextPtr)); + ContextPtr->PipeId = PipeId; + TxnPtr->NumPipes = 1; + + PipeDscPtr = CFE_SB_LocatePipeDescByID(PipeId); + + CFE_SB_LockSharedData(__func__, __LINE__); + + /* If the pipe does not exist or PipeId is out of range... */ + if (!CFE_SB_PipeDescIsMatch(PipeDscPtr, PipeId)) + { + ContextPtr->PendingEventId = CFE_SB_BAD_PIPEID_EID; + CFE_SB_MessageTxn_SetEventAndStatus(TxnPtr, 0, CFE_SB_BAD_ARGUMENT); + ++TxnPtr->NumPipeErrs; + } + else + { + ContextPtr->SysQueueId = PipeDscPtr->SysQueueId; + + /* + * Un-reference any previous buffer from the last call. + * + * NOTE: This is historical behavior where apps call CFE_SB_ReceiveBuffer() + * in the loop within the app's main task. There is currently no separate + * API to "free" or unreference a buffer that was returned from SB. + * + * Instead, each time this function is invoked, it is implicitly interpreted + * as an indication that the caller is done with the previous buffer. + * + * Unfortunately this prevents pipe IDs from being serviced/shared across + * multiple child tasks in a worker pattern design. This may be changed + * in a future version of CFE to decouple these actions, to allow for + * multiple workers to service the same pipe. + */ + if (PipeDscPtr->LastBuffer != NULL) + { + /* Decrement the Buffer Use Count, which will Free buffer if it becomes 0 */ + CFE_SB_DecrBufUseCnt(PipeDscPtr->LastBuffer); + PipeDscPtr->LastBuffer = NULL; + } + } + + CFE_SB_UnlockSharedData(__func__, __LINE__); +} + +/*---------------------------------------------------------------- + * + * Local Helper function + * Not invoked outside of this unit + * + *-----------------------------------------------------------------*/ +void CFE_SB_ReceiveTxn_ExportReference(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_PipeSetEntry_t *ContextPtr, + CFE_SB_BufferD_t *BufDscPtr, CFE_SB_BufferD_t **ParentBufDscPtrP) +{ + CFE_SB_PipeD_t * PipeDscPtr; + CFE_SB_DestinationD_t *DestPtr; + + PipeDscPtr = CFE_SB_LocatePipeDescByID(ContextPtr->PipeId); + + /* Now re-lock to store the buffer in the pipe descriptor */ + CFE_SB_LockSharedData(__func__, __LINE__); + + /* + * NOTE: This uses the same PipeDscPtr that was found earlier. + * But it has to be revalidated because its theoretically possible + * the pipe got deleted between now and then. + */ + if (CFE_SB_PipeDescIsMatch(PipeDscPtr, ContextPtr->PipeId)) + { + /* + ** Load the pipe tables 'CurrentBuff' with the buffer descriptor + ** ptr corresponding to the message just read. This is done so that + ** the buffer can be released on the next receive call for this pipe. + ** + ** This counts as a new reference as it is being stored in the PipeDsc + */ + CFE_SB_IncrBufUseCnt(BufDscPtr); + PipeDscPtr->LastBuffer = BufDscPtr; + + /* + * Also set the Receivers pointer to the address of the actual message + * (currently this is "borrowing" the ref above, not its own ref) + */ + *ParentBufDscPtrP = BufDscPtr; + + /* get pointer to destination to be used in decrementing msg limit cnt*/ + DestPtr = CFE_SB_GetDestPtr(BufDscPtr->DestRouteId, ContextPtr->PipeId); + + /* + ** DestPtr would be NULL if the msg is unsubscribed to while it is on + ** the pipe. The BuffCount may be zero if the msg is unsubscribed to and + ** then resubscribed to while it is on the pipe. Both of these cases are + ** considered nominal and are handled by the code below. + */ + if (DestPtr != NULL && DestPtr->BuffCount > 0) + { + DestPtr->BuffCount--; + } + + if (PipeDscPtr->CurrentQueueDepth > 0) + { + --PipeDscPtr->CurrentQueueDepth; + } + } + else + { + CFE_SB_MessageTxn_SetEventAndStatus(TxnPtr, 0, CFE_SB_PIPE_RD_ERR); + + /* should send the bad pipe ID event here too */ + ContextPtr->PendingEventId = CFE_SB_BAD_PIPEID_EID; + } + + /* Always decrement the use count, for the ref that was in the queue */ + CFE_SB_DecrBufUseCnt(BufDscPtr); + + CFE_SB_UnlockSharedData(__func__, __LINE__); +} + +/*---------------------------------------------------------------- + * + * Local Helper function + * Not invoked outside of this unit + * + *-----------------------------------------------------------------*/ +bool CFE_SB_ReceiveTxn_PipeHandler(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_PipeSetEntry_t *ContextPtr, void *Arg) +{ + CFE_SB_BufferD_t * BufDscPtr; + CFE_SB_BufferD_t **ParentBufDscPtrP; + size_t BufDscSize; + + ParentBufDscPtrP = Arg; + + /* Read the buffer descriptor address from the queue. */ + ContextPtr->OsStatus = OS_QueueGet(ContextPtr->SysQueueId, &BufDscPtr, sizeof(BufDscPtr), &BufDscSize, + CFE_SB_MessageTxn_GetOsTimeout(TxnPtr)); + + /* + * translate the return value - + * + * CFE functions have their own set of RC values should not directly return OSAL codes + * The size should always match. If it does not, then generate CFE_SB_Q_RD_ERR_EID. + */ + + if (ContextPtr->OsStatus == OS_SUCCESS && BufDscPtr != NULL && BufDscSize == sizeof(BufDscPtr)) + { + CFE_SB_ReceiveTxn_ExportReference(TxnPtr, ContextPtr, BufDscPtr, ParentBufDscPtrP); + } + else + { + *ParentBufDscPtrP = NULL; + + if (ContextPtr->OsStatus == OS_QUEUE_EMPTY) + { + /* normal if using CFE_SB_POLL */ + CFE_SB_MessageTxn_SetEventAndStatus(TxnPtr, 0, CFE_SB_NO_MESSAGE); + } + else if (ContextPtr->OsStatus == OS_QUEUE_TIMEOUT) + { + /* normal if using a nonzero timeout */ + CFE_SB_MessageTxn_SetEventAndStatus(TxnPtr, 0, CFE_SB_TIME_OUT); + } + else + { + /* off-nominal condition, report an error event */ + CFE_SB_MessageTxn_SetEventAndStatus(TxnPtr, 0, CFE_SB_PIPE_RD_ERR); + ContextPtr->PendingEventId = CFE_SB_Q_RD_ERR_EID; + } + } + + /* Read ops only process one pipe */ + return false; +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +const CFE_SB_Buffer_t *CFE_SB_ReceiveTxn_Execute(CFE_SB_MessageTxn_State_t *TxnPtr) +{ + CFE_SB_BufferD_t * BufDscPtr; + const CFE_SB_Buffer_t *Result; + bool IsAcceptable; + CFE_Status_t Status; + + Result = NULL; + + while (CFE_SB_MessageTxn_IsOK(TxnPtr)) + { + BufDscPtr = NULL; + + /* + * Read from the pipe(s). Currently this is just one but someday could + * become more than one e.g. a high-pri and a low-pri queue. + */ + CFE_SB_MessageTxn_ProcessPipes(CFE_SB_ReceiveTxn_PipeHandler, TxnPtr, &BufDscPtr); + + /* If nothing received, then quit */ + if (BufDscPtr == NULL) + { + TxnPtr->RoutingMsgId = CFE_SB_INVALID_MSG_ID; + TxnPtr->ContentSize = 0; + Result = NULL; + break; + } + + if (TxnPtr->IsEndpoint) + { + Status = CFE_MSG_VerificationAction(&BufDscPtr->Content.Msg, BufDscPtr->AllocatedSize, &IsAcceptable); + if (Status != CFE_SUCCESS) + { + /* This typically should not happen - only if VerificationAction got bad arguments */ + IsAcceptable = false; + } + } + else + { + /* If no verification being done at this stage - consider everything "good" */ + IsAcceptable = true; + } + + if (IsAcceptable) + { + /* + * Replicate the buffer descriptor MsgId and ContentSize in the transaction. + * + * This does not use the functions that check the values because that will + * send an event and set an error status if they are bad. But on the recv side, + * it is already here - whatever we got should be returned. Error checking for + * size and MsgId should have been done on the transmit side, so they should never + * be bad at this point. + */ + TxnPtr->RoutingMsgId = BufDscPtr->MsgId; + TxnPtr->ContentSize = BufDscPtr->ContentSize; + Result = &BufDscPtr->Content; + break; + } + + /* Report an event indicating the buffer is being dropped */ + CFE_SB_MessageTxn_ReportSingleEvent(TxnPtr, TxnPtr->PipeSet, CFE_SB_RCV_MESSAGE_INTEGRITY_FAIL_EID); + + /* + * Also need to re-set the PipeId for proper accounting. This buffer will be dropped, + * and this decrements the use count and removes it from the LastBuffer pointer in the + * Pipe Descriptor + */ + CFE_SB_ReceiveTxn_SetPipeId(TxnPtr, TxnPtr->PipeSet->PipeId); + } + + return Result; +} diff --git a/modules/sb/fsw/src/cfe_sb_priv.h b/modules/sb/fsw/src/cfe_sb_priv.h index 737682936..dc172d7ca 100644 --- a/modules/sb/fsw/src/cfe_sb_priv.h +++ b/modules/sb/fsw/src/cfe_sb_priv.h @@ -124,16 +124,18 @@ typedef struct CFE_SB_BufferD CFE_SB_BufferLink_t Link; /**< Links for inclusion in the tracking lists */ /** - * Actual MsgId of the content, cached here to avoid repeat - * calls into CFE_MSG API during traversal/delivery of the message. + * MsgId that should be used for routing/delivering the message. Traditionally, + * this is the same as the MsgId inside the content, but cached for easier access. + * However, the user may also choose to route messages explicitly and thus this + * message ID could differ from the one inside the message content. * - * MsgId is set for buffers which contain actual data in transit. AppId is unset - * while in transit, as it may be sent to multiple apps. + * MsgId is set for buffers in transit. AppId is unset while in transit, as it + * may be sent to multiple apps. * * During zero copy buffer initial allocation, the MsgId is not known at this time * and should be set to the invalid msg ID. */ - CFE_SB_MsgId_t MsgId; + CFE_SBR_RouteId_t DestRouteId; /** * Current owner of the buffer, if owned by a single app. @@ -146,11 +148,10 @@ typedef struct CFE_SB_BufferD */ CFE_ES_AppId_t AppId; - size_t AllocatedSize; /**< Total size of this descriptor (including descriptor itself) */ - size_t ContentSize; /**< Actual size of message content currently stored in the buffer */ - CFE_MSG_Type_t ContentType; /**< Type of message content currently stored in the buffer */ + CFE_SB_MsgId_t MsgId; - bool NeedsUpdate; /**< If message should get its header fields automatically updated */ + size_t AllocatedSize; /**< Total size of this descriptor (including descriptor itself) */ + size_t ContentSize; /**< Actual size of message content currently stored in the buffer */ uint16 UseCount; /**< Number of active references to this buffer in the system */ @@ -168,10 +169,10 @@ typedef struct CFE_SB_BufferD typedef struct { CFE_SB_PipeId_t PipeId; - uint8 Opts; - uint8 Spare; CFE_ES_AppId_t AppId; osal_id_t SysQueueId; + uint8 Opts; + uint8 Spare; uint16 SendErrors; uint16 MaxQueueDepth; uint16 CurrentQueueDepth; @@ -259,29 +260,82 @@ typedef struct } CFE_SB_Global_t; /****************************************************************************** -** Typedef: CFE_SB_SendErrEventBuf_t -** -** Purpose: -** This structure is used to store event information during a send. -*/ + * Typedef: CFE_SB_PipeSetEntry_t + */ typedef struct { - uint32 EventId; - int32 OsStatus; CFE_SB_PipeId_t PipeId; -} CFE_SB_SendErrEventBuf_t; + osal_id_t SysQueueId; + uint16 PendingEventId; + int32 OsStatus; +} CFE_SB_PipeSetEntry_t; -/****************************************************************************** -** Typedef: CFE_SB_EventBuf_t -** -** Purpose: -** This structure is used to store event information during a send. -*/ +typedef enum +{ + CFE_SB_MessageTxn_TimeoutMode_POLL, /**< Transaction should not block */ + CFE_SB_MessageTxn_TimeoutMode_PEND, /**< Transaction should block indefinitely */ + CFE_SB_MessageTxn_TimeoutMode_TIMED /**< Transaction should block for a limited amount of time */ +} CFE_SB_MessageTxn_TimeoutMode_t; + +/** + * \brief Tracks the status of a message/buffer transmit transaction + * + * This includes the list of destinations to send to, and the status of each, + * as well as any pending event IDs from the transaction setup. + * + */ typedef struct { - uint32 EvtsToSnd; - CFE_SB_SendErrEventBuf_t EvtBuf[CFE_PLATFORM_SB_MAX_DEST_PER_PKT]; -} CFE_SB_EventBuf_t; + bool IsTransmit; + bool IsEndpoint; + + uint16 NumPipes; + uint16 MaxPipes; + uint16 NumPipeErrs; + uint16 TransactionEventId; + + CFE_SB_MessageTxn_TimeoutMode_t TimeoutMode; + int32_t UserTimeoutParam; + OS_time_t AbsTimeout; + + CFE_Status_t Status; + size_t ContentSize; + CFE_SB_MsgId_t RoutingMsgId; + + const void *RefMemPtr; + + CFE_SB_PipeSetEntry_t *PipeSet; +} CFE_SB_MessageTxn_State_t; + +/** + * \brief Tracks the status of a message/buffer transmit transaction + * + * This includes the list of destinations to send to, and the status of each, + * as well as any pending event IDs from the transaction setup. + * + */ +typedef struct +{ + CFE_SB_MessageTxn_State_t MessageTxn_State; + + CFE_SB_PipeSetEntry_t DestSet[CFE_PLATFORM_SB_MAX_DEST_PER_PKT]; +} CFE_SB_TransmitTxn_State_t; + +/** + * \brief Tracks the status of a message/buffer transmit transaction + * + * This includes the list of destinations to send to, and the status of each, + * as well as any pending event IDs from the transaction setup. + * + */ +typedef struct +{ + CFE_SB_MessageTxn_State_t MessageTxn_State; + + CFE_SB_PipeSetEntry_t Source; +} CFE_SB_ReceiveTxn_State_t; + +typedef bool (*CFE_SB_MessageTxn_PipeHandler_t)(CFE_SB_MessageTxn_State_t *, CFE_SB_PipeSetEntry_t *, void *); /* ** Software Bus Function Prototypes @@ -410,20 +464,6 @@ int32 CFE_SB_UnsubscribeWithAppId(CFE_SB_MsgId_t MsgId, CFE_SB_PipeId_t PipeId, */ int32 CFE_SB_UnsubscribeFull(CFE_SB_MsgId_t MsgId, CFE_SB_PipeId_t PipeId, uint8 Scope, CFE_ES_AppId_t AppId); -/*---------------------------------------------------------------------------------------*/ -/** - * \brief Internal routine to validate a transmit message before sending - * - * \param[in] MsgPtr Pointer to the message to validate - * \param[out] MsgIdPtr Message Id of message - * \param[out] SizePtr Size of message - * \param[out] RouteIdPtr Route ID of the message (invalid if none) - * - * \return Execution status, see \ref CFEReturnCodes - */ -int32 CFE_SB_TransmitMsgValidate(const CFE_MSG_Message_t *MsgPtr, CFE_SB_MsgId_t *MsgIdPtr, CFE_MSG_Size_t *SizePtr, - CFE_SBR_RouteId_t *RouteIdPtr); - /*---------------------------------------------------------------------------------------*/ /** * Release all zero-copy buffers associated with the given app ID. @@ -513,7 +553,7 @@ int32 CFE_SB_SendSubscriptionReport(CFE_SB_MsgId_t MsgId, CFE_SB_PipeId_t PipeId * * @returns grant/deny status */ -uint32 CFE_SB_RequestToSendEvent(CFE_ES_TaskId_t TaskId, uint32 Bit); +uint32 CFE_SB_RequestToSendEvent(CFE_ES_TaskId_t TaskId, int32 Bit); /*---------------------------------------------------------------------------------------*/ /** @@ -521,7 +561,7 @@ uint32 CFE_SB_RequestToSendEvent(CFE_ES_TaskId_t TaskId, uint32 Bit); * * This should be called after a successful CFE_SB_RequestToSendEvent() */ -void CFE_SB_FinishSendEvent(CFE_ES_TaskId_t TaskId, uint32 Bit); +void CFE_SB_FinishSendEvent(CFE_ES_TaskId_t TaskId, int32 Bit); /*---------------------------------------------------------------------------------------*/ /** @@ -619,41 +659,6 @@ CFE_SB_BufferD_t *CFE_SB_GetBufferFromPool(size_t MaxMsgSize); */ void CFE_SB_ReturnBufferToPool(CFE_SB_BufferD_t *bd); -/*---------------------------------------------------------------------------------------*/ -/** - * \brief Broadcast a SB buffer descriptor to all destinations in route - * - * Internal routine that implements the logic of transmitting a message buffer - * to all destinations subscribed in the SB route. - * - * As this function will broadcast the message to any number of destinations (0-many), - * and some may be successful and some may fail, the status cannot be expressed - * in any single error code, so this does not return any status. - * - * Instead, this routine handles all potential outcomes on its own, and does - * not expect the caller to handle any delivery issues. Also note that the general - * design pattern of the software bus is a "send and forget" model where the sender does - * not know (or care) what entities are subscribed to the data being generated. - * - * - For any undeliverable destination (limit, OSAL error, etc), a proper event is generated. - * - For any successful queueing, the buffer use count is incremented - * - * The caller is expected to hold a reference (use count) of the buffer prior to invoking - * this routine, representing itself, which is then consumed by this routine. - * - * \note _This call will "consume" the buffer by decrementing the buffer use count_ after - * broadcasting the message to all subscribed pipes. - * - * The caller should not access the buffer again after calling this function, as it may - * be deallocated at any time. If the caller wishes to continue accessing the buffer, - * it should explicitly increment the use count before calling this, which will prevent - * deallocation. - * - * \param[in] BufDscPtr Pointer to the buffer descriptor to broadcast - * \param[in] RouteId Route to send to - */ -void CFE_SB_BroadcastBufferToRoute(CFE_SB_BufferD_t *BufDscPtr, CFE_SBR_RouteId_t RouteId); - /*---------------------------------------------------------------------------------------*/ /** * \brief Perform basic sanity check on the Zero Copy handle @@ -745,6 +750,436 @@ CFE_SB_DestinationD_t *CFE_SB_GetDestPtr(CFE_SBR_RouteId_t RouteId, CFE_SB_PipeI **/ size_t CFE_SB_MsgHdrSize(const CFE_MSG_Message_t *MsgPtr); +/* + * Message Transmit/Receive Transaction implementation functions + */ + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Set up transaction initial state + * + * Helper function to initialize the common parts of a transmit/receive transaction + * + * \param[out] TxnPtr Transaction object to initialize + * \param[inout] PipeSet Pointer to pipe entry buffer + * \param[in] MaxPipes Number of entries in PipeSet + * \param[in] RefMemPtr User-supplied Opaque object pointer for event logging + */ +void CFE_SB_MessageTxn_Init(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_PipeSetEntry_t *PipeSet, uint16 MaxPipes, + const void *RefMemPtr); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Check transaction current state + * + * Checks if the transaction is in a good state. + * + * \param[in] TxnPtr Transaction object to check + * \returns Boolean indicating transaction condition + * \retval true if the transaction is OK + * \retval false if the transaction has an error + */ +bool CFE_SB_MessageTxn_IsOK(const CFE_SB_MessageTxn_State_t *TxnPtr); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Get transaction content size + * + * Obtains the size of the object/message being passed in this transaction + * + * \param[in] TxnPtr Transaction object + * \returns Size of message/object being passed + */ +static inline size_t CFE_SB_MessageTxn_GetContentSize(const CFE_SB_MessageTxn_State_t *TxnPtr) +{ + return TxnPtr->ContentSize; +} + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Get transaction MsgId used for routing + * + * Obtains the MsgId used for routing of the object/message being passed in this transaction + * + * \note This MsgId may be different than the one embedded within the message + * + * \param[in] TxnPtr Transaction object + * \returns MsgId used for routing of message/object being passed + */ +static inline CFE_SB_MsgId_t CFE_SB_MessageTxn_GetRoutingMsgId(CFE_SB_MessageTxn_State_t *TxnPtr) +{ + return TxnPtr->RoutingMsgId; +} + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Get transaction status code + * + * Obtains the CFE status code associated with the transaction + * + * \param[in] TxnPtr Transaction object + * \returns CFE Status code + */ +static inline CFE_Status_t CFE_SB_MessageTxn_GetStatus(CFE_SB_MessageTxn_State_t *TxnPtr) +{ + return TxnPtr->Status; +} + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Set transaction status code and event + * + * If an off-nominal condition occurs, this routine sets the specified status and/or event code + * that should be returned to the caller to indicate that condition. + * + * A value of 0 (which is an alias to #CFE_SUCCESS for status codes) can be passed for either + * parameter if there is no defined code for the particular condition. + * + * The transaction object stores the first non-zero value for event ID or status code. + * + * \param[inout] TxnPtr Transaction object + * \param[in] EventId The event ID to set + * \param[in] Status The status code to set + */ +void CFE_SB_MessageTxn_SetEventAndStatus(CFE_SB_MessageTxn_State_t *TxnPtr, uint16 EventId, CFE_Status_t Status); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Set transaction MsgId used for routing + * + * Configures the MsgId used for routing of the object/message being passed in this transaction + * + * \note This MsgId may be different than the one embedded within the message + * + * \param[inout] TxnPtr Transaction object + * \param[in] RoutingMsgId MsgId used for routing of message/object being passed + */ +void CFE_SB_MessageTxn_SetRoutingMsgId(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_MsgId_t RoutingMsgId); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Set transaction content size + * + * Configures the size of the object/message being passed in this transaction + * + * \param[inout] TxnPtr Transaction object + * \param[in] ContentSize Size of message/object being passed + */ +void CFE_SB_MessageTxn_SetContentSize(CFE_SB_MessageTxn_State_t *TxnPtr, size_t ContentSize); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Configure transaction time out + * + * Configures the relative timeout (in milliseconds) for this transaction. This is based + * on sampling the PSP clock at the time this function is invoked, and adding the relative + * time to it. + * + * This should be called early in the transaction configuration process. + * + * \param[inout] TxnPtr Transaction object + * \param[in] Timeout Timeout for message/object transaction + */ +void CFE_SB_MessageTxn_SetTimeout(CFE_SB_MessageTxn_State_t *TxnPtr, int32 Timeout); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Configure message endpoint (origination/termination) processing + * + * For a transmit transaction this enables origination processing. If configured as + * true, then the CFE_MSG_OriginationAction() function is invoked prior to sending the message. + * The specific action(s) depend on the MSG module implementation, and may include: + * - Updating the sequence number + * - Updating the message timestamp + * - Calculating/Appending any applicable CRC, checksum, or other error control field + * - Any other system-specific message header updates + * + * For a receive transaction, this enables message verification. If configured as + * true, then the CFE_MSG_VerificationAction() function will be invoked before the message buffer + * is returned to the caller. The verification criteria depends on the MSG implementation, but + * is typically expected to be the inverse of the origination processing. That is, items that + * were calculated or updated as part of the origination process can be verified during this step. + * If verification fails, then an event will be logged, the message will be dropped, and the + * implementation will get the next message in the queue (if any). + * + * \note The receive verification function in the default MSG implementation always returns + * success, thus should be backward compatible with previous CFE versions that did not perform + * any verification at the SB level, leaving it up to the individual app to check. To make use + * of this feature, the implementer must also provide an alternate implemenation of + * CFE_MSG_VerificationAction() that performs the desired checks for their particular use-case. + * + * \sa CFE_MSG_OriginationAction() + * \sa CFE_MSG_VerificationAction() + * + * \param[inout] TxnPtr Transaction object + * \param[in] IsEndpoint Pass as "true" to enable endpoint (origination/termination) message processing + */ +void CFE_SB_MessageTxn_SetEndpoint(CFE_SB_MessageTxn_State_t *TxnPtr, bool IsEndpoint); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Helper function to get the OSAL timeout to use + * + * Returns the relative timeout (in milliseconds) for any pending queue operation(s). + * This reflects the amount of time left from the initial call to CFE_SB_MessageTxn_SetTimeout(). + * + * If the original timeout was to PEND, this always returns -1 (OS_PEND). + * If the original timeout was to POLL, this always returns 0 (OS_CHECK). + * If the original timeout was some nonzero value, this returns the time left, in milliseconds, + * based on the current time as obtained from the PSP. + * + * \param[in] TxnPtr Transaction object + * \returns Timeout to use for OSAL routines that may block + */ +int32 CFE_SB_MessageTxn_GetOsTimeout(const CFE_SB_MessageTxn_State_t *TxnPtr); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Facilitates reporting of a single event related to a transmit transaction + * + * Internal routine that assembles the following information about how an event should + * be reported: + * + * - Complete message text (human-readable) + * - The type of event (information, error, etc) + * - The loop-detection bit to use (request to send/clear to send) + * + * \param[inout] TxnPtr Transaction object + * \param[in] TskId Calling task identifier + * \param[in] EventId Event to report + * \param[out] EvtMsg Message buffer + * \param[in] EvtMsgSize Size of EvtMsg buffer + * \param[in] ContextPtr Context information (may be NULL for general transaction events) + * \param[out] EventType Buffer to store event type + * \param[out] ReqBit Buffer to store request/loop detect bit + */ +void CFE_SB_MessageTxn_GetEventDetails(const CFE_SB_MessageTxn_State_t *TxnPtr, const CFE_SB_PipeSetEntry_t *ContextPtr, + uint16 EventId, CFE_ES_TaskId_t TskId, char *EvtMsg, size_t EvtMsgSize, + CFE_EVS_EventType_Enum_t *EventType, int32 *ReqBit); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Reports a single event related to a transmit transaction + * + * Internal routine that performs the basic flow of sending a single event ID related + * to a transmit transaction. This uses CFE_SB_TransmitTxn_GetEventDetails() to determine + * how the event should be reported, then checks/sets the Request bit (loop detect), sends the + * event, then clears the request bit. + * + * \param[in] TxnPtr Transaction object + * \param[in] ContextPtr Context information (may be NULL for general transaction events) + * \param[in] EventId Event to report + * + * \returns Boolean indicating if the event constitutes a transaction failure + * \retval true if the transmit transaction failed as a result (event is an error) + * \retval false if this was informational (event is benign, e.g. no subscribers) + */ +bool CFE_SB_MessageTxn_ReportSingleEvent(const CFE_SB_MessageTxn_State_t *TxnPtr, + const CFE_SB_PipeSetEntry_t *ContextPtr, uint16 EventId); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Implements all reporting and accouting associated with a transmit transaction + * + * Internal routine that sends all events and increments any telemetry counters associated + * with the transaction. This should be called as the last step of a transmit operation. + * + * \param[in] TxnPtr Transaction object + */ +void CFE_SB_MessageTxn_ReportEvents(const CFE_SB_MessageTxn_State_t *TxnPtr); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Process all pipes identified in the transaction object + * + * The provided HandlerFunc will be invoked for every item in the PipeSet within the transaction + * object. The handler function can stop the loop by returning "false". + * + * \param[in] HandlerFunc Handler function to call + * \param[inout] TxnPtr Transaction object, passed through to handler + * \param[inout] Arg Opaque argument to pass to handler + */ +void CFE_SB_MessageTxn_ProcessPipes(CFE_SB_MessageTxn_PipeHandler_t HandlerFunc, CFE_SB_MessageTxn_State_t *TxnPtr, + void *Arg); + +/* + * Receive Transaction implementation/helper functions + * These functions are specific to the receive-side operation + */ + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Initialize a receive transaction + * + * This should be the first function called to initialize a transaction state object + * that has been newly allocated. The object will be put into a safe initial state. + * + * The RefMemPtr should refer to the object/buffer that the user intends to receive from the + * software bus. This is kept as part of the transaction mainly for logging/event purposes, + * the address may be reported in events if the transaction is unsuccessful. + * + * \param[out] TxnPtr Transaction object to initialize + * \param[in] RefMemPtr Pointer to user object/buffer being received (opaque) + */ +CFE_SB_MessageTxn_State_t *CFE_SB_ReceiveTxn_Init(CFE_SB_ReceiveTxn_State_t *TxnPtr, const void *RefMemPtr); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Sets the Pipe ID to read for a receive transaction + * + * Currently, receive transactions only deal with a single pipe. This sets the pipe ID to + * read to receive the object. + * + * \param[inout] TxnPtr Transaction object + * \param[in] PipeId Pipe ID to read from + */ +void CFE_SB_ReceiveTxn_SetPipeId(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_PipeId_t PipeId); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Pipe handler function for receive transactions + * + * Helper function to implement reading of a pipe during a receive transaction. This + * is only used via CFE_SB_MessageTxn_ProcessPipes(), but declared here so it can be unit + * tested. + * + * This implements the logic of transmitting a message buffer to all destinations that were + * identified via CFE_SB_TransmitTxn_FindDestinations(). + * + * As this function will broadcast the message to any number of destinations (0-many), + * and some may be successful and some may fail, this function always returns true + * to continue the loop inside CFE_SB_MessageTxn_ProcessPipes(). + * + * The status/result of every queue write operation is kept as part of the transaction + * status object. Event reporting related those operations is deferred to the + * CFE_SB_TransmitTxn_ReportEvents() function, which should be invoked at the end + * of the transaction to report any delivery issues. + * + * The caller is expected to hold a reference (use count) of the buffer prior to invoking + * this routine, representing itself, plus the number of active destinations. + * + * \note If any queue write operation is _not_ successful, the use count will be decremented. + * \sa CFE_SB_MessageTxn_ProcessPipes + * + * \param[inout] TxnPtr Transaction object + * \param[in] ContextPtr Pointer to pipe entry within transaction + * \param[inout] Arg Opaque argument for API, should be a CFE_SB_BufferD_t** + * \returns always false to stop the parent loop (receive transactions only read a single pipe) + */ +bool CFE_SB_ReceiveTxn_PipeHandler(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_PipeSetEntry_t *ContextPtr, void *Arg); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Executes a receive transaction + * + * Implements reading of the pipe(s) and exporting information such as the content pointer, content size, + * and message ID. + * + * \param[inout] TxnPtr Transaction object + * \returns Pointer to buffer that was read + * \retval NULL if no message was read (e.g. if a timeout occurred or polling an empty queue) + */ +const CFE_SB_Buffer_t *CFE_SB_ReceiveTxn_Execute(CFE_SB_MessageTxn_State_t *TxnPtr); + +/* + * Transmit Transaction implementation/helper functions + * These functions are specific to the transmit-side operation + */ + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Initialize a transmit transaction + * + * This should be the first function called to initialize a transaction state object + * that has been newly allocated. The object will be put into a safe initial state. + * + * The RefMemPtr should refer to the object/buffer that the user intends to send on the + * software bus. This is kept as part of the transaction mainly for logging/event purposes, + * the address may be reported in events if the transaction is unsuccessful. + * + * \param[out] TxnPtr Transaction object to initialize + * \param[in] RefMemPtr Pointer to user object/buffer being transmitted (opaque) + */ +CFE_SB_MessageTxn_State_t *CFE_SB_TransmitTxn_Init(CFE_SB_TransmitTxn_State_t *TxnPtr, const void *RefMemPtr); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Set up a transmit transaction from a CFE MSG object + * + * Extracts the routing information - specifically MsgId and Size - from the message content. + * + * Software Bus transmit operations typically operate based on the MsgId and Size information + * that the application stores the message header prior to transmission. This function implements + * that traditional behavior by pulling this information out of the message and caching it in + * the transaction state object for use during routing and delivery of the object. + * + * The information is extracted from the message using the CFE MSG API calls. + * + * \param[inout] TxnPtr Transaction object + * \param[in] MsgPtr Pointer to message being transmitted + */ +void CFE_SB_TransmitTxn_SetupFromMsg(CFE_SB_MessageTxn_State_t *TxnPtr, const CFE_MSG_Message_t *MsgPtr); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Find the complete set of destination pipes for the given transaction + * + * Looks up the routing information (via the SBR subsystem/module) and collects the set of + * active destinations (Pipe IDs) that the message will need to be broadcast to. + * + * If no destinations are found, then this sets the transaction status to generate a + * NO SUBSCRIBERS event, but it does not actually send the event from here. + * + * \sa CFE_SB_TransmitTxn_ReportEvents() + * + * \note This also increments the buffer use count for every destination found, in anticipation + * of writing the buffer address into the underlying queue. If the subsequent write is not + * actually successful, then the count must be decremented accordingly, to keep the reference + * counts correct. + * + * \param[inout] TxnPtr Transaction object + * \param[inout] BufDscPtr Buffer descriptor that is pending broadcast + */ +void CFE_SB_TransmitTxn_FindDestinations(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_BufferD_t *BufDscPtr); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Pipe handler function for transmit transactions + * + * Helper function to implement reading of a pipe during a transmit transaction. This + * is only used via CFE_SB_MessageTxn_ProcessPipes(), but declared here so it can be unit + * tested. + * + * \sa CFE_SB_MessageTxn_ProcessPipes + * + * \param[inout] TxnPtr Transaction object + * \param[in] ContextPtr Pointer to pipe entry within transaction + * \param[inout] Arg Opaque argument for API, should be a CFE_SB_BufferD_t* + * \returns always true to continue the parent loop (transmit transactions process all pipes) + */ +bool CFE_SB_TransmitTxn_PipeHandler(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_PipeSetEntry_t *ContextPtr, void *Arg); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Executes the transmit transaction + * + * Internal routine that finds all active destinations for the route and broadcasts the + * buffer to those destinations. Any errors or off-nominal events that occur will be stored + * in the transaction object for deferred reporting via CFE_SB_TransmitTxn_ReportEvents(). + * + * \note Upon successful operation, this function decrements the use count of the buffer. + * The calling routine is expected to hold one reference to the buffer being transmitted, which + * is then consumed by this call. If the caller intends to retain a reference to the buffer, + * the use count must be incremented prior to invoking this function. + * + * However, if this returns a non-successful status code then the use count will _not_ be + * decremented - but this can generally only fail if the passed-in buffer does not validate. + * + * \param[inout] TxnPtr Transaction object + * \param[inout] BufPtr Buffer object that is pending to be broadcast + */ +void CFE_SB_TransmitTxn_Execute(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_Buffer_t *BufPtr); + /* * Software Bus Message Handler Function prototypes */ diff --git a/modules/sb/ut-coverage/sb_UT.c b/modules/sb/ut-coverage/sb_UT.c index b31faa4e5..20c6ab89d 100644 --- a/modules/sb/ut-coverage/sb_UT.c +++ b/modules/sb/ut-coverage/sb_UT.c @@ -390,7 +390,7 @@ void Test_SB_Main_RcvErr(void) UT_SetDeferredRetcode(UT_KEY(OS_QueueGet), 1, -1); CFE_SB_TaskMain(); - CFE_UtAssert_EVENTCOUNT(6); + CFE_UtAssert_EVENTCOUNT(7); CFE_UtAssert_EVENTSENT(CFE_SB_INIT_EID); @@ -2879,6 +2879,591 @@ void Test_Unsubscribe_GetDestPtr(void) CFE_UtAssert_TEARDOWN(CFE_SB_DeletePipe(TestPipe2)); } +void Test_TransmitTxn_Init(void) +{ + /* Test case for: + * void CFE_SB_TransmitTxn_Init(CFE_SB_TransmitTxn_State_t *TxnPtr, const void *RefMemPtr); + */ + CFE_SB_TransmitTxn_State_t TxnBuf; + CFE_SB_MessageTxn_State_t *Txn; + uint32 MyData; + + /* Call to ensure coverage */ + memset(&Txn, 0xEE, sizeof(Txn)); + MyData = 123; + UtAssert_ADDRESS_EQ(Txn = CFE_SB_TransmitTxn_Init(&TxnBuf, &MyData), &TxnBuf); + + /* This should have cleared everything */ + UtAssert_ADDRESS_EQ(Txn->RefMemPtr, &MyData); + UtAssert_ZERO(Txn->NumPipes); + UtAssert_ZERO(Txn->NumPipeErrs); + UtAssert_ZERO(Txn->TransactionEventId); + UtAssert_ZERO(Txn->TimeoutMode); + UtAssert_ZERO(Txn->ContentSize); + UtAssert_BOOL_FALSE(Txn->IsEndpoint); +} + +void Test_MessageTxn_SetEventAndStatus(void) +{ + /* Test function for: + * void CFE_SB_MessageTxn_SetEventAndStatus(CFE_SB_MessageTxn_State_t *TxnPtr, + * uint16 EventId, CFE_Status_t Status) + */ + CFE_SB_MessageTxn_State_t Txn; + + /* + * although this function is used throughout the other tests, this is needed to target + * certain branches/paths that don't get executed. Namely - this should only store + * the first error/event that occurs. + */ + + /* Set only an event ID first, then attempt to set both */ + memset(&Txn, 0, sizeof(Txn)); + + UtAssert_BOOL_TRUE(CFE_SB_MessageTxn_IsOK(&Txn)); + + CFE_SB_MessageTxn_SetEventAndStatus(&Txn, CFE_SB_MSG_TOO_BIG_EID, CFE_SUCCESS); + UtAssert_UINT16_EQ(Txn.TransactionEventId, CFE_SB_MSG_TOO_BIG_EID); + UtAssert_INT32_EQ(Txn.Status, CFE_SUCCESS); + UtAssert_BOOL_FALSE(CFE_SB_MessageTxn_IsOK(&Txn)); + + CFE_SB_MessageTxn_SetEventAndStatus(&Txn, CFE_SB_SEND_INV_MSGID_EID, CFE_SB_BAD_ARGUMENT); + UtAssert_UINT16_EQ(Txn.TransactionEventId, CFE_SB_MSG_TOO_BIG_EID); + UtAssert_INT32_EQ(Txn.Status, CFE_SB_BAD_ARGUMENT); + UtAssert_BOOL_FALSE(CFE_SB_MessageTxn_IsOK(&Txn)); + + /* Set only a status code first, then attempt to set both */ + memset(&Txn, 0, sizeof(Txn)); + + CFE_SB_MessageTxn_SetEventAndStatus(&Txn, 0, CFE_SB_BUF_ALOC_ERR); + UtAssert_UINT16_EQ(Txn.TransactionEventId, 0); + UtAssert_INT32_EQ(Txn.Status, CFE_SB_BUF_ALOC_ERR); + UtAssert_BOOL_FALSE(CFE_SB_MessageTxn_IsOK(&Txn)); + + CFE_SB_MessageTxn_SetEventAndStatus(&Txn, CFE_SB_SEND_INV_MSGID_EID, CFE_SB_BAD_ARGUMENT); + UtAssert_UINT16_EQ(Txn.TransactionEventId, CFE_SB_SEND_INV_MSGID_EID); + UtAssert_INT32_EQ(Txn.Status, CFE_SB_BUF_ALOC_ERR); + UtAssert_BOOL_FALSE(CFE_SB_MessageTxn_IsOK(&Txn)); +} + +void Test_MessageTxn_Timeout(void) +{ + /* Test case for: + * void CFE_SB_MessageTxn_SetTimeout(CFE_SB_MessageTxn_State_t *TxnPtr, int32 Timeout); + * int32 CFE_SB_MessageTxn_GetOsTimeout(const CFE_SB_MessageTxn_State_t *TxnPtr); + */ + CFE_SB_MessageTxn_State_t Txn; + OS_time_t TestTime; + + memset(&Txn, 0, sizeof(Txn)); + + UtAssert_VOIDCALL(CFE_SB_MessageTxn_SetTimeout(&Txn, CFE_SB_POLL)); + UtAssert_INT32_EQ(CFE_SB_MessageTxn_GetOsTimeout(&Txn), OS_CHECK); + + UtAssert_VOIDCALL(CFE_SB_MessageTxn_SetTimeout(&Txn, CFE_SB_PEND_FOREVER)); + UtAssert_INT32_EQ(CFE_SB_MessageTxn_GetOsTimeout(&Txn), OS_PEND); + + TestTime = OS_TimeFromTotalSeconds(1000000000); + UT_SetDataBuffer(UT_KEY(CFE_PSP_GetTime), &TestTime, sizeof(TestTime), false); + UtAssert_VOIDCALL(CFE_SB_MessageTxn_SetTimeout(&Txn, 100)); + + UT_SetDataBuffer(UT_KEY(CFE_PSP_GetTime), &TestTime, sizeof(TestTime), false); + UtAssert_INT32_EQ(CFE_SB_MessageTxn_GetOsTimeout(&Txn), 100); + + TestTime = OS_TimeAdd(TestTime, OS_TimeFromTotalMilliseconds(10)); + UT_SetDataBuffer(UT_KEY(CFE_PSP_GetTime), &TestTime, sizeof(TestTime), false); + UtAssert_INT32_EQ(CFE_SB_MessageTxn_GetOsTimeout(&Txn), 90); + + TestTime = OS_TimeAdd(TestTime, OS_TimeFromTotalSeconds(1)); + UT_SetDataBuffer(UT_KEY(CFE_PSP_GetTime), &TestTime, sizeof(TestTime), false); + UtAssert_INT32_EQ(CFE_SB_MessageTxn_GetOsTimeout(&Txn), OS_CHECK); + + UtAssert_VOIDCALL(CFE_SB_MessageTxn_SetTimeout(&Txn, -1000)); + UtAssert_INT32_EQ(Txn.Status, CFE_SB_BAD_ARGUMENT); +} + +void Test_MessageTxn_SetupFromMsg(void) +{ + /* Test case for: + * CFE_Status_t CFE_SB_TransmitTxn_SetupFromMsg(CFE_SB_MessageTxn_State_t *TxnPtr, const CFE_MSG_Message_t + * *MsgPtr); + */ + CFE_SB_MessageTxn_State_t Txn; + CFE_MSG_Message_t Msg; + CFE_SB_MsgId_t MsgId; + CFE_MSG_Size_t MsgSize; + + memset(&Msg, 0, sizeof(Msg)); + + /* Transaction already failed case (no-op) */ + memset(&Txn, 0, sizeof(Txn)); + Txn.Status = -20; + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_SetupFromMsg(&Txn, &Msg)); + UtAssert_INT32_EQ(Txn.Status, -20); + + /* CFE_MSG_GetMsgId() fail case */ + memset(&Txn, 0, sizeof(Txn)); + UT_SetDeferredRetcode(UT_KEY(CFE_MSG_GetMsgId), 1, -10); + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_SetupFromMsg(&Txn, &Msg)); + UtAssert_INT32_EQ(Txn.Status, -10); + UtAssert_UINT32_EQ(Txn.TransactionEventId, CFE_SB_SEND_BAD_ARG_EID); + + /* Invalid MsgId case */ + memset(&Txn, 0, sizeof(Txn)); + MsgId = CFE_SB_INVALID_MSG_ID; + UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_SetupFromMsg(&Txn, &Msg)); + UtAssert_INT32_EQ(Txn.Status, CFE_SB_BAD_ARGUMENT); + UtAssert_UINT32_EQ(Txn.TransactionEventId, CFE_SB_SEND_INV_MSGID_EID); + + /* CFE_MSG_GetSize() fail case */ + memset(&Txn, 0, sizeof(Txn)); + MsgId = SB_UT_TLM_MID; + UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); + UT_SetDeferredRetcode(UT_KEY(CFE_MSG_GetSize), 1, -11); + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_SetupFromMsg(&Txn, &Msg)); + UtAssert_INT32_EQ(Txn.Status, -11); + UtAssert_UINT32_EQ(Txn.TransactionEventId, CFE_SB_SEND_BAD_ARG_EID); + + /* Message too big case */ + memset(&Txn, 0, sizeof(Txn)); + MsgId = SB_UT_TLM_MID; + UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); + MsgSize = 1 + CFE_MISSION_SB_MAX_SB_MSG_SIZE; + UT_SetDataBuffer(UT_KEY(CFE_MSG_GetSize), &MsgSize, sizeof(MsgSize), false); + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_SetupFromMsg(&Txn, &Msg)); + UtAssert_INT32_EQ(Txn.Status, CFE_SB_MSG_TOO_BIG); + UtAssert_UINT32_EQ(Txn.TransactionEventId, CFE_SB_MSG_TOO_BIG_EID); + + /* Nominal case */ + memset(&Txn, 0, sizeof(Txn)); + MsgId = SB_UT_TLM_MID; + UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); + MsgSize = sizeof(Msg); + UT_SetDataBuffer(UT_KEY(CFE_MSG_GetSize), &MsgSize, sizeof(MsgSize), false); + + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_SetupFromMsg(&Txn, &Msg)); + UtAssert_INT32_EQ(Txn.Status, CFE_SUCCESS); + UtAssert_ZERO(Txn.TransactionEventId); + CFE_UtAssert_MSGID_EQ(Txn.RoutingMsgId, SB_UT_TLM_MID); + UtAssert_UINT32_EQ(Txn.ContentSize, sizeof(Msg)); +} + +void Test_TransmitTxn_FindDestinations(void) +{ + /* Test case for: + * void CFE_SB_TransmitTxn_FindDestinations(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_BufferD_t *BufDscPtr); + */ + + CFE_SB_TransmitTxn_State_t TxnBuf; + CFE_SB_MessageTxn_State_t *Txn; + CFE_SB_BufferD_t BufDsc; + CFE_SB_PipeD_t * PipeDscPtr; + CFE_SB_PipeId_t PipeId = CFE_SB_INVALID_PIPE; + CFE_SB_MsgId_t MsgId = SB_UT_TLM_MID; + CFE_SBR_RouteId_t RouteId; + CFE_SB_DestinationD_t * DestPtr; + + memset(&BufDsc, 0, sizeof(BufDsc)); + CFE_SB_TrackingListReset(&BufDsc.Link); /* so tracking list ops work */ + + CFE_UtAssert_SETUP(CFE_SB_CreatePipe(&PipeId, 2, "TestPipe")); + CFE_UtAssert_SETUP(CFE_SB_SubscribeFull(MsgId, PipeId, CFE_SB_DEFAULT_QOS, 2, CFE_SB_MSG_GLOBAL)); + PipeDscPtr = CFE_SB_LocatePipeDescByID(PipeId); + RouteId = CFE_SBR_GetRouteId(MsgId); + DestPtr = CFE_SB_GetDestPtr(RouteId, PipeId); + Txn = CFE_SB_TransmitTxn_Init(&TxnBuf, &BufDsc.Content); + PipeDscPtr->PeakQueueDepth = 1; + + CFE_SB_Global.HKTlmMsg.Payload.NoSubscribersCounter = 0; + + /* No subscriber case */ + Txn->RoutingMsgId = CFE_SB_INVALID_MSG_ID; + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_FindDestinations(Txn, &BufDsc)); + UtAssert_UINT32_EQ(CFE_SB_Global.HKTlmMsg.Payload.NoSubscribersCounter, 1); + UtAssert_UINT32_EQ(Txn->TransactionEventId, CFE_SB_SEND_NO_SUBS_EID); + UtAssert_UINT32_EQ(BufDsc.UseCount, 0); + + /* Nominal Case 1 */ + memset(&BufDsc, 0, sizeof(BufDsc)); + Txn = CFE_SB_TransmitTxn_Init(&TxnBuf, &BufDsc.Content); + CFE_SB_TrackingListReset(&BufDsc.Link); /* so tracking list ops work */ + Txn->RoutingMsgId = MsgId; + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_FindDestinations(Txn, &BufDsc)); + UtAssert_UINT32_EQ(Txn->NumPipes, 1); + CFE_UtAssert_RESOURCEID_EQ(Txn->PipeSet[0].PipeId, PipeId); + UtAssert_UINT32_EQ(Txn->PipeSet[0].PendingEventId, 0); + UtAssert_UINT32_EQ(BufDsc.UseCount, 1); + UtAssert_UINT32_EQ(DestPtr->BuffCount, 1); + UtAssert_UINT32_EQ(PipeDscPtr->CurrentQueueDepth, 1); + UtAssert_UINT32_EQ(PipeDscPtr->PeakQueueDepth, 1); + UtAssert_UINT32_EQ(PipeDscPtr->SendErrors, 0); + UtAssert_UINT32_EQ(Txn->NumPipeErrs, 0); + + /* Nominal Case 2 */ + memset(&BufDsc, 0, sizeof(BufDsc)); + Txn = CFE_SB_TransmitTxn_Init(&TxnBuf, &BufDsc.Content); + CFE_SB_TrackingListReset(&BufDsc.Link); /* so tracking list ops work */ + Txn->RoutingMsgId = MsgId; + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_FindDestinations(Txn, &BufDsc)); + UtAssert_UINT32_EQ(Txn->NumPipes, 1); + CFE_UtAssert_RESOURCEID_EQ(Txn->PipeSet[0].PipeId, PipeId); + UtAssert_UINT32_EQ(Txn->PipeSet[0].PendingEventId, 0); + UtAssert_UINT32_EQ(BufDsc.UseCount, 1); + UtAssert_UINT32_EQ(DestPtr->BuffCount, 2); + UtAssert_UINT32_EQ(PipeDscPtr->CurrentQueueDepth, 2); + UtAssert_UINT32_EQ(PipeDscPtr->PeakQueueDepth, 2); + UtAssert_UINT32_EQ(PipeDscPtr->SendErrors, 0); + UtAssert_UINT32_EQ(Txn->NumPipeErrs, 0); + + /* MsgLim Error Case */ + memset(&BufDsc, 0, sizeof(BufDsc)); + Txn = CFE_SB_TransmitTxn_Init(&TxnBuf, &BufDsc.Content); + CFE_SB_TrackingListReset(&BufDsc.Link); /* so tracking list ops work */ + Txn->RoutingMsgId = MsgId; + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_FindDestinations(Txn, &BufDsc)); + UtAssert_UINT32_EQ(Txn->NumPipes, 1); + CFE_UtAssert_RESOURCEID_EQ(Txn->PipeSet[0].PipeId, PipeId); + UtAssert_UINT32_EQ(Txn->PipeSet[0].PendingEventId, CFE_SB_MSGID_LIM_ERR_EID); + UtAssert_ZERO(BufDsc.UseCount); + UtAssert_UINT32_EQ(DestPtr->BuffCount, 2); + UtAssert_UINT32_EQ(PipeDscPtr->CurrentQueueDepth, 2); + UtAssert_UINT32_EQ(PipeDscPtr->PeakQueueDepth, 2); + UtAssert_UINT32_EQ(PipeDscPtr->SendErrors, 1); + UtAssert_UINT32_EQ(Txn->NumPipeErrs, 1); + DestPtr->BuffCount = 0; + + /* Destination Inactive Case */ + memset(&BufDsc, 0, sizeof(BufDsc)); + Txn = CFE_SB_TransmitTxn_Init(&TxnBuf, &BufDsc.Content); + CFE_SB_TrackingListReset(&BufDsc.Link); /* so tracking list ops work */ + Txn->RoutingMsgId = MsgId; + DestPtr->Active = CFE_SB_INACTIVE; + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_FindDestinations(Txn, &BufDsc)); + UtAssert_ZERO(Txn->NumPipes); + UtAssert_UINT32_EQ(BufDsc.UseCount, 0); + UtAssert_UINT32_EQ(DestPtr->BuffCount, 0); + DestPtr->Active = CFE_SB_ACTIVE; + + /* Pipe "Ignore Mine" Option Case w/Matching AppID */ + Txn = CFE_SB_TransmitTxn_Init(&TxnBuf, &BufDsc.Content); + Txn->RoutingMsgId = MsgId; + PipeDscPtr->Opts |= CFE_SB_PIPEOPTS_IGNOREMINE; + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_FindDestinations(Txn, &BufDsc)); + UtAssert_ZERO(Txn->NumPipes); + UtAssert_UINT32_EQ(BufDsc.UseCount, 0); + UtAssert_UINT32_EQ(DestPtr->BuffCount, 0); + + /* Pipe "Ignore Mine" Option Case w/Non-Matching AppID */ + Txn = CFE_SB_TransmitTxn_Init(&TxnBuf, &BufDsc.Content); + Txn->RoutingMsgId = MsgId; + PipeDscPtr->AppId = CFE_ES_APPID_UNDEFINED; + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_FindDestinations(Txn, &BufDsc)); + UtAssert_UINT32_EQ(Txn->NumPipes, 1); + UtAssert_UINT32_EQ(BufDsc.UseCount, 1); + UtAssert_UINT32_EQ(DestPtr->BuffCount, 1); + CFE_ES_GetAppID(&PipeDscPtr->AppId); + PipeDscPtr->Opts &= ~CFE_SB_PIPEOPTS_IGNOREMINE; + + /* DestPtr List too long - this emulates a hypothetical bug in SBR allowing list to grow too long */ + /* Hack to make it infinite length */ + DestPtr->Next = DestPtr; + Txn = CFE_SB_TransmitTxn_Init(&TxnBuf, &BufDsc.Content); + Txn->RoutingMsgId = MsgId; + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_FindDestinations(Txn, &BufDsc)); + UtAssert_UINT32_EQ(Txn->NumPipes, CFE_PLATFORM_SB_MAX_DEST_PER_PKT); + + CFE_UtAssert_TEARDOWN(CFE_SB_DeletePipe(PipeId)); +} + +void Test_TransmitTxn_PipeHandler(void) +{ + /* Test function for: + * bool CFE_SB_TransmitTxn_PipeHandler(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_PipeSetEntry_t *ContextPtr, void + * *Arg); + */ + + CFE_SB_TransmitTxn_State_t TxnBuf; + CFE_SB_MessageTxn_State_t *Txn; + CFE_SB_PipeId_t PipeId = CFE_SB_INVALID_PIPE; + CFE_SB_MsgId_t MsgId = SB_UT_TLM_MID; + CFE_SB_BufferD_t SBBufD; + + memset(&SBBufD, 0, sizeof(SBBufD)); + memset(&TxnBuf, 0, sizeof(TxnBuf)); + CFE_SB_TrackingListReset(&SBBufD.Link); /* so tracking list ops work */ + Txn = CFE_SB_TransmitTxn_Init(&TxnBuf, &SBBufD.Content); + + CFE_UtAssert_SETUP(CFE_SB_CreatePipe(&PipeId, 3, "TestPipe")); + CFE_UtAssert_SETUP(CFE_SB_Subscribe(MsgId, PipeId)); + + SBBufD.DestRouteId = CFE_SBR_GetRouteId(MsgId); + + CFE_SB_LocatePipeDescByID(PipeId)->CurrentQueueDepth = 1; + CFE_SB_GetDestPtr(SBBufD.DestRouteId, PipeId)->BuffCount = 1; + + Txn->NumPipes = 6; + Txn->NumPipeErrs = 1; + Txn->PipeSet[0].PipeId = PipeId; + Txn->PipeSet[1].PipeId = PipeId; + Txn->PipeSet[2].PipeId = PipeId; + Txn->PipeSet[2].PendingEventId = 1; + Txn->PipeSet[3].PipeId = PipeId; + Txn->PipeSet[4].PipeId = PipeId; + Txn->PipeSet[5].PipeId = SB_UT_ALTERNATE_INVALID_PIPEID; + + UT_SetDeferredRetcode(UT_KEY(OS_QueuePut), 2, OS_QUEUE_FULL); + UT_SetDeferredRetcode(UT_KEY(OS_QueuePut), 1, OS_ERROR); + UT_SetDeferredRetcode(UT_KEY(OS_QueuePut), 1, OS_ERROR); + UT_SetDeferredRetcode(UT_KEY(OS_QueuePut), 1, OS_ERROR); + + UtAssert_VOIDCALL(CFE_SB_MessageTxn_ProcessPipes(CFE_SB_TransmitTxn_PipeHandler, Txn, &SBBufD)); + UtAssert_STUB_COUNT(OS_QueuePut, 5); + UtAssert_UINT32_EQ(Txn->NumPipeErrs, 5); + UtAssert_UINT32_EQ(Txn->PipeSet[0].PendingEventId, 0); + UtAssert_UINT32_EQ(Txn->PipeSet[1].PendingEventId, CFE_SB_Q_FULL_ERR_EID); + UtAssert_UINT32_EQ(Txn->PipeSet[2].PendingEventId, 1); + UtAssert_UINT32_EQ(Txn->PipeSet[3].PendingEventId, CFE_SB_Q_WR_ERR_EID); + UtAssert_UINT32_EQ(Txn->PipeSet[4].PendingEventId, CFE_SB_Q_WR_ERR_EID); + UtAssert_UINT32_EQ(Txn->PipeSet[5].PendingEventId, CFE_SB_Q_WR_ERR_EID); + + CFE_UtAssert_TEARDOWN(CFE_SB_DeletePipe(PipeId)); +} + +void Test_TransmitTxn_Execute(void) +{ + /* Test case for: + * CFE_Status_t CFE_SB_TransmitTxn_Execute(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_Buffer_t *BufPtr); + */ + CFE_SB_BufferD_t SBBufD; + CFE_SB_TransmitTxn_State_t TxnBuf; + CFE_SB_MessageTxn_State_t *Txn; + CFE_ES_AppId_t MyAppId; + CFE_SB_PipeId_t PipeId = CFE_SB_INVALID_PIPE; + CFE_SB_MsgId_t MsgId = SB_UT_TLM_MID; + + memset(&SBBufD, 0, sizeof(SBBufD)); + memset(&TxnBuf, 0, sizeof(TxnBuf)); + CFE_SB_TrackingListReset(&SBBufD.Link); /* so tracking list ops work */ + Txn = CFE_SB_TransmitTxn_Init(&TxnBuf, &SBBufD.Content); + CFE_UtAssert_SETUP(CFE_SB_CreatePipe(&PipeId, 2, "TestPipe")); + CFE_UtAssert_SETUP(CFE_ES_GetAppID(&MyAppId)); + + /* no subs - this should still keep status as SUCCESS but trigger CFE_SB_SEND_NO_SUBS_EID event */ + SBBufD.AppId = MyAppId; + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_Execute(Txn, &SBBufD.Content)); + UtAssert_STUB_COUNT(OS_QueuePut, 0); + UtAssert_UINT32_EQ(Txn->TransactionEventId, CFE_SB_SEND_NO_SUBS_EID); + UtAssert_INT32_EQ(Txn->Status, CFE_SUCCESS); + UtAssert_BOOL_FALSE(CFE_RESOURCEID_TEST_DEFINED(SBBufD.AppId)); + + /* add a subscriber - nominal case */ + CFE_UtAssert_SETUP(CFE_SB_SubscribeFull(MsgId, PipeId, CFE_SB_DEFAULT_QOS, 2, CFE_SB_MSG_GLOBAL)); + Txn = CFE_SB_TransmitTxn_Init(&TxnBuf, &SBBufD.Content); + Txn->RoutingMsgId = MsgId; + SBBufD.AppId = MyAppId; + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_Execute(Txn, &SBBufD.Content)); + UtAssert_BOOL_TRUE(CFE_SB_MessageTxn_IsOK(Txn)); + UtAssert_STUB_COUNT(OS_QueuePut, 1); + UtAssert_BOOL_FALSE(CFE_RESOURCEID_TEST_DEFINED(SBBufD.AppId)); + + /* error writing to the pipe - this also still keeps status as SUCCESS but trigger CFE_SB_Q_WR_ERR_EID */ + Txn = CFE_SB_TransmitTxn_Init(&TxnBuf, &SBBufD.Content); + Txn->RoutingMsgId = MsgId; + SBBufD.AppId = MyAppId; + UT_SetDeferredRetcode(UT_KEY(OS_QueuePut), 1, OS_ERROR); + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_Execute(Txn, &SBBufD.Content)); + UtAssert_BOOL_TRUE(CFE_SB_MessageTxn_IsOK(Txn)); + UtAssert_UINT32_EQ(Txn->PipeSet[0].PendingEventId, CFE_SB_Q_WR_ERR_EID); + UtAssert_STUB_COUNT(OS_QueuePut, 2); + UtAssert_BOOL_FALSE(CFE_RESOURCEID_TEST_DEFINED(SBBufD.AppId)); + + /* Validation fail */ + Txn = CFE_SB_TransmitTxn_Init(&TxnBuf, &SBBufD.Content); + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_Execute(Txn, &SBBufD.Content)); + UtAssert_BOOL_FALSE(CFE_SB_MessageTxn_IsOK(Txn)); + UtAssert_INT32_EQ(Txn->Status, CFE_SB_BUFFER_INVALID); + UtAssert_STUB_COUNT(OS_QueuePut, 2); + + CFE_UtAssert_TEARDOWN(CFE_SB_DeletePipe(PipeId)); +} + +void Test_MessageTxn_GetEventDetails(void) +{ + /* Test case for: + * void CFE_SB_MessageTxn_GetEventDetails(const CFE_SB_MessageTxn_State_t *TxnPtr, CFE_ES_TaskId_t TskId, uint16 + * EventId,char *EvtMsg, size_t EvtMsgSize, const CFE_SB_PipeSetEntry_t *ContextPtr,CFE_EVS_EventType_Enum_t + * *EventType, int32 *ReqBit); + */ + + CFE_SB_MessageTxn_State_t Txn; + CFE_SB_PipeSetEntry_t Entry; + CFE_ES_TaskId_t MyTskId; + char EvtMsg[64]; + CFE_EVS_EventType_Enum_t EvType; + int32 EvReqBit; + CFE_SB_PipeId_t PipeId; + + CFE_UtAssert_SETUP(CFE_ES_GetTaskID(&MyTskId)); + CFE_UtAssert_SETUP(CFE_SB_CreatePipe(&PipeId, 3, "TestPipe")); + CFE_SB_MessageTxn_Init(&Txn, &Entry, 1, &PipeId); + EvReqBit = 0; + EvType = 0; + Entry.PipeId = PipeId; + + memset(EvtMsg, 0xAA, sizeof(EvtMsg)); + UtAssert_VOIDCALL( + CFE_SB_MessageTxn_GetEventDetails(&Txn, NULL, 0, MyTskId, EvtMsg, sizeof(EvtMsg), &EvType, &EvReqBit)); + UtAssert_ZERO(EvtMsg[0]); + UtAssert_ZERO(EvReqBit); + + memset(EvtMsg, 0xAA, sizeof(EvtMsg)); + UtAssert_VOIDCALL(CFE_SB_MessageTxn_GetEventDetails(&Txn, NULL, CFE_SB_SEND_BAD_ARG_EID, MyTskId, EvtMsg, + sizeof(EvtMsg), &EvType, &EvReqBit)); + UtAssert_NONZERO(EvtMsg[0]); + UtAssert_NOT_NULL(memchr(EvtMsg, 0, sizeof(EvtMsg))); + UtAssert_UINT32_EQ(EvReqBit, CFE_SB_SEND_BAD_ARG_EID_BIT); + + memset(EvtMsg, 0xAA, sizeof(EvtMsg)); + UtAssert_VOIDCALL(CFE_SB_MessageTxn_GetEventDetails(&Txn, NULL, CFE_SB_SEND_INV_MSGID_EID, MyTskId, EvtMsg, + sizeof(EvtMsg), &EvType, &EvReqBit)); + UtAssert_NONZERO(EvtMsg[0]); + UtAssert_UINT32_EQ(EvReqBit, CFE_SB_SEND_INV_MSGID_EID_BIT); + UtAssert_NOT_NULL(memchr(EvtMsg, 0, sizeof(EvtMsg))); + + memset(EvtMsg, 0xAA, sizeof(EvtMsg)); + UtAssert_VOIDCALL(CFE_SB_MessageTxn_GetEventDetails(&Txn, NULL, CFE_SB_MSG_TOO_BIG_EID, MyTskId, EvtMsg, + sizeof(EvtMsg), &EvType, &EvReqBit)); + UtAssert_NONZERO(EvtMsg[0]); + UtAssert_NOT_NULL(memchr(EvtMsg, 0, sizeof(EvtMsg))); + UtAssert_UINT32_EQ(EvReqBit, CFE_SB_MSG_TOO_BIG_EID_BIT); + + memset(EvtMsg, 0xAA, sizeof(EvtMsg)); + UtAssert_VOIDCALL(CFE_SB_MessageTxn_GetEventDetails(&Txn, NULL, CFE_SB_SEND_NO_SUBS_EID, MyTskId, EvtMsg, + sizeof(EvtMsg), &EvType, &EvReqBit)); + UtAssert_NONZERO(EvtMsg[0]); + UtAssert_NOT_NULL(memchr(EvtMsg, 0, sizeof(EvtMsg))); + UtAssert_UINT32_EQ(EvReqBit, CFE_SB_SEND_NO_SUBS_EID_BIT); + + memset(EvtMsg, 0xAA, sizeof(EvtMsg)); + UtAssert_VOIDCALL(CFE_SB_MessageTxn_GetEventDetails(&Txn, &Entry, CFE_SB_GET_BUF_ERR_EID, MyTskId, EvtMsg, + sizeof(EvtMsg), &EvType, &EvReqBit)); + UtAssert_NONZERO(EvtMsg[0]); + UtAssert_NOT_NULL(memchr(EvtMsg, 0, sizeof(EvtMsg))); + UtAssert_UINT32_EQ(EvReqBit, CFE_SB_GET_BUF_ERR_EID_BIT); + + memset(EvtMsg, 0xAA, sizeof(EvtMsg)); + UtAssert_VOIDCALL(CFE_SB_MessageTxn_GetEventDetails(&Txn, &Entry, CFE_SB_MSGID_LIM_ERR_EID, MyTskId, EvtMsg, + sizeof(EvtMsg), &EvType, &EvReqBit)); + UtAssert_NONZERO(EvtMsg[0]); + UtAssert_NOT_NULL(memchr(EvtMsg, 0, sizeof(EvtMsg))); + UtAssert_UINT32_EQ(EvReqBit, CFE_SB_MSGID_LIM_ERR_EID_BIT); + + memset(EvtMsg, 0xAA, sizeof(EvtMsg)); + UtAssert_VOIDCALL(CFE_SB_MessageTxn_GetEventDetails(&Txn, &Entry, CFE_SB_Q_FULL_ERR_EID, MyTskId, EvtMsg, + sizeof(EvtMsg), &EvType, &EvReqBit)); + UtAssert_NONZERO(EvtMsg[0]); + UtAssert_NOT_NULL(memchr(EvtMsg, 0, sizeof(EvtMsg))); + UtAssert_UINT32_EQ(EvReqBit, CFE_SB_Q_FULL_ERR_EID_BIT); + + memset(EvtMsg, 0xAA, sizeof(EvtMsg)); + UtAssert_VOIDCALL(CFE_SB_MessageTxn_GetEventDetails(&Txn, NULL, CFE_SB_Q_WR_ERR_EID, MyTskId, EvtMsg, + sizeof(EvtMsg), &EvType, &EvReqBit)); + UtAssert_NONZERO(EvtMsg[0]); + UtAssert_NOT_NULL(memchr(EvtMsg, 0, sizeof(EvtMsg))); + UtAssert_UINT32_EQ(EvReqBit, CFE_SB_Q_WR_ERR_EID_BIT); + + CFE_UtAssert_TEARDOWN(CFE_SB_DeletePipe(PipeId)); +} + +void Test_MessageTxn_ReportSingleEvent(void) +{ + /* Test case for: + * bool CFE_SB_MessageTxn_ReportSingleEvent(uint16 EventId, const CFE_SB_MessageTxn_State_t *TxnPtr, const + * CFE_SB_PipeSetEntry_t *ContextPtr); + */ + CFE_SB_PipeSetEntry_t Entry; + CFE_SB_MessageTxn_State_t Txn; + CFE_SB_PipeId_t PipeId; + CFE_ES_TaskId_t MyTskId; + + memset(&Txn, 0, sizeof(Txn)); + memset(&Entry, 0, sizeof(Entry)); + CFE_UtAssert_SETUP(CFE_SB_CreatePipe(&PipeId, 3, "TestPipe")); + CFE_UtAssert_SETUP(CFE_ES_GetTaskID(&MyTskId)); + Txn.NumPipes = 1; + Txn.PipeSet = &Entry; + Entry.PipeId = PipeId; + + /* Event ID == 0 is reserved for no event, nothing to report */ + UT_ClearEventHistory(); + UtAssert_BOOL_FALSE(CFE_SB_MessageTxn_ReportSingleEvent(&Txn, NULL, 0)); + CFE_UtAssert_EVENTCOUNT(0); + + /* No subscribers should be an information event (not an error) */ + UT_ClearEventHistory(); + UtAssert_BOOL_FALSE(CFE_SB_MessageTxn_ReportSingleEvent(&Txn, NULL, CFE_SB_SEND_NO_SUBS_EID)); + CFE_UtAssert_EVENTCOUNT(1); + CFE_UtAssert_EVENTSENT(CFE_SB_SEND_NO_SUBS_EID); + + /* Queue write failure should be an error */ + /* Note that with context != NULL, it looks up the pipe name, which generates a debug event */ + UT_ClearEventHistory(); + UtAssert_BOOL_TRUE(CFE_SB_MessageTxn_ReportSingleEvent(&Txn, &Entry, CFE_SB_Q_WR_ERR_EID)); + CFE_UtAssert_EVENTCOUNT(2); + CFE_UtAssert_EVENTSENT(CFE_SB_Q_WR_ERR_EID); + CFE_UtAssert_EVENTSENT(CFE_SB_GETPIPENAME_EID); + + /* Check that the loop-avoidance works */ + CFE_SB_RequestToSendEvent(MyTskId, CFE_SB_MSG_TOO_BIG_EID_BIT); + UT_ClearEventHistory(); + UtAssert_BOOL_TRUE(CFE_SB_MessageTxn_ReportSingleEvent(&Txn, NULL, CFE_SB_MSG_TOO_BIG_EID)); + CFE_UtAssert_EVENTCOUNT(0); + CFE_SB_FinishSendEvent(MyTskId, CFE_SB_MSG_TOO_BIG_EID_BIT); + + CFE_UtAssert_TEARDOWN(CFE_SB_DeletePipe(PipeId)); +} + +void Test_MessageTxn_ReportEvents(void) +{ + /* Test case for: + * void CFE_SB_MessageTxn_ReportEvents(const CFE_SB_MessageTxn_State_t *TxnPtr); + */ + CFE_SB_PipeSetEntry_t PipeSetEntry; + CFE_SB_MessageTxn_State_t Txn; + + memset(&Txn, 0, sizeof(Txn)); + Txn.PipeSet = &PipeSetEntry; + Txn.NumPipes = 1; + Txn.MaxPipes = 1; + + /* nominal */ + UtAssert_VOIDCALL(CFE_SB_MessageTxn_ReportEvents(&Txn)); + UtAssert_ZERO(CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter); + + /* with an event at the transaction level, known to be an error */ + Txn.TransactionEventId = CFE_SB_MSG_TOO_BIG_EID; + Txn.IsTransmit = true; + UtAssert_VOIDCALL(CFE_SB_MessageTxn_ReportEvents(&Txn)); + UtAssert_UINT32_EQ(CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter, 1); + + /* with some undefined/unknown event at the transaction level, not an error */ + Txn.TransactionEventId = 0xFFFF; + UtAssert_VOIDCALL(CFE_SB_MessageTxn_ReportEvents(&Txn)); + UtAssert_UINT32_EQ(CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter, 1); + + /* with an event at the pipe level, known to be an error */ + Txn.TransactionEventId = 0; + Txn.PipeSet[0].PendingEventId = CFE_SB_Q_FULL_ERR_EID; + UtAssert_VOIDCALL(CFE_SB_MessageTxn_ReportEvents(&Txn)); + UtAssert_UINT32_EQ(CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter, 2); + + /* with some undefined/unknown event at the pipe level, not an error */ + Txn.PipeSet[0].PendingEventId = 0xFFFF; + UtAssert_VOIDCALL(CFE_SB_MessageTxn_ReportEvents(&Txn)); + UtAssert_UINT32_EQ(CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter, 2); +} + /* ** Function for calling SB send message API test functions */ @@ -2897,10 +3482,19 @@ void Test_TransmitMsg_API(void) SB_UT_ADD_SUBTEST(Test_TransmitBuffer_NoIncrement); SB_UT_ADD_SUBTEST(Test_TransmitMsg_ZeroCopyBufferValidate); SB_UT_ADD_SUBTEST(Test_TransmitMsg_DisabledDestination); - SB_UT_ADD_SUBTEST(Test_BroadcastBufferToRoute); - SB_UT_ADD_SUBTEST(Test_TransmitMsgValidate_MaxMsgSizePlusOne); - SB_UT_ADD_SUBTEST(Test_TransmitMsgValidate_NoSubscribers); - SB_UT_ADD_SUBTEST(Test_TransmitMsgValidate_InvalidMsgId); + + SB_UT_ADD_SUBTEST(Test_MessageTxn_SetEventAndStatus); + SB_UT_ADD_SUBTEST(Test_MessageTxn_SetupFromMsg); + SB_UT_ADD_SUBTEST(Test_MessageTxn_Timeout); + SB_UT_ADD_SUBTEST(Test_MessageTxn_GetEventDetails); + SB_UT_ADD_SUBTEST(Test_MessageTxn_ReportSingleEvent); + SB_UT_ADD_SUBTEST(Test_MessageTxn_ReportEvents); + + SB_UT_ADD_SUBTEST(Test_TransmitTxn_Init); + SB_UT_ADD_SUBTEST(Test_TransmitTxn_FindDestinations); + SB_UT_ADD_SUBTEST(Test_TransmitTxn_PipeHandler); + SB_UT_ADD_SUBTEST(Test_TransmitTxn_Execute); + SB_UT_ADD_SUBTEST(Test_AllocateMessageBuffer); SB_UT_ADD_SUBTEST(Test_ReleaseMessageBuffer); } @@ -3015,8 +3609,8 @@ void Test_TransmitMsg_UpdateHeader(void) memset(&TlmPkt, 0, sizeof(TlmPkt)); - /* Set up hook for checking CFE_MSG_UpdateHeader calls */ - UT_SetHookFunction(UT_KEY(CFE_MSG_UpdateHeader), UT_CheckSetSequenceCount, &SeqCnt); + /* Set up hook for checking CFE_MSG_SetSequenceCount calls */ + UT_SetHookFunction(UT_KEY(CFE_MSG_SetSequenceCount), UT_CheckSetSequenceCount, &SeqCnt); CFE_UtAssert_SETUP(CFE_SB_CreatePipe(&PipeId, PipeDepth, "SeqCntTestPipe")); CFE_UtAssert_SETUP(CFE_SB_Subscribe(MsgId, PipeId)); @@ -3029,7 +3623,7 @@ void Test_TransmitMsg_UpdateHeader(void) UT_SetDataBuffer(UT_KEY(CFE_MSG_GetType), &Type, sizeof(Type), false); CFE_UtAssert_SETUP(CFE_SB_TransmitMsg(CFE_MSG_PTR(TlmPkt.TelemetryHeader), true)); - UtAssert_STUB_COUNT(CFE_MSG_UpdateHeader, 1); + UtAssert_STUB_COUNT(CFE_MSG_SetSequenceCount, 1); UtAssert_STUB_COUNT(CFE_MSG_GetNextSequenceCount, 1); UtAssert_INT32_EQ(SeqCnt, SeqCntExpected); @@ -3040,7 +3634,7 @@ void Test_TransmitMsg_UpdateHeader(void) /* Assert sequence count wasn't set */ UtAssert_STUB_COUNT(CFE_MSG_GetNextSequenceCount, 1); - UtAssert_STUB_COUNT(CFE_MSG_UpdateHeader, 1); + UtAssert_STUB_COUNT(CFE_MSG_SetSequenceCount, 1); SeqCntExpected = 2; UT_SetDefaultReturnValue(UT_KEY(CFE_MSG_GetNextSequenceCount), SeqCntExpected); @@ -3049,7 +3643,7 @@ void Test_TransmitMsg_UpdateHeader(void) UT_SetDataBuffer(UT_KEY(CFE_MSG_GetType), &Type, sizeof(Type), false); CFE_UtAssert_SUCCESS(CFE_SB_TransmitMsg(CFE_MSG_PTR(TlmPkt.TelemetryHeader), true)); UtAssert_INT32_EQ(SeqCnt, SeqCntExpected); - UtAssert_STUB_COUNT(CFE_MSG_UpdateHeader, 2); + UtAssert_STUB_COUNT(CFE_MSG_SetSequenceCount, 2); UtAssert_STUB_COUNT(CFE_MSG_GetNextSequenceCount, 2); CFE_UtAssert_EVENTCOUNT(2); @@ -3063,7 +3657,7 @@ void Test_TransmitMsg_UpdateHeader(void) UT_SetDataBuffer(UT_KEY(CFE_MSG_GetSize), &Size, sizeof(Size), false); UT_SetDataBuffer(UT_KEY(CFE_MSG_GetType), &Type, sizeof(Type), false); CFE_UtAssert_SETUP(CFE_SB_TransmitMsg(CFE_MSG_PTR(TlmPkt.TelemetryHeader), true)); /* increment to 3 */ - UtAssert_STUB_COUNT(CFE_MSG_UpdateHeader, 3); + UtAssert_STUB_COUNT(CFE_MSG_SetSequenceCount, 3); UtAssert_STUB_COUNT(CFE_MSG_GetNextSequenceCount, 3); CFE_UtAssert_SETUP(CFE_SB_Subscribe(MsgId, PipeId)); /* resubscribe so we can receive a msg */ @@ -3075,7 +3669,7 @@ void Test_TransmitMsg_UpdateHeader(void) UT_SetDataBuffer(UT_KEY(CFE_MSG_GetType), &Type, sizeof(Type), false); CFE_UtAssert_SETUP(CFE_SB_TransmitMsg(CFE_MSG_PTR(TlmPkt.TelemetryHeader), true)); /* increment to 4 */ UtAssert_INT32_EQ(SeqCnt, SeqCntExpected); - UtAssert_STUB_COUNT(CFE_MSG_UpdateHeader, 4); + UtAssert_STUB_COUNT(CFE_MSG_SetSequenceCount, 4); UtAssert_STUB_COUNT(CFE_MSG_GetNextSequenceCount, 4); CFE_UtAssert_TEARDOWN(CFE_SB_DeletePipe(PipeId)); @@ -3295,9 +3889,6 @@ void Test_TransmitMsg_ZeroCopyBufferValidate(void) * descriptor which is NOT from CFE_SB_AllocateMessageBuffer(). */ memset(&BadZeroCpyBuf, 0, sizeof(BadZeroCpyBuf)); - /* Null Buffer => BAD_ARGUMENT */ - UtAssert_INT32_EQ(CFE_SB_ZeroCopyBufferValidate(NULL, &BufDscPtr), CFE_SB_BAD_ARGUMENT); - /* Non-null buffer pointer but Non Zero-Copy => CFE_SB_BUFFER_INVALID */ UtAssert_INT32_EQ(CFE_SB_ZeroCopyBufferValidate(&BadZeroCpyBuf.Content, &BufDscPtr), CFE_SB_BUFFER_INVALID); @@ -3326,8 +3917,8 @@ void Test_TransmitBuffer_IncrementSeqCnt(void) CFE_MSG_Size_t Size = sizeof(SB_UT_Test_Tlm_t); CFE_MSG_Type_t Type = CFE_MSG_Type_Tlm; - /* Set up hook for checking CFE_MSG_UpdateHeader calls */ - UT_SetHookFunction(UT_KEY(CFE_MSG_UpdateHeader), UT_CheckSetSequenceCount, &SeqCnt); + /* Set up hook for checking CFE_MSG_SetSequenceCount calls */ + UT_SetHookFunction(UT_KEY(CFE_MSG_SetSequenceCount), UT_CheckSetSequenceCount, &SeqCnt); CFE_UtAssert_SETUP(CFE_SB_CreatePipe(&PipeId, PipeDepth, "ZeroCpyTestPipe")); @@ -3496,113 +4087,12 @@ void Test_TransmitMsg_DisabledDestination(void) CFE_UtAssert_TEARDOWN(CFE_SB_DeletePipe(PipeId)); } -/* -** Test successful CFE_SB_BroadcastBufferToRoute -*/ -void Test_BroadcastBufferToRoute(void) +void UT_CFE_MSG_Verify_CustomHandler(void *UserObj, UT_EntryKey_t FuncKey, const UT_StubContext_t *Context) { - CFE_SB_PipeId_t PipeId = CFE_SB_INVALID_PIPE; - CFE_SB_MsgId_t MsgId = SB_UT_TLM_MID; - CFE_SB_BufferD_t SBBufD; - int32 PipeDepth; - CFE_SBR_RouteId_t RouteId; - - memset(&SBBufD, 0, sizeof(SBBufD)); - SBBufD.MsgId = MsgId; - CFE_SB_TrackingListReset(&SBBufD.Link); - - PipeDepth = 2; - CFE_UtAssert_SETUP(CFE_SB_CreatePipe(&PipeId, PipeDepth, "TestPipe")); - CFE_UtAssert_SETUP(CFE_SB_Subscribe(MsgId, PipeId)); - - RouteId = CFE_SBR_GetRouteId(MsgId); - - /* No return from this function - it handles all errors */ - CFE_SB_BroadcastBufferToRoute(&SBBufD, RouteId); - - CFE_UtAssert_EVENTCOUNT(2); - UT_ClearEventHistory(); + bool *IsAcceptable = UT_Hook_GetArgValueByName(Context, "IsAcceptable", bool *); - /* Calling this with invalid route ID is essentially a no-op, called for coverage */ - CFE_SB_BroadcastBufferToRoute(&SBBufD, CFE_SBR_INVALID_ROUTE_ID); - - CFE_UtAssert_TEARDOWN(CFE_SB_DeletePipe(PipeId)); -} - -/* -** Test response to sending a message with the message size larger than allowed -*/ -void Test_TransmitMsgValidate_MaxMsgSizePlusOne(void) -{ - CFE_SB_MsgId_t MsgId = SB_UT_TLM_MID; - CFE_SB_MsgId_t MsgIdRtn; - SB_UT_Test_Tlm_t TlmPkt; - CFE_MSG_Size_t Size = CFE_MISSION_SB_MAX_SB_MSG_SIZE + 1; - CFE_MSG_Size_t SizeRtn = 0; - CFE_SBR_RouteId_t RouteIdRtn; - - memset(&TlmPkt, 0, sizeof(TlmPkt)); - - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetSize), &Size, sizeof(Size), false); - - UtAssert_INT32_EQ(CFE_SB_TransmitMsgValidate(CFE_MSG_PTR(TlmPkt.TelemetryHeader), &MsgIdRtn, &SizeRtn, &RouteIdRtn), - CFE_SB_MSG_TOO_BIG); - CFE_UtAssert_MSGID_EQ(MsgIdRtn, MsgId); - UtAssert_INT32_EQ(SizeRtn, Size); - - CFE_UtAssert_EVENTCOUNT(1); - - CFE_UtAssert_EVENTSENT(CFE_SB_MSG_TOO_BIG_EID); -} - -/* -** Test response to sending a message which has no subscribers -*/ -void Test_TransmitMsgValidate_NoSubscribers(void) -{ - CFE_SB_MsgId_t MsgId = SB_UT_TLM_MID; - CFE_SB_MsgId_t MsgIdRtn; - SB_UT_Test_Tlm_t TlmPkt; - CFE_MSG_Size_t Size = sizeof(TlmPkt); - CFE_MSG_Size_t SizeRtn = 0; - CFE_SBR_RouteId_t RouteIdRtn = CFE_SBR_INVALID_ROUTE_ID; - - memset(&TlmPkt, 0, sizeof(TlmPkt)); - - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetSize), &Size, sizeof(Size), false); - - CFE_UtAssert_SUCCESS( - CFE_SB_TransmitMsgValidate(CFE_MSG_PTR(TlmPkt.TelemetryHeader), &MsgIdRtn, &SizeRtn, &RouteIdRtn)); - CFE_UtAssert_MSGID_EQ(MsgIdRtn, MsgId); - UtAssert_INT32_EQ(SizeRtn, Size); - UtAssert_BOOL_FALSE(CFE_SBR_IsValidRouteId(RouteIdRtn)); - - CFE_UtAssert_EVENTCOUNT(1); - - CFE_UtAssert_EVENTSENT(CFE_SB_SEND_NO_SUBS_EID); -} - -/* -** Test response to sending a message which has an invalid Msg ID -*/ -void Test_TransmitMsgValidate_InvalidMsgId(void) -{ - CFE_SB_MsgId_t MsgId = CFE_SB_INVALID_MSG_ID; - CFE_SB_MsgId_t MsgIdRtn; - SB_UT_Test_Tlm_t TlmPkt; - CFE_MSG_Size_t SizeRtn; - CFE_SBR_RouteId_t RouteIdRtn; - - memset(&TlmPkt, 0, sizeof(TlmPkt)); - - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); - - UtAssert_INT32_EQ(CFE_SB_TransmitMsgValidate(CFE_MSG_PTR(TlmPkt.TelemetryHeader), &MsgIdRtn, &SizeRtn, &RouteIdRtn), - CFE_SB_BAD_ARGUMENT); - CFE_UtAssert_EVENTCOUNT(1); - CFE_UtAssert_EVENTSENT(CFE_SB_SEND_INV_MSGID_EID); + /* Return alternating false/true (this is needed to avoid an endless loop) */ + *IsAcceptable = (UT_GetStubCount(FuncKey) & 1) == 0; } /* @@ -3621,17 +4111,18 @@ void Test_ReceiveBuffer_API(void) static void SB_UT_PipeIdModifyHandler(void *UserObj, UT_EntryKey_t FuncKey, const UT_StubContext_t *Context) { - void * data = UT_Hook_GetArgValueByName(Context, "data", void *); + CFE_SB_BufferD_t ** OutData = UT_Hook_GetArgValueByName(Context, "data", void *); size_t * size_copied = UT_Hook_GetArgValueByName(Context, "size_copied", size_t *); int32 status; - static SB_UT_Test_Tlm_t FakeTlmPkt; - SB_UT_Test_Tlm_t ** OutData; + static CFE_SB_BufferD_t LocalBuf; CFE_SB_PipeD_t * PipeDscPtr = UserObj; - OutData = data; - *OutData = &FakeTlmPkt; - *size_copied = sizeof(*OutData); - status = OS_SUCCESS; + memset(&LocalBuf, 0, sizeof(LocalBuf)); + CFE_SB_TrackingListReset(&LocalBuf.Link); + LocalBuf.UseCount = 1; + *OutData = &LocalBuf; + *size_copied = sizeof(*OutData); + status = OS_SUCCESS; UT_Stub_SetReturnValue(FuncKey, status); /* Modify the PipeID so it fails to match */ @@ -3691,8 +4182,8 @@ void Test_ReceiveBuffer_InvalidPipeId(void) UT_SetHandlerFunction(UT_KEY(OS_QueueGet), SB_UT_PipeIdModifyHandler, PipeDscPtr); UtAssert_INT32_EQ(CFE_SB_ReceiveBuffer(&SBBufPtr, PipeId, CFE_SB_POLL), CFE_SB_PIPE_RD_ERR); CFE_UtAssert_EVENTSENT(CFE_SB_BAD_PIPEID_EID); - UtAssert_UINT8_EQ(CFE_SB_Global.HKTlmMsg.Payload.MsgReceiveErrorCounter, 2); - UtAssert_UINT8_EQ(CFE_SB_Global.HKTlmMsg.Payload.InternalErrorCounter, 0); + UtAssert_UINT8_EQ(CFE_SB_Global.HKTlmMsg.Payload.MsgReceiveErrorCounter, 1); + UtAssert_UINT8_EQ(CFE_SB_Global.HKTlmMsg.Payload.InternalErrorCounter, 1); UT_SetHandlerFunction(UT_KEY(OS_QueueGet), NULL, NULL); /* restore the PipeID so it can be deleted */ @@ -3784,7 +4275,7 @@ void Test_ReceiveBuffer_PipeReadError(void) UT_SetDeferredRetcode(UT_KEY(OS_QueueGet), 1, OS_ERROR); UtAssert_INT32_EQ(CFE_SB_ReceiveBuffer(&SBBufPtr, PipeId, CFE_SB_PEND_FOREVER), CFE_SB_PIPE_RD_ERR); - CFE_UtAssert_EVENTCOUNT(2); + CFE_UtAssert_EVENTCOUNT(3); CFE_UtAssert_EVENTSENT(CFE_SB_Q_RD_ERR_EID); @@ -4178,11 +4669,6 @@ void Test_SB_SpecialCases(void) SB_UT_ADD_SUBTEST(Test_PutDestBlk_ErrLogic); SB_UT_ADD_SUBTEST(Test_CFE_SB_Buffers); SB_UT_ADD_SUBTEST(Test_CFE_SB_BadPipeInfo); - SB_UT_ADD_SUBTEST(Test_SB_TransmitMsgPaths_Nominal); - SB_UT_ADD_SUBTEST(Test_SB_TransmitMsgPaths_LimitErr); - SB_UT_ADD_SUBTEST(Test_SB_TransmitMsgPaths_FullErr); - SB_UT_ADD_SUBTEST(Test_SB_TransmitMsgPaths_WriteErr); - SB_UT_ADD_SUBTEST(Test_SB_TransmitMsgPaths_IgnoreOpt); SB_UT_ADD_SUBTEST(Test_ReceiveBuffer_UnsubResubPath); SB_UT_ADD_SUBTEST(Test_MessageString); } @@ -4352,281 +4838,6 @@ void Test_CFE_SB_BadPipeInfo(void) CFE_UtAssert_TEARDOWN(CFE_SB_DeletePipe(PipeId)); } -/* -** Test send housekeeping information command -*/ -void Test_SB_TransmitMsgPaths_Nominal(void) -{ - union - { - CFE_SB_Buffer_t SBBuf; - CFE_SB_SendHkCmd_t SendHkCmd; - } Housekeeping; - CFE_SB_MsgId_t MsgId; - CFE_SB_PipeId_t PipeId = CFE_SB_INVALID_PIPE; - SB_UT_Test_Tlm_t TlmPkt; - int32 PipeDepth = 2; - CFE_MSG_Size_t Size; - CFE_MSG_Type_t Type; - - memset(&Housekeeping, 0, sizeof(Housekeeping)); - memset(&TlmPkt, 0, sizeof(TlmPkt)); - - /* Set up for dispatch FIRST */ - UT_SetupBasicMsgDispatch(&UT_TPID_CFE_SB_SEND_HK, sizeof(Housekeeping.SendHkCmd), false); - - /* For internal send message call */ - MsgId = CFE_SB_ValueToMsgId(CFE_SB_HK_TLM_MID); - Size = sizeof(CFE_SB_Global.HKTlmMsg); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetSize), &Size, sizeof(Size), false); - - /* Repress sending the no subscriptions event and process request */ - CFE_SB_Global.HKTlmMsg.Payload.NoSubscribersCounter = 0; - CFE_SB_Global.StopRecurseFlags[1] |= CFE_BIT(CFE_SB_SEND_NO_SUBS_EID_BIT); - CFE_SB_ProcessCmdPipePkt(&Housekeeping.SBBuf); - - /* The no subs event should not be in history but count should increment */ - CFE_UtAssert_EVENTNOTSENT(CFE_SB_SEND_NO_SUBS_EID); - UtAssert_INT32_EQ(CFE_SB_Global.HKTlmMsg.Payload.NoSubscribersCounter, 1); - - /* Repress get buffer error */ - CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter = 0; - CFE_SB_Global.StopRecurseFlags[1] |= CFE_BIT(CFE_SB_GET_BUF_ERR_EID_BIT); - - /* Set up for dispatch FIRST */ - UT_SetupBasicMsgDispatch(&UT_TPID_CFE_SB_SEND_HK, sizeof(Housekeeping.SendHkCmd), false); - - /* For internal send message call */ - MsgId = CFE_SB_ValueToMsgId(CFE_SB_HK_TLM_MID); - Size = sizeof(CFE_SB_Global.HKTlmMsg); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetSize), &Size, sizeof(Size), false); - - UT_SetDeferredRetcode(UT_KEY(CFE_ES_GetPoolBuf), 1, CFE_ES_ERR_MEM_BLOCK_SIZE); - CFE_SB_ProcessCmdPipePkt(&Housekeeping.SBBuf); - UtAssert_INT32_EQ(CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter, 0); - - CFE_UtAssert_EVENTNOTSENT(CFE_SB_GET_BUF_ERR_EID); - - CFE_UtAssert_EVENTCOUNT(0); - - CFE_SB_Global.StopRecurseFlags[1] = 0; - - /* Create a message ID with the command bit set and disable reporting */ - MsgId = SB_UT_CMD_MID; - Size = sizeof(TlmPkt); - Type = CFE_MSG_Type_Cmd; - CFE_UtAssert_SETUP(CFE_SB_CreatePipe(&PipeId, PipeDepth, "TestPipe")); - - /* Will fail because of deferred CFE_ES_GetPoolBuf failure return */ - UtAssert_INT32_EQ(CFE_SB_Subscribe(MsgId, PipeId), CFE_SB_BUF_ALOC_ERR); - - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetSize), &Size, sizeof(Size), false); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetType), &Type, sizeof(Type), false); - - CFE_UtAssert_SUCCESS(CFE_SB_TransmitMsg(CFE_MSG_PTR(TlmPkt.TelemetryHeader), true)); - - CFE_UtAssert_EVENTCOUNT(3); - - /* - * Test Additional paths within CFE_SB_TransmitMsgValidate that skip sending events to avoid a loop - * For all of these they should skip sending the event but still increment the MsgSendErrorCounter - */ - - /* CFE_SB_MSG_TOO_BIG_EID loop filter */ - CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter = 0; - Size = CFE_MISSION_SB_MAX_SB_MSG_SIZE + 1; - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetSize), &Size, sizeof(Size), false); - CFE_SB_Global.StopRecurseFlags[1] |= CFE_BIT(CFE_SB_MSG_TOO_BIG_EID_BIT); - CFE_SB_TransmitMsg(&Housekeeping.SBBuf.Msg, true); - CFE_UtAssert_EVENTNOTSENT(CFE_SB_MSG_TOO_BIG_EID); - UtAssert_UINT32_EQ(CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter, 1); - - /* CFE_SB_SEND_INV_MSGID_EID loop filter */ - CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter = 0; - MsgId = CFE_SB_INVALID_MSG_ID; - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); - CFE_SB_Global.StopRecurseFlags[1] |= CFE_BIT(CFE_SB_SEND_INV_MSGID_EID_BIT); - CFE_SB_TransmitMsg(&Housekeeping.SBBuf.Msg, true); - CFE_UtAssert_EVENTNOTSENT(CFE_SB_SEND_INV_MSGID_EID); - UtAssert_UINT32_EQ(CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter, 1); - - /* CFE_SB_SEND_BAD_ARG_EID loop filter */ - CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter = 0; - CFE_SB_Global.StopRecurseFlags[1] |= CFE_BIT(CFE_SB_SEND_BAD_ARG_EID_BIT); - CFE_SB_TransmitMsg(NULL, true); - CFE_UtAssert_EVENTNOTSENT(CFE_SB_SEND_BAD_ARG_EID); - UtAssert_UINT32_EQ(CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter, 1); - - CFE_UtAssert_TEARDOWN(CFE_SB_DeletePipe(PipeId)); -} - -void Test_SB_TransmitMsgPaths_LimitErr(void) -{ - CFE_SB_MsgId_t MsgId; - CFE_SB_PipeId_t PipeId = CFE_SB_INVALID_PIPE; - SB_UT_Test_Tlm_t TlmPkt; - int32 PipeDepth = 2; - CFE_MSG_Type_t Type = CFE_MSG_Type_Tlm; - CFE_MSG_Size_t Size = sizeof(TlmPkt); - - memset(&TlmPkt, 0, sizeof(TlmPkt)); - - /* Test inhibiting sending a "message ID limit error" message */ - MsgId = SB_UT_TLM_MID; - CFE_UtAssert_SETUP(CFE_SB_CreatePipe(&PipeId, PipeDepth, "MsgLimTestPipe")); - - /* Set maximum allowed messages on the pipe at one time to 1 */ - CFE_UtAssert_SETUP(CFE_SB_SubscribeEx(MsgId, PipeId, CFE_SB_DEFAULT_QOS, 1)); - - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetSize), &Size, sizeof(Size), false); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetType), &Type, sizeof(Type), false); - - /* First send should pass */ - CFE_UtAssert_SUCCESS(CFE_SB_TransmitMsg(CFE_MSG_PTR(TlmPkt.TelemetryHeader), true)); - - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetSize), &Size, sizeof(Size), false); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetType), &Type, sizeof(Type), false); - - CFE_SB_Global.StopRecurseFlags[1] |= CFE_BIT(CFE_SB_MSGID_LIM_ERR_EID_BIT); - CFE_UtAssert_SUCCESS(CFE_SB_TransmitMsg(CFE_MSG_PTR(TlmPkt.TelemetryHeader), true)); - CFE_SB_Global.StopRecurseFlags[1] = 0; - - CFE_UtAssert_EVENTNOTSENT(CFE_SB_MSGID_LIM_ERR_EID); - - CFE_UtAssert_TEARDOWN(CFE_SB_DeletePipe(PipeId)); -} - -void Test_SB_TransmitMsgPaths_FullErr(void) -{ - CFE_SB_MsgId_t MsgId; - CFE_SB_PipeId_t PipeId = CFE_SB_INVALID_PIPE; - SB_UT_Test_Tlm_t TlmPkt; - int32 PipeDepth = 2; - CFE_MSG_Type_t Type = CFE_MSG_Type_Tlm; - CFE_MSG_Size_t Size = sizeof(TlmPkt); - - memset(&TlmPkt, 0, sizeof(TlmPkt)); - - /* Test inhibiting sending a "pipe full" message */ - MsgId = SB_UT_TLM_MID; - CFE_UtAssert_SETUP(CFE_SB_CreatePipe(&PipeId, PipeDepth, "PipeFullTestPipe")); - CFE_UtAssert_SETUP(CFE_SB_Subscribe(MsgId, PipeId)); - - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetSize), &Size, sizeof(Size), false); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetType), &Type, sizeof(Type), false); - - /* This send should pass */ - CFE_UtAssert_SUCCESS(CFE_SB_TransmitMsg(CFE_MSG_PTR(TlmPkt.TelemetryHeader), true)); - - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetSize), &Size, sizeof(Size), false); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetType), &Type, sizeof(Type), false); - - /* Tell the QueuePut stub to return OS_QUEUE_FULL on its next call */ - UT_SetDeferredRetcode(UT_KEY(OS_QueuePut), 1, OS_QUEUE_FULL); - CFE_SB_Global.StopRecurseFlags[1] |= CFE_BIT(CFE_SB_Q_FULL_ERR_EID_BIT); - CFE_UtAssert_SUCCESS(CFE_SB_TransmitMsg(CFE_MSG_PTR(TlmPkt.TelemetryHeader), true)); - CFE_SB_Global.StopRecurseFlags[1] = 0; - - CFE_UtAssert_EVENTNOTSENT(CFE_SB_Q_FULL_ERR_EID_BIT); - - CFE_UtAssert_EVENTCOUNT(2); - - CFE_UtAssert_TEARDOWN(CFE_SB_DeletePipe(PipeId)); -} - -void Test_SB_TransmitMsgPaths_WriteErr(void) -{ - CFE_SB_MsgId_t MsgId; - CFE_SB_PipeId_t PipeId = CFE_SB_INVALID_PIPE; - SB_UT_Test_Tlm_t TlmPkt; - int32 PipeDepth = 2; - CFE_MSG_Type_t Type = CFE_MSG_Type_Tlm; - CFE_MSG_Size_t Size = sizeof(TlmPkt); - - memset(&TlmPkt, 0, sizeof(TlmPkt)); - - /* Test inhibiting sending a "pipe write error" message */ - MsgId = SB_UT_TLM_MID; - CFE_UtAssert_SETUP(CFE_SB_CreatePipe(&PipeId, PipeDepth, "TestPipe")); - CFE_UtAssert_SETUP(CFE_SB_Subscribe(MsgId, PipeId)); - UT_SetDeferredRetcode(UT_KEY(OS_QueuePut), 1, OS_ERROR); - - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetSize), &Size, sizeof(Size), false); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetType), &Type, sizeof(Type), false); - - CFE_SB_Global.StopRecurseFlags[1] |= CFE_BIT(CFE_SB_Q_WR_ERR_EID_BIT); - CFE_UtAssert_SUCCESS(CFE_SB_TransmitMsg(CFE_MSG_PTR(TlmPkt.TelemetryHeader), true)); - CFE_SB_Global.StopRecurseFlags[1] = 0; - - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetSize), &Size, sizeof(Size), false); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetType), &Type, sizeof(Type), false); - - CFE_UtAssert_SUCCESS(CFE_SB_TransmitMsg(CFE_MSG_PTR(TlmPkt.TelemetryHeader), true)); - - CFE_UtAssert_EVENTCOUNT(2); - - CFE_UtAssert_EVENTNOTSENT(CFE_SB_Q_WR_ERR_EID); - - CFE_UtAssert_TEARDOWN(CFE_SB_DeletePipe(PipeId)); -} - -void Test_SB_TransmitMsgPaths_IgnoreOpt(void) -{ - CFE_SB_MsgId_t MsgId; - CFE_SB_PipeId_t PipeId = CFE_SB_INVALID_PIPE; - SB_UT_Test_Tlm_t TlmPkt; - int32 PipeDepth = 2; - CFE_MSG_Type_t Type = CFE_MSG_Type_Tlm; - CFE_MSG_Size_t Size = sizeof(TlmPkt); - CFE_SB_PipeD_t * PipeDscPtr; - CFE_ES_AppId_t AppId; - - memset(&TlmPkt, 0, sizeof(TlmPkt)); - - /* Setup Test skipping sending to a pipe when the pipe option is set to ignore */ - MsgId = SB_UT_TLM_MID; - CFE_UtAssert_SETUP(CFE_SB_CreatePipe(&PipeId, PipeDepth, "SkipPipe")); - CFE_UtAssert_SETUP(CFE_SB_Subscribe(MsgId, PipeId)); - CFE_UtAssert_SETUP(CFE_SB_SetPipeOpts(PipeId, CFE_SB_PIPEOPTS_IGNOREMINE)); - - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetSize), &Size, sizeof(Size), false); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetType), &Type, sizeof(Type), false); - - /* Test skipping this pipe and the send should pass */ - CFE_UtAssert_SUCCESS(CFE_SB_TransmitMsg(CFE_MSG_PTR(TlmPkt.TelemetryHeader), true)); - UtAssert_STUB_COUNT(OS_QueuePut, 0); - - /* Set up and send again without matching ApId and it should transmit */ - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetSize), &Size, sizeof(Size), false); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetType), &Type, sizeof(Type), false); - PipeDscPtr = CFE_SB_LocatePipeDescByID(PipeId); - AppId = PipeDscPtr->AppId; - PipeDscPtr->AppId = CFE_ES_APPID_UNDEFINED; - - /* Also hit case where not the peak depth */ - PipeDscPtr->PeakQueueDepth += 2; - CFE_UtAssert_SUCCESS(CFE_SB_TransmitMsg(CFE_MSG_PTR(TlmPkt.TelemetryHeader), true)); - UtAssert_STUB_COUNT(OS_QueuePut, 1); - - /* Set AppId back so it can be deleted */ - PipeDscPtr->AppId = AppId; - - CFE_UtAssert_TEARDOWN(CFE_SB_SetPipeOpts(PipeId, 0)); - CFE_UtAssert_TEARDOWN(CFE_SB_DeletePipe(PipeId)); -} - /* ** Test receiving a message response to an unsubscribing to message, then ** resubscribing to it while it's in the pipe diff --git a/modules/sb/ut-coverage/sb_UT.h b/modules/sb/ut-coverage/sb_UT.h index 45e15fcf3..ed75ccb96 100644 --- a/modules/sb/ut-coverage/sb_UT.h +++ b/modules/sb/ut-coverage/sb_UT.h @@ -2047,7 +2047,7 @@ void Test_TransmitMsg_DisabledDestination(void); /*****************************************************************************/ /** -** \brief Test CFE_SB_BroadcastBufferToRoute +** \brief Test CFE_SB_TransmitTxn_BroadcastToRoute ** ** \par Description ** This function tests broadcasting a message buffer with the metadata. @@ -2058,23 +2058,7 @@ void Test_TransmitMsg_DisabledDestination(void); ** \returns ** This function does not return a value. ******************************************************************************/ -void Test_BroadcastBufferToRoute(void); - -/*****************************************************************************/ -/** -** \brief Test response to sending a message which has no subscribers -** -** \par Description -** This function tests the response to sending a message which has no -** subscribers. -** -** \par Assumptions, External Events, and Notes: -** None -** -** \returns -** This function does not return a value. -******************************************************************************/ -void Test_TransmitMsgValidate_NoSubscribers(void); +void Test_TransmitTxn_PipeHandler(void); /*****************************************************************************/ /** @@ -2090,7 +2074,7 @@ void Test_TransmitMsgValidate_NoSubscribers(void); ** \returns ** This function does not return a value. ******************************************************************************/ -void Test_TransmitMsgValidate_InvalidMsgId(void); +void Test_TransmitTxn_SetupFromMsg_InvalidMsgId(void); /*****************************************************************************/ /** @@ -2107,7 +2091,7 @@ void Test_TransmitMsgValidate_InvalidMsgId(void); ** \returns ** This function does not return a value. ******************************************************************************/ -void Test_TransmitMsgValidate_MaxMsgSizePlusOne(void); +void Test_TransmitTxn_SetupFromMsg_MaxMsgSizePlusOne(void); /*****************************************************************************/ /** @@ -2457,25 +2441,6 @@ void Test_CFE_SB_Buffers(void); ******************************************************************************/ void Test_CFE_SB_BadPipeInfo(void); -/*****************************************************************************/ -/** -** \brief Test TransmitMsgFull function paths -** -** \par Description -** This function tests branch paths in the TransmitMsgFull function. -** -** \par Assumptions, External Events, and Notes: -** None -** -** \returns -** This function does not return a value. -******************************************************************************/ -void Test_SB_TransmitMsgPaths_Nominal(void); -void Test_SB_TransmitMsgPaths_LimitErr(void); -void Test_SB_TransmitMsgPaths_FullErr(void); -void Test_SB_TransmitMsgPaths_WriteErr(void); -void Test_SB_TransmitMsgPaths_IgnoreOpt(void); - /*****************************************************************************/ /** ** \brief Test ReceiveBuffer function unsubscribe/resubscribe path