From f3a09810d3ee094b6b265a6ced530642c7ab9b7c Mon Sep 17 00:00:00 2001 From: Isaac Rowe <9010221+irowebbn@users.noreply.github.com> Date: Fri, 21 Jul 2023 09:51:33 -0500 Subject: [PATCH 1/7] Fix #2407 Add perfid limit info to ES docstring --- modules/core_api/fsw/inc/cfe_es.h | 6 +++++- modules/es/config/default_cfe_es_interface_cfg.h | 9 +++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/modules/core_api/fsw/inc/cfe_es.h b/modules/core_api/fsw/inc/cfe_es.h index cac2c8f03..fcc91ab0e 100644 --- a/modules/core_api/fsw/inc/cfe_es.h +++ b/modules/core_api/fsw/inc/cfe_es.h @@ -1501,7 +1501,11 @@ CFE_Status_t CFE_ES_GetMemPoolStats(CFE_ES_MemPoolStats_t *BufPtr, CFE_ES_MemHan ** tool (see section 5.15). ** ** \par Assumptions, External Events, and Notes: - +** +** \c Marker limited to the range of 0 to +** #CFE_MISSION_ES_PERF_MAX_IDS - 1. Any performance ids outside of this +** range will be ignored and will be flagged as an error. +** ** This function implements a circular buffer using an array. ** DataStart points to first stored entry ** DataEnd points to next available entry diff --git a/modules/es/config/default_cfe_es_interface_cfg.h b/modules/es/config/default_cfe_es_interface_cfg.h index 04f833187..3786beeb3 100644 --- a/modules/es/config/default_cfe_es_interface_cfg.h +++ b/modules/es/config/default_cfe_es_interface_cfg.h @@ -52,9 +52,14 @@ ** \cfeescfg Define Max Number of Performance IDs for messages ** ** \par Description: -** Defines the maximum number of perf ids allowed in command/telemetry messages +** Defines the maximum number of perf ids allowed. ** -** This affects the layout of command/telemetry messages but does not affect run +** Each performance id is used to identify something that needs to be +** measured. Performance ids are limited to the range of 0 to +** #CFE_MISSION_ES_PERF_MAX_IDS - 1. Any performance ids outside of this +** range will be ignored and will be flagged as an error. +** +** This affects the layout of telemetry messages but does not affect run ** time behavior or internal allocation. ** ** \par Limits From 0345296664ba8a6c6ea2cf2606e355813c041790 Mon Sep 17 00:00:00 2001 From: Joseph Hickey Date: Fri, 17 Nov 2023 13:31:29 -0500 Subject: [PATCH 2/7] Fix #2469, change workflow to use output on failure option Instead of using "make test" target, call ctest directly and pass the --output-on-failure option. If a test fails, then the workflow log will contain the details of the test --- .github/workflows/code-coverage.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml index 45153fd92..479bb03c4 100644 --- a/.github/workflows/code-coverage.yml +++ b/.github/workflows/code-coverage.yml @@ -83,18 +83,18 @@ jobs: - name: Test run: | lcov --capture --initial --directory build --output-file coverage_base.info - make -C build/native/default_cpu1/config test - make -C build/native/default_cpu1/core_api test - make -C build/native/default_cpu1/core_private test - make -C build/native/default_cpu1/es test - make -C build/native/default_cpu1/evs test - make -C build/native/default_cpu1/fs test - make -C build/native/default_cpu1/msg test - make -C build/native/default_cpu1/resourceid test - make -C build/native/default_cpu1/sb test - make -C build/native/default_cpu1/sbr test - make -C build/native/default_cpu1/tbl test - make -C build/native/default_cpu1/time test + (cd build/native/default_cpu1/config && ctest --output-on-failure) + (cd build/native/default_cpu1/core_api && ctest --output-on-failure) + (cd build/native/default_cpu1/core_private && ctest --output-on-failure) + (cd build/native/default_cpu1/es && ctest --output-on-failure) + (cd build/native/default_cpu1/evs && ctest --output-on-failure) + (cd build/native/default_cpu1/fs && ctest --output-on-failure) + (cd build/native/default_cpu1/msg && ctest --output-on-failure) + (cd build/native/default_cpu1/resourceid && ctest --output-on-failure) + (cd build/native/default_cpu1/sb && ctest --output-on-failure) + (cd build/native/default_cpu1/sbr && ctest --output-on-failure) + (cd build/native/default_cpu1/tbl && ctest --output-on-failure) + (cd build/native/default_cpu1/time && ctest --output-on-failure) - name: Calculate Coverage run: | From 4a4ada462aff10f6237d50f920754f34a730a6f9 Mon Sep 17 00:00:00 2001 From: Joseph Hickey Date: Thu, 16 Nov 2023 16:19:34 -0500 Subject: [PATCH 3/7] Fix #2467, message integrity API Adds two new APIs to the MSG module related to integrity: CFE_MSG_OriginationAction CFE_MSG_VerificationAction These should perform all calculations/updates on origination and all verifcation/checks at termination of the message path, respectively. The specific set of actions depends on how the user has implemented their particular MSG module. By default, the traditional CFE encapsulation sets a checksum on commands and a timestamp on TLM, and no verification at the receiver. --- modules/cfe_testcase/src/sb_sendrecv_test.c | 2 +- modules/core_api/fsw/inc/cfe_msg.h | 73 +++++++++------ modules/core_api/fsw/inc/cfe_sb.h | 35 ++++--- .../core_api/ut-stubs/src/cfe_msg_handlers.c | 32 +++++++ modules/core_api/ut-stubs/src/cfe_msg_stubs.c | 49 +++++++--- modules/core_api/ut-stubs/src/cfe_sb_stubs.c | 8 +- modules/msg/CMakeLists.txt | 2 +- modules/msg/fsw/src/cfe_msg_init.c | 31 ------- .../{cfe_msg_verify.c => cfe_msg_integrity.c} | 38 +++++++- modules/msg/ut-coverage/CMakeLists.txt | 2 +- modules/msg/ut-coverage/msg_UT.c | 4 +- modules/msg/ut-coverage/test_cfe_msg_init.c | 37 -------- modules/msg/ut-coverage/test_cfe_msg_init.h | 2 +- .../msg/ut-coverage/test_cfe_msg_integrity.c | 91 +++++++++++++++++++ modules/msg/ut-coverage/test_cfe_msg_verify.c | 59 ------------ modules/msg/ut-coverage/test_cfe_msg_verify.h | 2 +- modules/sb/fsw/inc/cfe_sb_eventids.h | 25 +++++ modules/sb/fsw/src/cfe_sb_api.c | 17 ++-- modules/sb/ut-coverage/sb_UT.c | 23 +++-- 19 files changed, 321 insertions(+), 211 deletions(-) rename modules/msg/fsw/src/{cfe_msg_verify.c => cfe_msg_integrity.c} (58%) create mode 100644 modules/msg/ut-coverage/test_cfe_msg_integrity.c delete mode 100644 modules/msg/ut-coverage/test_cfe_msg_verify.c 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..bc4280ece 100644 --- a/modules/sb/fsw/src/cfe_sb_api.c +++ b/modules/sb/fsw/src/cfe_sb_api.c @@ -123,7 +123,8 @@ CFE_Status_t CFE_SB_CreatePipe(CFE_SB_PipeId_t *PipeIdPtr, uint16 Depth, const c CFE_ES_GetTaskID(&TskId); /* check input parameters */ - if ((PipeIdPtr == NULL) || (Depth > OS_QUEUE_MAX_DEPTH) || (Depth == 0) || (PipeName != NULL && PipeName[0] == '\0')) + if ((PipeIdPtr == NULL) || (Depth > OS_QUEUE_MAX_DEPTH) || (Depth == 0) || + (PipeName != NULL && PipeName[0] == '\0')) { PendingEventId = CFE_SB_CR_PIPE_BAD_ARG_EID; Status = CFE_SB_BAD_ARGUMENT; @@ -1287,7 +1288,7 @@ int32 CFE_SB_UnsubscribeFull(CFE_SB_MsgId_t MsgId, CFE_SB_PipeId_t PipeId, uint8 * See description in header file for argument/return detail * *-----------------------------------------------------------------*/ -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) { int32 Status; CFE_MSG_Size_t Size = 0; @@ -1349,7 +1350,7 @@ CFE_Status_t CFE_SB_TransmitMsg(const CFE_MSG_Message_t *MsgPtr, bool UpdateHead memcpy(&BufDscPtr->Content, MsgPtr, Size); BufDscPtr->MsgId = MsgId; BufDscPtr->ContentSize = Size; - BufDscPtr->NeedsUpdate = UpdateHeader; + BufDscPtr->NeedsUpdate = IsOrigination; CFE_MSG_GetType(MsgPtr, &BufDscPtr->ContentType); /* @@ -1535,8 +1536,10 @@ void CFE_SB_BroadcastBufferToRoute(CFE_SB_BufferD_t *BufDscPtr, CFE_SBR_RouteId_ uint32 i; char FullName[(OS_MAX_API_NAME * 2)]; char PipeName[OS_MAX_API_NAME]; + bool IsAcceptable; SBSndErr.EvtsToSnd = 0; + IsAcceptable = true; /* get app id for loopback testing */ CFE_ES_GetAppID(&AppId); @@ -1555,8 +1558,10 @@ void CFE_SB_BroadcastBufferToRoute(CFE_SB_BufferD_t *BufDscPtr, CFE_SBR_RouteId_ { CFE_SBR_IncrementSequenceCounter(RouteId); + CFE_MSG_SetSequenceCount(&BufDscPtr->Content.Msg, CFE_SBR_GetSequenceCounter(RouteId)); + /* Update all MSG headers based on the current sequence */ - CFE_MSG_UpdateHeader(&BufDscPtr->Content.Msg, CFE_SBR_GetSequenceCounter(RouteId)); + CFE_MSG_OriginationAction(&BufDscPtr->Content.Msg, BufDscPtr->ContentSize, &IsAcceptable); /* Clear the flag, just in case */ BufDscPtr->NeedsUpdate = false; @@ -2118,7 +2123,7 @@ 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_TransmitBuffer(CFE_SB_Buffer_t *BufPtr, bool IsOrigination) { int32 Status; CFE_SB_BufferD_t *BufDscPtr; @@ -2143,7 +2148,7 @@ CFE_Status_t CFE_SB_TransmitBuffer(CFE_SB_Buffer_t *BufPtr, bool UpdateHeader) */ if (Status == CFE_SUCCESS) { - BufDscPtr->NeedsUpdate = UpdateHeader; + BufDscPtr->NeedsUpdate = IsOrigination; CFE_MSG_GetType(&BufPtr->Msg, &BufDscPtr->ContentType); /* Now broadcast the message, which consumes the buffer */ diff --git a/modules/sb/ut-coverage/sb_UT.c b/modules/sb/ut-coverage/sb_UT.c index b31faa4e5..72df5a6b3 100644 --- a/modules/sb/ut-coverage/sb_UT.c +++ b/modules/sb/ut-coverage/sb_UT.c @@ -3015,8 +3015,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 +3029,8 @@ 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_OriginationAction, 1); UtAssert_STUB_COUNT(CFE_MSG_GetNextSequenceCount, 1); UtAssert_INT32_EQ(SeqCnt, SeqCntExpected); @@ -3039,8 +3040,9 @@ void Test_TransmitMsg_UpdateHeader(void) CFE_UtAssert_SUCCESS(CFE_SB_TransmitMsg(CFE_MSG_PTR(TlmPkt.TelemetryHeader), false)); /* Assert sequence count wasn't set */ + UtAssert_STUB_COUNT(CFE_MSG_SetSequenceCount, 1); UtAssert_STUB_COUNT(CFE_MSG_GetNextSequenceCount, 1); - UtAssert_STUB_COUNT(CFE_MSG_UpdateHeader, 1); + UtAssert_STUB_COUNT(CFE_MSG_OriginationAction, 1); SeqCntExpected = 2; UT_SetDefaultReturnValue(UT_KEY(CFE_MSG_GetNextSequenceCount), SeqCntExpected); @@ -3049,7 +3051,8 @@ 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_OriginationAction, 2); UtAssert_STUB_COUNT(CFE_MSG_GetNextSequenceCount, 2); CFE_UtAssert_EVENTCOUNT(2); @@ -3063,7 +3066,8 @@ 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_OriginationAction, 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 +3079,8 @@ 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_OriginationAction, 4); UtAssert_STUB_COUNT(CFE_MSG_GetNextSequenceCount, 4); CFE_UtAssert_TEARDOWN(CFE_SB_DeletePipe(PipeId)); @@ -3326,8 +3331,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")); From c83a193805d9f5d040830c29f8777a6d2c66b0d3 Mon Sep 17 00:00:00 2001 From: Joseph Hickey Date: Fri, 17 Nov 2023 11:59:29 -0500 Subject: [PATCH 4/7] Fix #2465, add multi threaded SB perf tests Add multi-thread variants of the SB bulk transfer tests. The intent is to create some contention on the SB shared data mutex between the threads. This more closely resembles real use cases where different threads are doing sending and receving simultaneously. --- .../cfe_testcase/src/sb_performance_test.c | 485 +++++++++++++++++- 1 file changed, 479 insertions(+), 6 deletions(-) diff --git a/modules/cfe_testcase/src/sb_performance_test.c b/modules/cfe_testcase/src/sb_performance_test.c index 25f86ef44..c24e9f61d 100644 --- a/modules/cfe_testcase/src/sb_performance_test.c +++ b/modules/cfe_testcase/src/sb_performance_test.c @@ -33,6 +33,9 @@ #include "cfe_msgids.h" #include "cfe_test_msgids.h" +/* Number of messages to send during test */ +uint32_t UT_BulkTestDuration = 1000; + /* A simple command message */ typedef struct { @@ -47,6 +50,26 @@ typedef struct uint32 TlmPayload; } CFE_FT_TestTlmMessage_t; +/* State structure for multicore test - shared between threads */ +typedef struct UT_BulkMultiCoreSharedState +{ + volatile bool XmitFinished; + volatile bool RecvFinished; + + CFE_ES_TaskId_t TaskIdXmit; + CFE_ES_TaskId_t TaskIdRecv; + CFE_SB_PipeId_t PipeId; + osal_id_t SyncSem; + uint32 SendCount; + uint32 RecvCount; + OS_time_t StartTime; + OS_time_t EndTime; + +} UT_BulkMultiCoreSharedState_t; + +UT_BulkMultiCoreSharedState_t BulkCmd; +UT_BulkMultiCoreSharedState_t BulkTlm; + /* * This test procedure should be agnostic to specific MID values, but it should * not overlap/interfere with real MIDs used by other apps. @@ -54,7 +77,7 @@ typedef struct static const CFE_SB_MsgId_t CFE_FT_CMD_MSGID = CFE_SB_MSGID_WRAP_VALUE(CFE_TEST_CMD_MID); static const CFE_SB_MsgId_t CFE_FT_TLM_MSGID = CFE_SB_MSGID_WRAP_VALUE(CFE_TEST_HK_TLM_MID); -void TestBulkTransmitRecv(void) +void TestBulkTransferSingle(void) { CFE_SB_PipeId_t PipeId1 = CFE_SB_INVALID_PIPE; CFE_SB_PipeId_t PipeId2 = CFE_SB_INVALID_PIPE; @@ -66,12 +89,20 @@ void TestBulkTransmitRecv(void) uint32 SendCount; OS_time_t StartTime; OS_time_t ElapsedTime; + int64 AvgRate; + uint32_t PrintMask; memset(&CmdMsg, 0, sizeof(CmdMsg)); memset(&TlmMsg, 0, sizeof(TlmMsg)); - UtPrintf("Testing: Bulk SB Transmit/Receive"); - CFE_PSP_GetTime(&StartTime); + PrintMask = UT_BulkTestDuration >> 4; + PrintMask |= PrintMask >> 1; + PrintMask |= PrintMask >> 2; + PrintMask |= PrintMask >> 4; + PrintMask |= PrintMask >> 8; + PrintMask |= PrintMask >> 16; + + UtPrintf("Testing: Single Threaded Bulk SB Transmit/Receive"); /* Setup, create a pipe and subscribe (one cmd, one tlm) */ UtAssert_INT32_EQ(CFE_SB_CreatePipe(&PipeId1, 5, "TestPipe1"), CFE_SUCCESS); @@ -83,7 +114,9 @@ void TestBulkTransmitRecv(void) UtAssert_INT32_EQ(CFE_MSG_Init(CFE_MSG_PTR(CmdMsg.CommandHeader), CFE_FT_CMD_MSGID, sizeof(CmdMsg)), CFE_SUCCESS); UtAssert_INT32_EQ(CFE_MSG_Init(CFE_MSG_PTR(TlmMsg.TelemetryHeader), CFE_FT_TLM_MSGID, sizeof(TlmMsg)), CFE_SUCCESS); - for (SendCount = 0; SendCount < 1000000; ++SendCount) + CFE_PSP_GetTime(&StartTime); + + for (SendCount = 0; SendCount < UT_BulkTestDuration; ++SendCount) { CmdMsg.CmdPayload = SendCount; TlmMsg.TlmPayload = ~SendCount; @@ -129,7 +162,7 @@ void TestBulkTransmitRecv(void) } /* report progress periodically */ - if ((SendCount % 50000) == 0) + if ((SendCount & PrintMask) == 0) { UtPrintf("Success after %lu messages", (unsigned long)SendCount); } @@ -140,9 +173,449 @@ void TestBulkTransmitRecv(void) UtAssert_MIR("Elapsed time for SB bulk message test: %lu usec", (unsigned long)OS_TimeGetTotalMicroseconds(ElapsedTime)); + + /* Note that in this test, each iteration sends and recvs TWO messages */ + AvgRate = OS_TimeGetTotalMilliseconds(ElapsedTime); + AvgRate = ((int64)SendCount * 20000) / AvgRate; + + UtAssert_MIR("Message Rate: %ld.%01ld messages/sec", (long)(AvgRate / 10), (long)(AvgRate % 10)); + + UtAssert_INT32_EQ(CFE_SB_DeletePipe(PipeId1), CFE_SUCCESS); + UtAssert_INT32_EQ(CFE_SB_DeletePipe(PipeId2), CFE_SUCCESS); +} + +void RunSingleCmdSendRecv(void) +{ + CFE_FT_TestCmdMessage_t CmdMsg; + CFE_SB_Buffer_t * MsgBuf; + const CFE_FT_TestCmdMessage_t *CmdPtr; + + UtAssert_INT32_EQ(CFE_MSG_Init(CFE_MSG_PTR(CmdMsg.CommandHeader), CFE_FT_CMD_MSGID, sizeof(CmdMsg)), CFE_SUCCESS); + + CFE_PSP_GetTime(&BulkCmd.StartTime); + + while (BulkCmd.SendCount < UT_BulkTestDuration) + { + CmdMsg.CmdPayload = BulkCmd.SendCount; + + /* In order to not "flood" with test results, this should be silent unless a failure occurs */ + CFE_Assert_STATUS_STORE(CFE_SB_TransmitMsg(CFE_MSG_PTR(CmdMsg.CommandHeader), true)); + if (!CFE_Assert_STATUS_SILENTCHECK(CFE_SUCCESS)) + { + break; + } + + ++BulkCmd.SendCount; + + CFE_Assert_STATUS_STORE(CFE_SB_ReceiveBuffer(&MsgBuf, BulkCmd.PipeId, CFE_SB_POLL)); + if (!CFE_Assert_STATUS_SILENTCHECK(CFE_SUCCESS)) + { + break; + } + + ++BulkCmd.RecvCount; + + /* As above, to avoid flooding of test cases, only report mismatch here */ + CmdPtr = (const void *)MsgBuf; + if (CmdPtr->CmdPayload != CmdMsg.CmdPayload) + { + UtAssert_UINT32_EQ(CmdPtr->CmdPayload, CmdMsg.CmdPayload); + break; + } + } + + CFE_PSP_GetTime(&BulkCmd.EndTime); + + BulkCmd.XmitFinished = true; + BulkCmd.RecvFinished = true; +} + +void RunSingleTlmSendRecv(void) +{ + CFE_FT_TestTlmMessage_t TlmMsg; + CFE_SB_Buffer_t * MsgBuf; + const CFE_FT_TestTlmMessage_t *TlmPtr; + + UtAssert_INT32_EQ(CFE_MSG_Init(CFE_MSG_PTR(TlmMsg.TelemetryHeader), CFE_FT_TLM_MSGID, sizeof(TlmMsg)), CFE_SUCCESS); + + CFE_PSP_GetTime(&BulkTlm.StartTime); + + while (BulkTlm.SendCount < UT_BulkTestDuration) + { + TlmMsg.TlmPayload = BulkTlm.SendCount; + + /* In order to not "flood" with test results, this should be silent unless a failure occurs */ + CFE_Assert_STATUS_STORE(CFE_SB_TransmitMsg(CFE_MSG_PTR(TlmMsg.TelemetryHeader), true)); + if (!CFE_Assert_STATUS_SILENTCHECK(CFE_SUCCESS)) + { + break; + } + + ++BulkTlm.SendCount; + + CFE_Assert_STATUS_STORE(CFE_SB_ReceiveBuffer(&MsgBuf, BulkTlm.PipeId, CFE_SB_POLL)); + if (!CFE_Assert_STATUS_SILENTCHECK(CFE_SUCCESS)) + { + break; + } + + ++BulkTlm.RecvCount; + + /* As above, to avoid flooding of test cases, only report mismatch here */ + TlmPtr = (const void *)MsgBuf; + if (TlmPtr->TlmPayload != TlmMsg.TlmPayload) + { + UtAssert_UINT32_EQ(TlmPtr->TlmPayload, TlmMsg.TlmPayload); + break; + } + } + + CFE_PSP_GetTime(&BulkTlm.EndTime); + + BulkTlm.XmitFinished = true; + BulkTlm.RecvFinished = true; +} + +OS_time_t UT_GetTotalElapsedTime(void) +{ + OS_time_t StartTime; + OS_time_t EndTime; + + /* + * Computing the average message rate is the total number of messages passed in ALL threads, + * divided by the time elapsed by ALL threads, which is the difference between the first + * thread to start and the last thread to finish. + * + * Note that the two threads have equal priority and no blocking/yielding in either, so it is + * possible based on OS scheduler that one thread runs entirely to completion before the second + * thread even starts running. + */ + if (OS_TimeGetTotalNanoseconds(OS_TimeSubtract(BulkCmd.StartTime, BulkTlm.StartTime)) < 0) + { + /* This means BulkCmd started first */ + StartTime = BulkCmd.StartTime; + } + else + { + /* This means BulkTlm started first */ + StartTime = BulkTlm.StartTime; + } + + if (OS_TimeGetTotalNanoseconds(OS_TimeSubtract(BulkCmd.EndTime, BulkTlm.EndTime)) < 0) + { + /* This means BulkTlm ended last */ + EndTime = BulkTlm.EndTime; + } + else + { + /* This means BulkCmd ended last */ + EndTime = BulkCmd.EndTime; + } + + return OS_TimeSubtract(EndTime, StartTime); +} + +void TestBulkTransferMulti2(void) +{ + OS_time_t ElapsedTime; + int64 AvgRate; + + UtPrintf("Testing: 2 Thread Bulk SB Transmit/Receive without Sync Sem"); + memset(&BulkCmd, 0, sizeof(BulkCmd)); + memset(&BulkTlm, 0, sizeof(BulkCmd)); + + /* Setup, create a pipe and subscribe (one cmd, one tlm) */ + UtAssert_INT32_EQ(CFE_SB_CreatePipe(&BulkCmd.PipeId, 5, "CmdPipe"), CFE_SUCCESS); + UtAssert_INT32_EQ(CFE_SB_CreatePipe(&BulkTlm.PipeId, 5, "TlmPipe"), CFE_SUCCESS); + UtAssert_INT32_EQ(CFE_SB_SubscribeEx(CFE_FT_CMD_MSGID, BulkCmd.PipeId, CFE_SB_DEFAULT_QOS, 3), CFE_SUCCESS); + UtAssert_INT32_EQ(CFE_SB_SubscribeEx(CFE_FT_TLM_MSGID, BulkTlm.PipeId, CFE_SB_DEFAULT_QOS, 3), CFE_SUCCESS); + + UtAssert_INT32_EQ(CFE_ES_CreateChildTask(&BulkCmd.TaskIdXmit, "CmdXmit", RunSingleCmdSendRecv, NULL, 32768, 150, 0), + CFE_SUCCESS); + UtAssert_INT32_EQ(CFE_ES_CreateChildTask(&BulkTlm.TaskIdXmit, "TlmXmit", RunSingleTlmSendRecv, NULL, 32768, 150, 0), + CFE_SUCCESS); + + do + { + OS_TaskDelay(1000); + UtPrintf("Counts => %lu/%lu CMD, %lu/%lu TLM", (unsigned long)BulkCmd.SendCount, + (unsigned long)BulkCmd.RecvCount, (unsigned long)BulkTlm.SendCount, (unsigned long)BulkTlm.RecvCount); + } while (!BulkCmd.XmitFinished || !BulkCmd.RecvFinished || !BulkTlm.XmitFinished || !BulkTlm.RecvFinished); + + ElapsedTime = OS_TimeSubtract(BulkCmd.EndTime, BulkCmd.StartTime); + UtAssert_MIR("Elapsed time for SB bulk CMD thread: %lu usec", + (unsigned long)OS_TimeGetTotalMicroseconds(ElapsedTime)); + + ElapsedTime = OS_TimeSubtract(BulkTlm.EndTime, BulkTlm.StartTime); + UtAssert_MIR("Elapsed time for SB bulk TLM thread: %lu usec", + (unsigned long)OS_TimeGetTotalMicroseconds(ElapsedTime)); + + ElapsedTime = UT_GetTotalElapsedTime(); + UtAssert_MIR("Elapsed time for all threads: %lu usec", (unsigned long)OS_TimeGetTotalMicroseconds(ElapsedTime)); + + /* The message rate is the total number of messages processed over the total time elapsed */ + AvgRate = OS_TimeGetTotalMilliseconds(ElapsedTime); + if (AvgRate > 0) + { + AvgRate = ((int64)(BulkCmd.RecvCount + BulkTlm.RecvCount) * 10000) / AvgRate; + UtAssert_MIR("Message Rate: %ld.%01ld messages/sec", (long)(AvgRate / 10), (long)(AvgRate % 10)); + } + else + { + /* If the entire test took less than a millisecond, then there is a config error */ + UtAssert_Failed("Test configuration error: Executed too fast, needs more cycles!"); + } + + UtAssert_INT32_EQ(CFE_SB_DeletePipe(BulkCmd.PipeId), CFE_SUCCESS); + UtAssert_INT32_EQ(CFE_SB_DeletePipe(BulkTlm.PipeId), CFE_SUCCESS); +} + +void UT_CommandTransmitterTask(void) +{ + CFE_SB_Buffer_t * BufPtr; + CFE_FT_TestCmdMessage_t *CmdMsgPtr; + + CFE_PSP_GetTime(&BulkCmd.StartTime); + + for (BulkCmd.SendCount = 0; BulkCmd.SendCount < UT_BulkTestDuration; ++BulkCmd.SendCount) + { + CFE_Assert_STATUS_STORE(OS_CountSemTake(BulkCmd.SyncSem)); + if (!CFE_Assert_STATUS_SILENTCHECK(OS_SUCCESS)) + { + CFE_Assert_STATUS_MUST_BE(OS_SUCCESS); + break; + } + + BufPtr = CFE_SB_AllocateMessageBuffer(sizeof(CFE_FT_TestCmdMessage_t)); + + CmdMsgPtr = (void *)&BufPtr->Msg; + + /* Initialize the message content */ + CFE_MSG_Init(CFE_MSG_PTR(CmdMsgPtr->CommandHeader), CFE_FT_CMD_MSGID, sizeof(*CmdMsgPtr)); + + CmdMsgPtr->CmdPayload = BulkCmd.SendCount; + + CFE_Assert_STATUS_STORE(CFE_SB_TransmitBuffer(BufPtr, true)); + if (!CFE_Assert_STATUS_SILENTCHECK(CFE_SUCCESS)) + { + CFE_Assert_STATUS_MUST_BE(CFE_SUCCESS); + break; + } + } + + BulkCmd.XmitFinished = true; +} + +void UT_TelemtryTransmitterTask(void) +{ + CFE_SB_Buffer_t * BufPtr; + CFE_FT_TestTlmMessage_t *TlmMsgPtr; + + CFE_PSP_GetTime(&BulkTlm.StartTime); + + for (BulkTlm.SendCount = 0; BulkTlm.SendCount < UT_BulkTestDuration; ++BulkTlm.SendCount) + { + CFE_Assert_STATUS_STORE(OS_CountSemTake(BulkTlm.SyncSem)); + if (!CFE_Assert_STATUS_SILENTCHECK(OS_SUCCESS)) + { + CFE_Assert_STATUS_MUST_BE(OS_SUCCESS); + break; + } + + BufPtr = CFE_SB_AllocateMessageBuffer(sizeof(CFE_FT_TestTlmMessage_t)); + + TlmMsgPtr = (void *)&BufPtr->Msg; + + /* Initialize the message content */ + CFE_MSG_Init(CFE_MSG_PTR(TlmMsgPtr->TelemetryHeader), CFE_FT_TLM_MSGID, sizeof(*TlmMsgPtr)); + + TlmMsgPtr->TlmPayload = BulkTlm.SendCount; + + CFE_Assert_STATUS_STORE(CFE_SB_TransmitBuffer(BufPtr, true)); + if (!CFE_Assert_STATUS_SILENTCHECK(CFE_SUCCESS)) + { + CFE_Assert_STATUS_MUST_BE(CFE_SUCCESS); + break; + } + } + + BulkTlm.XmitFinished = true; +} + +void UT_CommandReceiverTask(void) +{ + CFE_SB_Buffer_t * MsgBuf; + const CFE_FT_TestCmdMessage_t *CmdPtr; + + for (BulkCmd.RecvCount = 0; BulkCmd.RecvCount < UT_BulkTestDuration; ++BulkCmd.RecvCount) + { + CFE_Assert_STATUS_STORE(CFE_SB_ReceiveBuffer(&MsgBuf, BulkCmd.PipeId, 5000)); + if (!CFE_Assert_STATUS_SILENTCHECK(CFE_SUCCESS)) + { + CFE_Assert_STATUS_MUST_BE(CFE_SUCCESS); + break; + } + + /* As above, to avoid flooding of test cases, only report mismatch here */ + CmdPtr = (const void *)MsgBuf; + if (CmdPtr->CmdPayload != BulkCmd.RecvCount) + { + UtAssert_UINT32_EQ(CmdPtr->CmdPayload, BulkCmd.RecvCount); + break; + } + + CFE_Assert_STATUS_STORE(OS_CountSemGive(BulkCmd.SyncSem)); + if (!CFE_Assert_STATUS_SILENTCHECK(OS_SUCCESS)) + { + CFE_Assert_STATUS_MUST_BE(OS_SUCCESS); + break; + } + } + + CFE_PSP_GetTime(&BulkCmd.EndTime); + BulkCmd.RecvFinished = true; +} + +void UT_TelemetryReceiverTask(void) +{ + CFE_SB_Buffer_t * MsgBuf; + const CFE_FT_TestTlmMessage_t *TlmPtr; + + for (BulkTlm.RecvCount = 0; BulkTlm.RecvCount < UT_BulkTestDuration; ++BulkTlm.RecvCount) + { + CFE_Assert_STATUS_STORE(CFE_SB_ReceiveBuffer(&MsgBuf, BulkTlm.PipeId, 5000)); + if (!CFE_Assert_STATUS_SILENTCHECK(CFE_SUCCESS)) + { + CFE_Assert_STATUS_MUST_BE(CFE_SUCCESS); + break; + } + + /* As above, to avoid flooding of test cases, only report mismatch here */ + TlmPtr = (const void *)MsgBuf; + if (TlmPtr->TlmPayload != BulkTlm.RecvCount) + { + UtAssert_UINT32_EQ(TlmPtr->TlmPayload, BulkTlm.RecvCount); + break; + } + + CFE_Assert_STATUS_STORE(OS_CountSemGive(BulkTlm.SyncSem)); + if (!CFE_Assert_STATUS_SILENTCHECK(OS_SUCCESS)) + { + CFE_Assert_STATUS_MUST_BE(OS_SUCCESS); + break; + } + } + + CFE_PSP_GetTime(&BulkTlm.EndTime); + BulkTlm.RecvFinished = true; +} + +void TestBulkTransferMulti4(void) +{ + uint32 i; + OS_time_t ElapsedTime; + int64 AvgRate; + + UtPrintf("Testing: 4 Thread Bulk SB Transmit/Receive with Sync Sem"); + memset(&BulkCmd, 0, sizeof(BulkCmd)); + memset(&BulkTlm, 0, sizeof(BulkCmd)); + + /* Setup, create a pipe and subscribe (one cmd, one tlm) */ + UtAssert_INT32_EQ(CFE_SB_CreatePipe(&BulkCmd.PipeId, 10, "TestPipe1"), CFE_SUCCESS); + UtAssert_INT32_EQ(CFE_SB_CreatePipe(&BulkTlm.PipeId, 10, "TestPipe2"), CFE_SUCCESS); + + UtAssert_INT32_EQ(CFE_SB_SubscribeEx(CFE_FT_CMD_MSGID, BulkCmd.PipeId, CFE_SB_DEFAULT_QOS, 8), CFE_SUCCESS); + UtAssert_INT32_EQ(CFE_SB_SubscribeEx(CFE_FT_TLM_MSGID, BulkTlm.PipeId, CFE_SB_DEFAULT_QOS, 8), CFE_SUCCESS); + + UtAssert_INT32_EQ(OS_CountSemCreate(&BulkCmd.SyncSem, "CmdSem", 0, 0), OS_SUCCESS); + UtAssert_INT32_EQ(OS_CountSemCreate(&BulkTlm.SyncSem, "TlmSem", 0, 0), OS_SUCCESS); + + UtAssert_INT32_EQ( + CFE_ES_CreateChildTask(&BulkCmd.TaskIdXmit, "CmdXmit", UT_CommandTransmitterTask, NULL, 32768, 150, 0), + CFE_SUCCESS); + UtAssert_INT32_EQ( + CFE_ES_CreateChildTask(&BulkTlm.TaskIdXmit, "TlmXmit", UT_TelemtryTransmitterTask, NULL, 32768, 150, 0), + CFE_SUCCESS); + UtAssert_INT32_EQ( + CFE_ES_CreateChildTask(&BulkCmd.TaskIdRecv, "CmdRecv", UT_CommandReceiverTask, NULL, 32768, 100, 0), + CFE_SUCCESS); + UtAssert_INT32_EQ( + CFE_ES_CreateChildTask(&BulkTlm.TaskIdRecv, "TlmRecv", UT_TelemetryReceiverTask, NULL, 32768, 100, 0), + CFE_SUCCESS); + + /* Let all tasks start and pend on sem */ + OS_TaskDelay(500); + + /* Give sem several times each to get a pipeline going, but do not exceed msglim of 8 */ + for (i = 0; i < 8; ++i) + { + UtAssert_INT32_EQ(OS_CountSemGive(BulkCmd.SyncSem), OS_SUCCESS); + UtAssert_INT32_EQ(OS_CountSemGive(BulkTlm.SyncSem), OS_SUCCESS); + } + + do + { + OS_TaskDelay(1000); + UtPrintf("Counts => %lu/%lu CMD, %lu/%lu TLM", (unsigned long)BulkCmd.SendCount, + (unsigned long)BulkCmd.RecvCount, (unsigned long)BulkTlm.SendCount, (unsigned long)BulkTlm.RecvCount); + } while (!BulkCmd.XmitFinished || !BulkCmd.RecvFinished || !BulkTlm.XmitFinished || !BulkTlm.RecvFinished); + + ElapsedTime = OS_TimeSubtract(BulkCmd.EndTime, BulkCmd.StartTime); + UtAssert_MIR("Elapsed time for SB bulk CMD thread: %lu usec", + (unsigned long)OS_TimeGetTotalMicroseconds(ElapsedTime)); + + ElapsedTime = OS_TimeSubtract(BulkTlm.EndTime, BulkCmd.StartTime); + UtAssert_MIR("Elapsed time for SB bulk TLM thread: %lu usec", + (unsigned long)OS_TimeGetTotalMicroseconds(ElapsedTime)); + + ElapsedTime = UT_GetTotalElapsedTime(); + UtAssert_MIR("Elapsed time for all threads: %lu usec", (unsigned long)OS_TimeGetTotalMicroseconds(ElapsedTime)); + + /* The message rate is the total number of messages processed over the total time elapsed */ + AvgRate = OS_TimeGetTotalMilliseconds(ElapsedTime); + if (AvgRate > 0) + { + AvgRate = ((int64)(BulkCmd.RecvCount + BulkTlm.RecvCount) * 10000) / AvgRate; + UtAssert_MIR("Message Rate: %ld.%01ld messages/sec", (long)(AvgRate / 10), (long)(AvgRate % 10)); + } + else + { + /* If the entire test took less than a millisecond, then there is a config error */ + UtAssert_Failed("Test configuration error: Executed too fast, needs more cycles!"); + } + + /* Child tasks should have self-exited... */ + UtAssert_INT32_EQ(CFE_SB_DeletePipe(BulkCmd.PipeId), CFE_SUCCESS); + UtAssert_INT32_EQ(CFE_SB_DeletePipe(BulkTlm.PipeId), CFE_SUCCESS); + UtAssert_INT32_EQ(OS_CountSemDelete(BulkCmd.SyncSem), OS_SUCCESS); + UtAssert_INT32_EQ(OS_CountSemDelete(BulkTlm.SyncSem), OS_SUCCESS); } void SBPerformanceTestSetup(void) { - UtTest_Add(TestBulkTransmitRecv, NULL, NULL, "Test Bulk SB Transmit/Receive"); + long i; + OS_time_t clock_start; + OS_time_t clock_now; + + i = 0; + CFE_PSP_GetTime(&clock_start); + do + { + ++i; + CFE_PSP_GetTime(&clock_now); + } while (OS_TimeGetTotalMilliseconds(OS_TimeSubtract(clock_now, clock_start)) < 100); + + UtAssert_MIR("Local CPU speed loop count=%ld", i); + + while (i > 0) + { + i /= 5; + UT_BulkTestDuration *= 2; + } + + UtAssert_MIR("Configured to execute %lu message transfers", (unsigned long)UT_BulkTestDuration); + + UtTest_Add(TestBulkTransferSingle, NULL, NULL, "Single Thread Bulk Transfer"); + UtTest_Add(TestBulkTransferMulti2, NULL, NULL, "2 Thread Bulk Transfer"); + UtTest_Add(TestBulkTransferMulti4, NULL, NULL, "4 Thread Bulk Transfer"); } From 3a7f23a8ee1d60fc96b1d5a3204c2c4bc761c43d Mon Sep 17 00:00:00 2001 From: Isaac Rowe <9010221+irowebbn@users.noreply.github.com> Date: Thu, 15 Jun 2023 14:10:24 -0500 Subject: [PATCH 5/7] Fix #2372 TBL UT for path length config mismatch --- modules/tbl/ut-coverage/tbl_UT.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/modules/tbl/ut-coverage/tbl_UT.c b/modules/tbl/ut-coverage/tbl_UT.c index 77db831f9..113216dea 100644 --- a/modules/tbl/ut-coverage/tbl_UT.c +++ b/modules/tbl/ut-coverage/tbl_UT.c @@ -3037,6 +3037,7 @@ void Test_CFE_TBL_TblMod(void) CFE_TBL_Handle_t AccessIterator; uint8 CDS_Data[sizeof(UT_Table1_t)]; uint32 ExpectedCrc; + int maxPathLenDiff = (int) CFE_MISSION_MAX_PATH_LEN - (int) OS_MAX_PATH_LEN; memset(&TblInfo1, 0, sizeof(TblInfo1)); @@ -3177,13 +3178,22 @@ void Test_CFE_TBL_TblMod(void) UtAssert_INT32_EQ(CFE_TBL_GetAddress(&TblDataAddr, App1TblHandle1), CFE_TBL_INFO_UPDATED); /* - * LastFileLoaded (limited by mission) can be bigger than MyFilename (limited by osal), - * need to adjust length of check to account for difference and modified marking + * LastFileLoaded (limited by mission) can be bigger than MyFilename (limited by osal) */ - UtAssert_StrnCmp(TblInfo1.LastFileLoaded, MyFilename, sizeof(MyFilename) - 4, "%s == %s, %ld", - TblInfo1.LastFileLoaded, MyFilename, (long)sizeof(MyFilename) - 4); - UtAssert_StrCmp(&TblInfo1.LastFileLoaded[sizeof(MyFilename) - 4], "(*)", "%s == (*)", - &TblInfo1.LastFileLoaded[sizeof(MyFilename) - 4]); + UtAssert_StrnCmp(TblInfo1.LastFileLoaded, MyFilename, sizeof(TblInfo1.LastFileLoaded) - 4, "%s == %s, %ld", + TblInfo1.LastFileLoaded, MyFilename, (long)sizeof(TblInfo1.LastFileLoaded) - 4); + + if(maxPathLenDiff >= 0) + { + UtAssert_StrCmp(&TblInfo1.LastFileLoaded[sizeof(MyFilename) - 4], "(*)", "%s == (*)", + &TblInfo1.LastFileLoaded[sizeof(MyFilename) - 4]); + } + else if(maxPathLenDiff > -3) + { + int modIndicatorStart = (int) CFE_MISSION_MAX_PATH_LEN -4 - maxPathLenDiff; + UtAssert_StrCmp(&TblInfo1.LastFileLoaded[modIndicatorStart], "(*)", "%s == (*)", + &TblInfo1.LastFileLoaded[modIndicatorStart]); + } /* Test response to an invalid handle */ UtAssert_INT32_EQ(CFE_TBL_Modified(CFE_TBL_BAD_TABLE_HANDLE), CFE_TBL_ERR_INVALID_HANDLE); From 175b29b5527af98b3e0119d00732d8a83137ea81 Mon Sep 17 00:00:00 2001 From: Dylan Date: Tue, 5 Dec 2023 10:29:28 -0500 Subject: [PATCH 6/7] Updating documentation and version numbers for v7.0.0-rc4+dev424 --- CHANGELOG.md | 8 ++++++++ modules/core_api/fsw/inc/cfe_version.h | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b2ac3cefa..7c206f34c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## Development Build: v7.0.0-rc4+dev424 +- Add perfid limit info to ES docstring +- TBL UT update for OSAL/CFE path length mismatch +- add multi threaded SB perf tests +- message integrity API +- change workflow to use output on failure option +- See , , , , and + ## Development Build: v7.0.0-rc4+dev411 - rename reference table - Remove nonexist reference for perf tool diff --git a/modules/core_api/fsw/inc/cfe_version.h b/modules/core_api/fsw/inc/cfe_version.h index 39a53331e..f8fafa244 100644 --- a/modules/core_api/fsw/inc/cfe_version.h +++ b/modules/core_api/fsw/inc/cfe_version.h @@ -26,7 +26,7 @@ #define CFE_VERSION_H /* Development Build Macro Definitions */ -#define CFE_BUILD_NUMBER 411 /**< @brief Development: Number of development git commits since CFE_BUILD_BASELINE */ +#define CFE_BUILD_NUMBER 424 /**< @brief Development: Number of development git commits since CFE_BUILD_BASELINE */ #define CFE_BUILD_BASELINE "v7.0.0-rc4" /**< @brief Development: Reference git tag for build number */ /* See \ref cfsversions for definitions */ From 0d27889e487646e1eca3f536fc4e13a52b0b3e7e Mon Sep 17 00:00:00 2001 From: Joseph Hickey Date: Fri, 16 Jun 2023 16:18:05 -0400 Subject: [PATCH 7/7] Fix #2378, refactor SB to support additional use cases Cleans up the internal SB implementation so it can better support future enhancements such as message integrity, additional header fields and timestamping. --- modules/sb/fsw/src/cfe_sb_api.c | 844 +++--------------------- modules/sb/fsw/src/cfe_sb_buf.c | 1 - modules/sb/fsw/src/cfe_sb_priv.c | 1060 +++++++++++++++++++++++++++++- modules/sb/fsw/src/cfe_sb_priv.h | 597 ++++++++++++++--- modules/sb/ut-coverage/sb_UT.c | 1017 ++++++++++++++++------------ modules/sb/ut-coverage/sb_UT.h | 43 +- 6 files changed, 2289 insertions(+), 1273 deletions(-) diff --git a/modules/sb/fsw/src/cfe_sb_api.c b/modules/sb/fsw/src/cfe_sb_api.c index bc4280ece..89a1e8caf 100644 --- a/modules/sb/fsw/src/cfe_sb_api.c +++ b/modules/sb/fsw/src/cfe_sb_api.c @@ -1282,464 +1282,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 IsOrigination) -{ - 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 = IsOrigination; - 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]; - bool IsAcceptable; - - SBSndErr.EvtsToSnd = 0; - IsAcceptable = true; - - /* 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); - - CFE_MSG_SetSequenceCount(&BufDscPtr->Content.Msg, CFE_SBR_GetSequenceCounter(RouteId)); - - /* Update all MSG headers based on the current sequence */ - CFE_MSG_OriginationAction(&BufDscPtr->Content.Msg, BufDscPtr->ContentSize, &IsAcceptable); - - /* 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 @@ -1748,251 +1290,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)]; - - PendingEventID = 0; - Status = CFE_SUCCESS; - SysTimeout = OS_PEND; - SysQueueId = OS_OBJECT_ID_UNDEFINED; - PipeDscPtr = NULL; - BufDscPtr = NULL; - DestPtr = NULL; - BufDscSize = 0; - OsStatus = OS_SUCCESS; - - /* - * 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. - */ + CFE_SB_ReceiveTxn_State_t TxnBuf; + CFE_SB_MessageTxn_State_t *Txn; - 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; - } + Txn = CFE_SB_ReceiveTxn_Init(&TxnBuf, BufPtr); - /* 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; - } + *BufPtr = (CFE_SB_Buffer_t *)CFE_SB_ReceiveTxn_Execute(Txn); } - 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; - } - } + CFE_SB_MessageTxn_ReportEvents(Txn); - /* If not successful, set the output pointer to NULL */ - if (Status != CFE_SUCCESS && BufPtr != NULL) - { - *BufPtr = NULL; - } - - return Status; + return CFE_SB_MessageTxn_GetStatus(Txn); } /*---------------------------------------------------------------- @@ -2055,13 +1387,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 @@ -2071,23 +1404,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; } /*---------------------------------------------------------------- @@ -2096,25 +1425,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); } /*---------------------------------------------------------------- @@ -2123,52 +1458,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 IsOrigination) +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 = IsOrigination; - 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..169bc1361 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,1049 @@ 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 + * + *-----------------------------------------------------------------*/ +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..ee87cf09a 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 +{ + 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 { - uint32 EvtsToSnd; - CFE_SB_SendErrEventBuf_t EvtBuf[CFE_PLATFORM_SB_MAX_DEST_PER_PKT]; -} CFE_SB_EventBuf_t; + 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,440 @@ 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 + */ +static inline bool CFE_SB_MessageTxn_IsOK(const CFE_SB_MessageTxn_State_t *TxnPtr) +{ + return (TxnPtr->Status == CFE_SUCCESS && TxnPtr->TransactionEventId == 0); +} + + +/*---------------------------------------------------------------------------------------*/ +/** + * \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 72df5a6b3..68b85b0f8 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); } @@ -3300,9 +3894,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); @@ -3501,113 +4092,12 @@ void Test_TransmitMsg_DisabledDestination(void) CFE_UtAssert_TEARDOWN(CFE_SB_DeletePipe(PipeId)); } -/* -** Test successful CFE_SB_BroadcastBufferToRoute -*/ -void Test_BroadcastBufferToRoute(void) -{ - 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(); - - /* 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) +void UT_CFE_MSG_Verify_CustomHandler(void *UserObj, UT_EntryKey_t FuncKey, const UT_StubContext_t *Context) { - 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); + bool *IsAcceptable = UT_Hook_GetArgValueByName(Context, "IsAcceptable", bool *); - 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; } /* @@ -3626,17 +4116,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 */ @@ -3696,8 +4187,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 */ @@ -3789,7 +4280,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); @@ -4183,11 +4674,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); } @@ -4357,281 +4843,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