From 9a81cb67d430810442a1aa8b2aa9c85d5106265f Mon Sep 17 00:00:00 2001 From: Joseph Hickey Date: Thu, 1 Jun 2023 11:14:50 -0400 Subject: [PATCH] Fix #2362, Add source routing APIs to SB Add two new APIs that allow more control over the message routing. These allow the caller to directly specify the route (MsgId) and size of the content, and the message will be delivered based on the passed in values vs. the values inside the message itself. This restructures the existing/historical API calls to use the same underlying mechanism. Send/Receive actions have a transaction object associated and this tracks the status and events. Common event reporting is done based on this transaction object. --- modules/cfe_testcase/src/sb_sendrecv_test.c | 36 +- modules/core_api/fsw/inc/cfe_msg.h | 3 +- modules/core_api/fsw/inc/cfe_sb.h | 87 +- .../core_api/ut-stubs/src/cfe_msg_handlers.c | 16 + modules/core_api/ut-stubs/src/cfe_msg_stubs.c | 21 +- modules/core_api/ut-stubs/src/cfe_sb_stubs.c | 51 +- modules/msg/fsw/src/cfe_msg_init.c | 3 +- modules/msg/ut-coverage/test_cfe_msg_init.c | 6 +- modules/sb/fsw/inc/cfe_sb_eventids.h | 13 + modules/sb/fsw/src/cfe_sb_api.c | 874 +++---------- modules/sb/fsw/src/cfe_sb_buf.c | 1 - modules/sb/fsw/src/cfe_sb_priv.c | 1061 +++++++++++++++- modules/sb/fsw/src/cfe_sb_priv.h | 591 +++++++-- modules/sb/ut-coverage/sb_UT.c | 1126 +++++++++++------ modules/sb/ut-coverage/sb_UT.h | 58 +- 15 files changed, 2693 insertions(+), 1254 deletions(-) diff --git a/modules/cfe_testcase/src/sb_sendrecv_test.c b/modules/cfe_testcase/src/sb_sendrecv_test.c index 4f8be7ac2..023249d5a 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 "EnableUpdate" 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. * @@ -598,10 +598,44 @@ void TestMiscMessageUtils(void) CFE_SB_BAD_ARGUMENT); } +void TestSourceRouting(void) +{ + CFE_SB_Buffer_t * TxBufPtr; + const CFE_SB_Buffer_t *RxBufPtr; + size_t RxSize; + CFE_SB_MsgId_t RxMsgId; + CFE_SB_PipeId_t PipeId; + + /* Random data -- not a CCSDS header (this is intentional) */ + const uint8_t Content[16] = {0xe9, 0xf2, 0x67, 0x3d, 0x5e, 0x54, 0x1a, 0xa5, + 0xe9, 0xe2, 0x11, 0xe8, 0x71, 0x85, 0xb7, 0x0d}; + + /* Setup, create a pipe and subscribe (one cmd, one tlm) */ + UtAssert_INT32_EQ(CFE_SB_CreatePipe(&PipeId, 5, "TestPipe"), CFE_SUCCESS); + UtAssert_INT32_EQ(CFE_SB_SubscribeEx(CFE_FT_CMD_MSGID, PipeId, CFE_SB_DEFAULT_QOS, 3), CFE_SUCCESS); + + TxBufPtr = CFE_SB_AllocateMessageBuffer(sizeof(Content)); + UtAssert_NOT_NULL(TxBufPtr); + memcpy(TxBufPtr, Content, sizeof(Content)); + + UtAssert_INT32_EQ(CFE_SB_TransmitBufferWithRoute(TxBufPtr, sizeof(Content), CFE_FT_CMD_MSGID, false, CFE_SB_POLL), + CFE_SUCCESS); + UtAssert_INT32_EQ(CFE_SB_ReceiveBufferWithRoute(PipeId, &RxBufPtr, &RxSize, &RxMsgId, false, CFE_SB_POLL), + CFE_SUCCESS); + + UtAssert_ADDRESS_EQ(RxBufPtr, TxBufPtr); + UtAssert_UINT32_EQ(RxSize, sizeof(Content)); + CFE_Assert_MSGID_EQ(RxMsgId, CFE_FT_CMD_MSGID); + + /* Cleanup */ + UtAssert_INT32_EQ(CFE_SB_DeletePipe(PipeId), CFE_SUCCESS); +} + void SBSendRecvTestSetup(void) { UtTest_Add(TestBasicTransmitRecv, NULL, NULL, "Test Basic Transmit/Receive"); UtTest_Add(TestZeroCopyTransmitRecv, NULL, NULL, "Test Zero Copy Transmit/Receive"); UtTest_Add(TestMsgBroadcast, NULL, NULL, "Test Msg Broadcast"); + UtTest_Add(TestSourceRouting, NULL, NULL, "Test Source Routing APIs"); UtTest_Add(TestMiscMessageUtils, NULL, NULL, "Test Miscellaneous Message Utility APIs"); } diff --git a/modules/core_api/fsw/inc/cfe_msg.h b/modules/core_api/fsw/inc/cfe_msg.h index ccd43bdb7..8040b8114 100644 --- a/modules/core_api/fsw/inc/cfe_msg.h +++ b/modules/core_api/fsw/inc/cfe_msg.h @@ -77,12 +77,13 @@ CFE_Status_t CFE_MSG_Init(CFE_MSG_Message_t *MsgPtr, CFE_SB_MsgId_t MsgId, CFE_M * * \param[inout] MsgPtr A pointer to the buffer that contains the message @nonnull. * \param[in] SeqCnt The current sequence number from the message route + * \param[in] MsgSize The size of the message * * \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); +CFE_Status_t CFE_MSG_UpdateHeader(CFE_MSG_Message_t *MsgPtr, CFE_MSG_SequenceCount_t SeqCnt, CFE_MSG_Size_t MsgSize); /**\}*/ diff --git a/modules/core_api/fsw/inc/cfe_sb.h b/modules/core_api/fsw/inc/cfe_sb.h index a670d0120..104f72142 100644 --- a/modules/core_api/fsw/inc/cfe_sb.h +++ b/modules/core_api/fsw/inc/cfe_sb.h @@ -402,7 +402,7 @@ 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" +** In general, the "EnableUpdate" 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 ** an external entity (e.g. messages passing through CI or SBN), the @@ -421,7 +421,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] EnableUpdate Update the headers of the message ** ** \return Execution status, see \ref CFEReturnCodes ** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS @@ -429,7 +429,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 EnableUpdate); /*****************************************************************************/ /** @@ -471,6 +471,46 @@ 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); + +/*****************************************************************************/ +/** +** \brief Receive a generic buffer +** +** \par Description +** This routine accepts an abstract buffer via the software bus +** from the specified pipe, without relying on the MsgId and/or routing +** information from the message headers. As a result, the buffer need not +** strictly be a CFE CMD/TLM message, it can be any arbitrary data. The +** routing information is passed out-of-band and will be returned in the specified +** buffers. +** +** The message also may be verified if "EnableVerify" is set to true. In this +** case, CFE_MSG_Verify() is invoked on the received message, and if this results +** in validation failure, the message will be internally discarded and not passed +** back to the caller. Note that in this case, the buffer cannot be arbitrary data, +** it must be compliant with the expected headers. Arbitrary data can only be passed +** by setting this parameter to "false" +** +** \par Assumptions, External Events, and Notes: +** - See CFE_SB_ReceiveBuffer() +** +** \param[in] PipeId Pipe ID to receive from +** \param[out] BufPtr A pointer to the buffer to be received @nonnull. +** \param[out] ContentSize Actual content size of the message +** \param[out] RoutingMsgId Destination/Route to which the message was sent +** \param[in] EnableVerify Verify the headers of the message +** \param[in] TimeOut Maxmimum amount of time to block +** +** \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_TIME_OUT \copybrief CFE_SB_TIME_OUT +** \retval #CFE_SB_PIPE_RD_ERR \covtest \copybrief CFE_SB_PIPE_RD_ERR +** \retval #CFE_SB_NO_MESSAGE \copybrief CFE_SB_NO_MESSAGE +*/ +CFE_Status_t CFE_SB_ReceiveBufferWithRoute(CFE_SB_PipeId_t PipeId, const CFE_SB_Buffer_t **BufPtr, size_t *ContentSize, + CFE_SB_MsgId_t *RoutingMsgId, bool EnableVerify, int32 TimeOut); + /** @} */ /** @defgroup CFEAPISBZeroCopy cFE Zero Copy APIs @@ -544,7 +584,7 @@ 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" +** In general, the "EnableUpdate" 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 ** an external entity (e.g. messages passing through CI or SBN), the @@ -568,14 +608,49 @@ CFE_Status_t CFE_SB_ReleaseMessageBuffer(CFE_SB_Buffer_t *BufPtr); ** 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] EnableUpdate Update the headers of the 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 EnableUpdate); + +/*****************************************************************************/ +/** +** \brief Transmit a generic buffer with source routing +** +** \par Description +** This routine sends an abstract buffer via the software bus directly +** to the specified destination, without relying on the MsgId and/or routing +** information from the message headers. As a result, the buffer need not +** strictly be a CFE CMD/TLM message, it can be any arbitrary data (but see note). +** The buffer will be delivered to subscribers based on the message ID passed in. +** +** \par Assumptions, External Events, and Notes: +** -# See CFE_SB_TransmitBuffer() - the passed-in buffer object is consumed by this +** call in a similar manner. +** -# If "EnableUpdate" is passed as "true", then the content must be a CFE message +** with the expected headers (it cannot be arbitrary data, as the header update +** will modify it based on the normally expected header). Only when this boolean +** is passed as "false" does this routine not access the buffer at all. +** -# For normal broadcast messages, the TimeOut should generally be CFE_SB_POLL only. +** Nonzero timeouts may be useful for point-to-point connections as a future enhancement. +** +** \param[in] BufPtr A pointer to the buffer to be sent @nonnull. +** \param[in] ContentSize Actual content size of the message +** \param[in] RoutingMsgId Destination/Route to which the message should be broadcast +** \param[in] EnableUpdate Update the headers of the message +** \param[in] TimeOut Maxmimum amount of time to block (not for broadcast) +** +** \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_TransmitBufferWithRoute(CFE_SB_Buffer_t *BufPtr, size_t ContentSize, CFE_SB_MsgId_t RoutingMsgId, + bool EnableUpdate, int32 TimeOut); /** @} */ 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..315971276 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,19 @@ void UT_DefaultHandler_CFE_MSG_GetNextSequenceCount(void *UserObj, UT_EntryKey_t UT_Stub_SetReturnValue(FuncKey, return_value); } + +/*------------------------------------------------------------ + * + * Default handler for CFE_MSG_Verify coverage stub function + * + *------------------------------------------------------------*/ +void UT_DefaultHandler_CFE_MSG_Verify(void *UserObj, UT_EntryKey_t FuncKey, const UT_StubContext_t *Context) +{ + bool *VerifyStatus = UT_Hook_GetArgValueByName(Context, "VerifyStatus", bool *); + + /* by default just always return true -- a UT case that needs something else can override this handler */ + if (VerifyStatus != NULL) + { + *VerifyStatus = 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..bd0ef494c 100644 --- a/modules/core_api/ut-stubs/src/cfe_msg_stubs.c +++ b/modules/core_api/ut-stubs/src/cfe_msg_stubs.c @@ -43,6 +43,7 @@ void UT_DefaultHandler_CFE_MSG_GetSystem(void *, UT_EntryKey_t, const UT_StubCon 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_ValidateChecksum(void *, UT_EntryKey_t, const UT_StubContext_t *); +void UT_DefaultHandler_CFE_MSG_Verify(void *, UT_EntryKey_t, const UT_StubContext_t *); /* * ---------------------------------------------------- @@ -626,12 +627,13 @@ CFE_Status_t CFE_MSG_SetType(CFE_MSG_Message_t *MsgPtr, CFE_MSG_Type_t Type) * Generated stub function for CFE_MSG_UpdateHeader() * ---------------------------------------------------- */ -CFE_Status_t CFE_MSG_UpdateHeader(CFE_MSG_Message_t *MsgPtr, CFE_MSG_SequenceCount_t SeqCnt) +CFE_Status_t CFE_MSG_UpdateHeader(CFE_MSG_Message_t *MsgPtr, CFE_MSG_SequenceCount_t SeqCnt, CFE_MSG_Size_t MsgSize) { UT_GenStub_SetupReturnBuffer(CFE_MSG_UpdateHeader, 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_UpdateHeader, CFE_MSG_Size_t, MsgSize); UT_GenStub_Execute(CFE_MSG_UpdateHeader, Basic, NULL); @@ -654,3 +656,20 @@ CFE_Status_t CFE_MSG_ValidateChecksum(const CFE_MSG_Message_t *MsgPtr, bool *IsV return UT_GenStub_GetReturnValue(CFE_MSG_ValidateChecksum, CFE_Status_t); } + +/* + * ---------------------------------------------------- + * Generated stub function for CFE_MSG_Verify() + * ---------------------------------------------------- + */ +CFE_Status_t CFE_MSG_Verify(const CFE_MSG_Message_t *MsgPtr, bool *VerifyStatus) +{ + UT_GenStub_SetupReturnBuffer(CFE_MSG_Verify, CFE_Status_t); + + UT_GenStub_AddParam(CFE_MSG_Verify, const CFE_MSG_Message_t *, MsgPtr); + UT_GenStub_AddParam(CFE_MSG_Verify, bool *, VerifyStatus); + + UT_GenStub_Execute(CFE_MSG_Verify, Basic, UT_DefaultHandler_CFE_MSG_Verify); + + return UT_GenStub_GetReturnValue(CFE_MSG_Verify, 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..6c4598d3d 100644 --- a/modules/core_api/ut-stubs/src/cfe_sb_stubs.c +++ b/modules/core_api/ut-stubs/src/cfe_sb_stubs.c @@ -267,6 +267,28 @@ CFE_Status_t CFE_SB_ReceiveBuffer(CFE_SB_Buffer_t **BufPtr, CFE_SB_PipeId_t Pipe return UT_GenStub_GetReturnValue(CFE_SB_ReceiveBuffer, CFE_Status_t); } +/* + * ---------------------------------------------------- + * Generated stub function for CFE_SB_ReceiveBufferWithRoute() + * ---------------------------------------------------- + */ +CFE_Status_t CFE_SB_ReceiveBufferWithRoute(CFE_SB_PipeId_t PipeId, const CFE_SB_Buffer_t **BufPtr, size_t *ContentSize, + CFE_SB_MsgId_t *RoutingMsgId, bool EnableVerify, int32 TimeOut) +{ + UT_GenStub_SetupReturnBuffer(CFE_SB_ReceiveBufferWithRoute, CFE_Status_t); + + UT_GenStub_AddParam(CFE_SB_ReceiveBufferWithRoute, CFE_SB_PipeId_t, PipeId); + UT_GenStub_AddParam(CFE_SB_ReceiveBufferWithRoute, const CFE_SB_Buffer_t **, BufPtr); + UT_GenStub_AddParam(CFE_SB_ReceiveBufferWithRoute, size_t *, ContentSize); + UT_GenStub_AddParam(CFE_SB_ReceiveBufferWithRoute, CFE_SB_MsgId_t *, RoutingMsgId); + UT_GenStub_AddParam(CFE_SB_ReceiveBufferWithRoute, bool, EnableVerify); + UT_GenStub_AddParam(CFE_SB_ReceiveBufferWithRoute, int32, TimeOut); + + UT_GenStub_Execute(CFE_SB_ReceiveBufferWithRoute, Basic, NULL); + + return UT_GenStub_GetReturnValue(CFE_SB_ReceiveBufferWithRoute, CFE_Status_t); +} + /* * ---------------------------------------------------- * Generated stub function for CFE_SB_ReleaseMessageBuffer() @@ -384,29 +406,50 @@ 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 EnableUpdate) { 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, EnableUpdate); UT_GenStub_Execute(CFE_SB_TransmitBuffer, Basic, UT_DefaultHandler_CFE_SB_TransmitBuffer); return UT_GenStub_GetReturnValue(CFE_SB_TransmitBuffer, CFE_Status_t); } +/* + * ---------------------------------------------------- + * Generated stub function for CFE_SB_TransmitBufferWithRoute() + * ---------------------------------------------------- + */ +CFE_Status_t CFE_SB_TransmitBufferWithRoute(CFE_SB_Buffer_t *BufPtr, size_t ContentSize, CFE_SB_MsgId_t RoutingMsgId, + bool EnableUpdate, int32 TimeOut) +{ + UT_GenStub_SetupReturnBuffer(CFE_SB_TransmitBufferWithRoute, CFE_Status_t); + + UT_GenStub_AddParam(CFE_SB_TransmitBufferWithRoute, CFE_SB_Buffer_t *, BufPtr); + UT_GenStub_AddParam(CFE_SB_TransmitBufferWithRoute, size_t, ContentSize); + UT_GenStub_AddParam(CFE_SB_TransmitBufferWithRoute, CFE_SB_MsgId_t, RoutingMsgId); + UT_GenStub_AddParam(CFE_SB_TransmitBufferWithRoute, bool, EnableUpdate); + UT_GenStub_AddParam(CFE_SB_TransmitBufferWithRoute, int32, TimeOut); + + UT_GenStub_Execute(CFE_SB_TransmitBufferWithRoute, Basic, NULL); + + return UT_GenStub_GetReturnValue(CFE_SB_TransmitBufferWithRoute, CFE_Status_t); +} + /* * ---------------------------------------------------- * 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 EnableUpdate) { 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, EnableUpdate); UT_GenStub_Execute(CFE_SB_TransmitMsg, Basic, UT_DefaultHandler_CFE_SB_TransmitMsg); diff --git a/modules/msg/fsw/src/cfe_msg_init.c b/modules/msg/fsw/src/cfe_msg_init.c index ab988f4bb..c015d2894 100644 --- a/modules/msg/fsw/src/cfe_msg_init.c +++ b/modules/msg/fsw/src/cfe_msg_init.c @@ -60,7 +60,7 @@ CFE_Status_t CFE_MSG_Init(CFE_MSG_Message_t *MsgPtr, CFE_SB_MsgId_t MsgId, CFE_M * 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) +CFE_Status_t CFE_MSG_UpdateHeader(CFE_MSG_Message_t *MsgPtr, CFE_MSG_SequenceCount_t SeqCnt, CFE_MSG_Size_t MsgSize) { if (MsgPtr == NULL) { @@ -69,6 +69,7 @@ CFE_Status_t CFE_MSG_UpdateHeader(CFE_MSG_Message_t *MsgPtr, CFE_MSG_SequenceCou /* Sequence count is in the basic CCSDS Primary Hdr, so all msgs have it */ CFE_MSG_SetSequenceCount(MsgPtr, SeqCnt); + CFE_MSG_SetSize(MsgPtr, MsgSize); /* * TLM packets have a timestamp in the secondary header. diff --git a/modules/msg/ut-coverage/test_cfe_msg_init.c b/modules/msg/ut-coverage/test_cfe_msg_init.c index a707c3618..0c54cb1a0 100644 --- a/modules/msg/ut-coverage/test_cfe_msg_init.c +++ b/modules/msg/ut-coverage/test_cfe_msg_init.c @@ -145,18 +145,18 @@ void Test_MSG_UpdateHeader(void) CheckCnt = 0; /* bad buffer */ - UtAssert_INT32_EQ(CFE_MSG_UpdateHeader(NULL, SeqCnt), CFE_MSG_BAD_ARGUMENT); + UtAssert_INT32_EQ(CFE_MSG_UpdateHeader(NULL, SeqCnt, sizeof(LocalBuf)), 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_UtAssert_SUCCESS(CFE_MSG_UpdateHeader(&LocalBuf.msg, SeqCnt, sizeof(LocalBuf))); 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_UtAssert_SUCCESS(CFE_MSG_UpdateHeader(&LocalBuf.msg, SeqCnt, sizeof(LocalBuf))); CFE_MSG_GetSequenceCount(&LocalBuf.msg, &CheckCnt); UtAssert_UINT32_EQ(CheckCnt, SeqCnt); ++SeqCnt; diff --git a/modules/sb/fsw/inc/cfe_sb_eventids.h b/modules/sb/fsw/inc/cfe_sb_eventids.h index 326542f9c..4b586f9bf 100644 --- a/modules/sb/fsw/inc/cfe_sb_eventids.h +++ b/modules/sb/fsw/inc/cfe_sb_eventids.h @@ -759,6 +759,19 @@ * #CFE_SB_CreatePipe API failure due to no free queues. */ #define CFE_SB_CR_PIPE_NO_FREE_EID 70 + +/** + * \brief SB validation of received message failure event + * + * \par Type: ERROR + * + * \par Cause: + * + * A CFE SB receive transaction has dropped a message due + * to validation failure of the recevied buffer. + */ +#define CFE_SB_RCV_MESSAGE_VALIDATION_FAIL_EID 71 + /**\}*/ #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 1b9574260..a0bafa712 100644 --- a/modules/sb/fsw/src/cfe_sb_api.c +++ b/modules/sb/fsw/src/cfe_sb_api.c @@ -1287,452 +1287,44 @@ 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_ReceiveBuffer(CFE_SB_Buffer_t **BufPtr, CFE_SB_PipeId_t PipeId, int32 TimeOut) { - 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_ReceiveTxn_State_t TxnBuf; + CFE_SB_MessageTxn_State_t *Txn; - CFE_SB_LockSharedData(__func__, __LINE__); + Txn = CFE_SB_ReceiveTxn_Init(&TxnBuf, BufPtr); - if (Status == CFE_SUCCESS && CFE_SBR_IsValidRouteId(RouteId)) + 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. */ - BufDscPtr = CFE_SB_GetBufferFromPool(Size); - if (BufDscPtr == NULL) - { - PendingEventID = CFE_SB_GET_BUF_ERR_EID; - Status = CFE_SB_BUF_ALOC_ERR; - } + CFE_SB_MessageTxn_SetTimeout(Txn, TimeOut); } - /* - * 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) + if (CFE_SB_MessageTxn_IsOK(Txn)) { - CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter++; - } - - CFE_SB_UnlockSharedData(__func__, __LINE__); - - /* - * If a buffer was obtained above, then copy the content into it - * and broadcast it to all subscribers in the route. - * - * Note - if there is no route / no subscribers, the "Status" will - * be CFE_SUCCESS because CFE_SB_TransmitMsgValidate() succeeded, - * but there will be no buffer because CFE_SBR_IsValidRouteId() returned - * false. - * - * But if the descriptor is non-null it means the message is valid and - * there is a route to send it to. - */ - if (BufDscPtr != NULL) - { - /* Copy actual message content into buffer and set its metadata */ - memcpy(&BufDscPtr->Content, MsgPtr, Size); - BufDscPtr->MsgId = MsgId; - BufDscPtr->ContentSize = Size; - BufDscPtr->NeedsUpdate = UpdateHeader; - CFE_MSG_GetType(MsgPtr, &BufDscPtr->ContentType); + CFE_SB_ReceiveTxn_SetPipeId(Txn, PipeId); /* - * 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. + * 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. + * Use CFE_SB_ReceiveBufferWithRoute() to skip verification if needed. */ - CFE_SB_BroadcastBufferToRoute(BufDscPtr, RouteId); + CFE_SB_ReceiveTxn_SetVerify(Txn, true); + } + if (BufPtr != NULL) + { /* - * The broadcast function consumes the buffer, so it should not be - * accessed in this function anymore + * 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. */ - BufDscPtr = NULL; + *BufPtr = (CFE_SB_Buffer_t *)CFE_SB_ReceiveTxn_Execute(Txn); } - 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); + CFE_SB_MessageTxn_ReportEvents(Txn); - /* clear the bit so the task may send this event again */ - CFE_SB_FinishSendEvent(TskId, CFE_SB_GET_BUF_ERR_EID_BIT); - } - } - - return Status; -} - -/*---------------------------------------------------------------- - * - * Application-scope internal function - * See description in header file for argument/return detail - * - *-----------------------------------------------------------------*/ -int32 CFE_SB_TransmitMsgValidate(const CFE_MSG_Message_t *MsgPtr, CFE_SB_MsgId_t *MsgIdPtr, CFE_MSG_Size_t *SizePtr, - CFE_SBR_RouteId_t *RouteIdPtr) -{ - CFE_ES_TaskId_t TskId; - char FullName[(OS_MAX_API_NAME * 2)]; - uint16 PendingEventID; - int32 Status; - - PendingEventID = 0; - Status = CFE_SUCCESS; - - /* check input parameter */ - if (MsgPtr == NULL) - { - PendingEventID = CFE_SB_SEND_BAD_ARG_EID; - Status = CFE_SB_BAD_ARGUMENT; - } - - if (Status == CFE_SUCCESS) - { - CFE_MSG_GetMsgId(MsgPtr, MsgIdPtr); - - /* validate the msgid in the message */ - if (!CFE_SB_IsValidMsgId(*MsgIdPtr)) - { - PendingEventID = CFE_SB_SEND_INV_MSGID_EID; - Status = CFE_SB_BAD_ARGUMENT; - } - } - - if (Status == CFE_SUCCESS) - { - CFE_MSG_GetSize(MsgPtr, SizePtr); - - /* Verify the size of the pkt is < or = the mission defined max */ - if (*SizePtr > CFE_MISSION_SB_MAX_SB_MSG_SIZE) - { - PendingEventID = CFE_SB_MSG_TOO_BIG_EID; - Status = CFE_SB_MSG_TOO_BIG; - } - } - - if (Status == CFE_SUCCESS) - { - /* check the route, which should be done while locked */ - CFE_SB_LockSharedData(__func__, __LINE__); - - /* Get the routing id */ - *RouteIdPtr = CFE_SBR_GetRouteId(*MsgIdPtr); - - /* if there have been no subscriptions for this pkt, */ - /* increment the dropped pkt cnt, send event and return success */ - if (!CFE_SBR_IsValidRouteId(*RouteIdPtr)) - { - CFE_SB_Global.HKTlmMsg.Payload.NoSubscribersCounter++; - PendingEventID = CFE_SB_SEND_NO_SUBS_EID; - } - - CFE_SB_UnlockSharedData(__func__, __LINE__); - } - - if (PendingEventID != 0) - { - /* get task id for events */ - CFE_ES_GetTaskID(&TskId); - - switch (PendingEventID) - { - case CFE_SB_SEND_BAD_ARG_EID: - if (CFE_SB_RequestToSendEvent(TskId, CFE_SB_SEND_BAD_ARG_EID_BIT) == CFE_SB_GRANTED) - { - CFE_EVS_SendEventWithAppID(CFE_SB_SEND_BAD_ARG_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, - "Send Err:Bad input argument,Arg 0x%lx,App %s", (unsigned long)MsgPtr, - CFE_SB_GetAppTskName(TskId, FullName)); - - /* clear the bit so the task may send this event again */ - CFE_SB_FinishSendEvent(TskId, CFE_SB_SEND_BAD_ARG_EID_BIT); - } - break; - - case CFE_SB_SEND_INV_MSGID_EID: - if (CFE_SB_RequestToSendEvent(TskId, CFE_SB_SEND_INV_MSGID_EID_BIT) == CFE_SB_GRANTED) - { - CFE_EVS_SendEventWithAppID(CFE_SB_SEND_INV_MSGID_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, - "Send Err:Invalid MsgId(0x%x)in msg,App %s", - (unsigned int)CFE_SB_MsgIdToValue(*MsgIdPtr), - CFE_SB_GetAppTskName(TskId, FullName)); - - /* clear the bit so the task may send this event again */ - CFE_SB_FinishSendEvent(TskId, CFE_SB_SEND_INV_MSGID_EID_BIT); - } - break; - - case CFE_SB_MSG_TOO_BIG_EID: - if (CFE_SB_RequestToSendEvent(TskId, CFE_SB_MSG_TOO_BIG_EID_BIT) == CFE_SB_GRANTED) - { - CFE_EVS_SendEventWithAppID(CFE_SB_MSG_TOO_BIG_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, - "Send Err:Msg Too Big MsgId=0x%x,app=%s,size=%d,MaxSz=%d", - (unsigned int)CFE_SB_MsgIdToValue(*MsgIdPtr), - CFE_SB_GetAppTskName(TskId, FullName), (int)*SizePtr, - CFE_MISSION_SB_MAX_SB_MSG_SIZE); - - /* clear the bit so the task may send this event again */ - CFE_SB_FinishSendEvent(TskId, CFE_SB_MSG_TOO_BIG_EID_BIT); - } - break; - - case CFE_SB_SEND_NO_SUBS_EID: - /* Determine if event can be sent without causing recursive event problem */ - if (CFE_SB_RequestToSendEvent(TskId, CFE_SB_SEND_NO_SUBS_EID_BIT) == CFE_SB_GRANTED) - { - CFE_EVS_SendEventWithAppID(CFE_SB_SEND_NO_SUBS_EID, CFE_EVS_EventType_INFORMATION, - CFE_SB_Global.AppId, "No subscribers for MsgId 0x%x,sender %s", - (unsigned int)CFE_SB_MsgIdToValue(*MsgIdPtr), - CFE_SB_GetAppTskName(TskId, FullName)); - - /* clear the bit so the task may send this event again */ - CFE_SB_FinishSendEvent(TskId, CFE_SB_SEND_NO_SUBS_EID_BIT); - } - break; - } - } - - return Status; -} - -/*---------------------------------------------------------------- - * - * Application-scope internal function - * See description in header file for argument/return detail - * - *-----------------------------------------------------------------*/ -void CFE_SB_BroadcastBufferToRoute(CFE_SB_BufferD_t *BufDscPtr, CFE_SBR_RouteId_t RouteId) -{ - CFE_ES_AppId_t AppId; - CFE_ES_TaskId_t TskId; - CFE_SB_DestinationD_t *DestPtr; - CFE_SB_PipeD_t * PipeDscPtr; - CFE_SB_EventBuf_t SBSndErr; - int32 OsStatus; - uint32 i; - char FullName[(OS_MAX_API_NAME * 2)]; - char PipeName[OS_MAX_API_NAME]; - - SBSndErr.EvtsToSnd = 0; - - /* get app id for loopback testing */ - CFE_ES_GetAppID(&AppId); - - /* get task id for events and Sender Info*/ - CFE_ES_GetTaskID(&TskId); - - /* take semaphore to prevent a task switch during processing */ - CFE_SB_LockSharedData(__func__, __LINE__); - - /* For an invalid route / no subscribers this whole logic can be skipped */ - if (CFE_SBR_IsValidRouteId(RouteId)) - { - /* Set the seq count if requested (while locked) before actually sending */ - if (BufDscPtr->NeedsUpdate) - { - CFE_SBR_IncrementSequenceCounter(RouteId); - - /* Update all MSG headers based on the current sequence */ - CFE_MSG_UpdateHeader(&BufDscPtr->Content.Msg, CFE_SBR_GetSequenceCounter(RouteId)); - - /* Clear the flag, just in case */ - BufDscPtr->NeedsUpdate = false; - } - - /* Send the packet to all destinations */ - for (DestPtr = CFE_SBR_GetDestListHeadPtr(RouteId); DestPtr != NULL; DestPtr = DestPtr->Next) - { - if (DestPtr->Active == CFE_SB_ACTIVE) /* destination is active */ - { - PipeDscPtr = CFE_SB_LocatePipeDescByID(DestPtr->PipeId); - } - else - { - PipeDscPtr = NULL; - } - - if (!CFE_SB_PipeDescIsMatch(PipeDscPtr, DestPtr->PipeId)) - { - continue; - } - - if ((PipeDscPtr->Opts & CFE_SB_PIPEOPTS_IGNOREMINE) != 0 && - CFE_RESOURCEID_TEST_EQUAL(PipeDscPtr->AppId, AppId)) - { - continue; - } - - /* if Msg limit exceeded, log event, increment counter */ - /* and go to next destination */ - if (DestPtr->BuffCount >= DestPtr->MsgId2PipeLim) - { - SBSndErr.EvtBuf[SBSndErr.EvtsToSnd].PipeId = DestPtr->PipeId; - SBSndErr.EvtBuf[SBSndErr.EvtsToSnd].EventId = CFE_SB_MSGID_LIM_ERR_EID; - SBSndErr.EvtsToSnd++; - CFE_SB_Global.HKTlmMsg.Payload.MsgLimitErrorCounter++; - PipeDscPtr->SendErrors++; - - continue; - } - - /* - ** Write the buffer descriptor to the queue of the pipe. If the write - ** failed, log info and increment the pipe's error counter. - */ - OsStatus = OS_QueuePut(PipeDscPtr->SysQueueId, &BufDscPtr, sizeof(BufDscPtr), 0); - - if (OsStatus == OS_SUCCESS) - { - /* The queue now holds a ref to the buffer, so increment its ref count. */ - CFE_SB_IncrBufUseCnt(BufDscPtr); - - DestPtr->BuffCount++; /* used for checking MsgId2PipeLimit */ - DestPtr->DestCnt++; /* used for statistics */ - ++PipeDscPtr->CurrentQueueDepth; - if (PipeDscPtr->CurrentQueueDepth >= PipeDscPtr->PeakQueueDepth) - { - PipeDscPtr->PeakQueueDepth = PipeDscPtr->CurrentQueueDepth; - } - } - else - { - SBSndErr.EvtBuf[SBSndErr.EvtsToSnd].PipeId = DestPtr->PipeId; - if (OsStatus == OS_QUEUE_FULL) - { - SBSndErr.EvtBuf[SBSndErr.EvtsToSnd].EventId = CFE_SB_Q_FULL_ERR_EID; - CFE_SB_Global.HKTlmMsg.Payload.PipeOverflowErrorCounter++; - } - else - { - /* Unexpected error while writing to queue. */ - SBSndErr.EvtBuf[SBSndErr.EvtsToSnd].EventId = CFE_SB_Q_WR_ERR_EID; - SBSndErr.EvtBuf[SBSndErr.EvtsToSnd].OsStatus = OsStatus; - CFE_SB_Global.HKTlmMsg.Payload.InternalErrorCounter++; - } - SBSndErr.EvtsToSnd++; - PipeDscPtr->SendErrors++; - } /*end if */ - - } /* end loop over destinations */ - } - - /* - * If any specific delivery issues occurred, also increment the - * general error count before releasing the lock. - */ - if (SBSndErr.EvtsToSnd > 0) - { - CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter++; - } - - /* - * Remove this from whatever list it was in - * - * If it was a singleton/new buffer this has no effect. - * If it was a zero-copy buffer this removes it from the ZeroCopyList. - */ - CFE_SB_TrackingListRemove(&BufDscPtr->Link); - - /* clear the AppID field in case it was a zero copy buffer, - * as it is no longer owned by that app after broadcasting */ - BufDscPtr->AppId = CFE_ES_APPID_UNDEFINED; - - /* track the buffer as an in-transit message */ - CFE_SB_TrackingListAdd(&CFE_SB_Global.InTransitList, &BufDscPtr->Link); - - /* - ** Decrement the buffer UseCount and free buffer if cnt=0. This decrement is done - ** because the use cnt is initialized to 1 in CFE_SB_GetBufferFromPool. - ** Initializing the count to 1 (as opposed to zero) and decrementing it here are - ** done to ensure the buffer gets released when there are destinations that have - ** been disabled via ground command. - */ - CFE_SB_DecrBufUseCnt(BufDscPtr); - - /* release the semaphore */ - CFE_SB_UnlockSharedData(__func__, __LINE__); - - /* send an event for each pipe write error that may have occurred */ - for (i = 0; i < SBSndErr.EvtsToSnd; i++) - { - if (SBSndErr.EvtBuf[i].EventId == CFE_SB_MSGID_LIM_ERR_EID) - { - /* Determine if event can be sent without causing recursive event problem */ - if (CFE_SB_RequestToSendEvent(TskId, CFE_SB_MSGID_LIM_ERR_EID_BIT) == CFE_SB_GRANTED) - { - CFE_SB_GetPipeName(PipeName, sizeof(PipeName), SBSndErr.EvtBuf[i].PipeId); - - CFE_ES_PerfLogEntry(CFE_MISSION_SB_MSG_LIM_PERF_ID); - CFE_ES_PerfLogExit(CFE_MISSION_SB_MSG_LIM_PERF_ID); - - CFE_EVS_SendEventWithAppID(CFE_SB_MSGID_LIM_ERR_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, - "Msg Limit Err,MsgId 0x%x,pipe %s,sender %s", - (unsigned int)CFE_SB_MsgIdToValue(BufDscPtr->MsgId), PipeName, - CFE_SB_GetAppTskName(TskId, FullName)); - - /* clear the bit so the task may send this event again */ - CFE_SB_FinishSendEvent(TskId, CFE_SB_MSGID_LIM_ERR_EID_BIT); - } - } - else if (SBSndErr.EvtBuf[i].EventId == CFE_SB_Q_FULL_ERR_EID) - { - /* Determine if event can be sent without causing recursive event problem */ - if (CFE_SB_RequestToSendEvent(TskId, CFE_SB_Q_FULL_ERR_EID_BIT) == CFE_SB_GRANTED) - { - CFE_SB_GetPipeName(PipeName, sizeof(PipeName), SBSndErr.EvtBuf[i].PipeId); - - CFE_ES_PerfLogEntry(CFE_MISSION_SB_PIPE_OFLOW_PERF_ID); - CFE_ES_PerfLogExit(CFE_MISSION_SB_PIPE_OFLOW_PERF_ID); - - CFE_EVS_SendEventWithAppID(CFE_SB_Q_FULL_ERR_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, - "Pipe Overflow,MsgId 0x%x,pipe %s,sender %s", - (unsigned int)CFE_SB_MsgIdToValue(BufDscPtr->MsgId), PipeName, - CFE_SB_GetAppTskName(TskId, FullName)); - - /* clear the bit so the task may send this event again */ - CFE_SB_FinishSendEvent(TskId, CFE_SB_Q_FULL_ERR_EID_BIT); - } - } - else - { - /* Determine if event can be sent without causing recursive event problem */ - if (CFE_SB_RequestToSendEvent(TskId, CFE_SB_Q_WR_ERR_EID_BIT) == CFE_SB_GRANTED) - { - CFE_SB_GetPipeName(PipeName, sizeof(PipeName), SBSndErr.EvtBuf[i].PipeId); - - CFE_EVS_SendEventWithAppID(CFE_SB_Q_WR_ERR_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, - "Pipe Write Err,MsgId 0x%x,pipe %s,sender %s,stat %ld", - (unsigned int)CFE_SB_MsgIdToValue(BufDscPtr->MsgId), PipeName, - CFE_SB_GetAppTskName(TskId, FullName), (long)(SBSndErr.EvtBuf[i].OsStatus)); - - /* clear the bit so the task may send this event again */ - CFE_SB_FinishSendEvent(TskId, CFE_SB_Q_WR_ERR_EID_BIT); - } - } - } + return CFE_SB_MessageTxn_GetStatus(Txn); } /*---------------------------------------------------------------- @@ -1741,253 +1333,44 @@ void CFE_SB_BroadcastBufferToRoute(CFE_SB_BufferD_t *BufDscPtr, CFE_SBR_RouteId_ * See description in header file for argument/return detail * *-----------------------------------------------------------------*/ -CFE_Status_t CFE_SB_ReceiveBuffer(CFE_SB_Buffer_t **BufPtr, CFE_SB_PipeId_t PipeId, int32 TimeOut) +CFE_Status_t CFE_SB_ReceiveBufferWithRoute(CFE_SB_PipeId_t PipeId, const CFE_SB_Buffer_t **BufPtr, + CFE_MSG_Size_t *BufSize, CFE_SB_MsgId_t *RoutingMsgId, bool EnableVerify, + 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; + CFE_SB_ReceiveTxn_State_t TxnBuf; + CFE_SB_MessageTxn_State_t *Txn; - /* - * Check input args and see if any are bad, which require - * a "BAD_ARG_EID" to be generated. - * - * Also translate the timeout here. Timeouts greater than 0 - * may be passed to OSAL directly, but the two fixed constants - * CFE_SB_PEND_FOREVER and CFE_SB_POLL are checked explicitly, - * to maintain API independence - even though the values are - * currently defined the same. - */ - - if (BufPtr == NULL) - { - PendingEventID = CFE_SB_RCV_BAD_ARG_EID; - Status = CFE_SB_BAD_ARGUMENT; - } - else if (TimeOut > 0) - { - /* time outs greater than 0 can be passed to OSAL directly */ - SysTimeout = TimeOut; - } - else if (TimeOut == CFE_SB_POLL) - { - SysTimeout = OS_CHECK; - } - else if (TimeOut != CFE_SB_PEND_FOREVER) - { - /* any other timeout value is invalid */ - PendingEventID = CFE_SB_RCV_BAD_ARG_EID; - Status = CFE_SB_BAD_ARGUMENT; - } + 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); - - /* - * 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 (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_ReceiveTxn_SetPipeId(Txn, PipeId); + CFE_SB_ReceiveTxn_SetVerify(Txn, EnableVerify); } - /* 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. - */ - 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); + *BufPtr = CFE_SB_ReceiveTxn_Execute(Txn); } - /* Before unlocking, increment relevant error counter if needed */ - if (Status != CFE_SUCCESS && Status != CFE_SB_NO_MESSAGE && Status != CFE_SB_TIME_OUT) + /* Export the other details if the user asked for them */ + if (BufSize != NULL) { - 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; - } + *BufSize = CFE_SB_MessageTxn_GetContentSize(Txn); } - - CFE_SB_UnlockSharedData(__func__, __LINE__); - - /* Now actually send the event, after unlocking (do not call EVS with SB locked) */ - if (PendingEventID != 0) + if (RoutingMsgId != NULL) { - /* 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; - } + *RoutingMsgId = CFE_SB_MessageTxn_GetRoutingMsgId(Txn); } - /* If not successful, set the output pointer to NULL */ - if (Status != CFE_SUCCESS && BufPtr != NULL) - { - *BufPtr = NULL; - } + CFE_SB_MessageTxn_ReportEvents(Txn); - return Status; + return CFE_SB_MessageTxn_GetStatus(Txn); } /*---------------------------------------------------------------- @@ -2050,13 +1433,14 @@ CFE_SB_Buffer_t *CFE_SB_AllocateMessageBuffer(size_t MsgSize) /*---------------------------------------------------------------- * - * Application-scope internal function + * Implemented per public API * See description in header file for argument/return detail * *-----------------------------------------------------------------*/ -int32 CFE_SB_ZeroCopyBufferValidate(CFE_SB_Buffer_t *BufPtr, CFE_SB_BufferD_t **BufDscPtr) +CFE_Status_t CFE_SB_ReleaseMessageBuffer(CFE_SB_Buffer_t *BufPtr) { - cpuaddr BufDscAddr; + CFE_SB_BufferD_t *BufDscPtr; + int32 Status; /* * Sanity Check that the pointers are not NULL @@ -2066,23 +1450,19 @@ int32 CFE_SB_ZeroCopyBufferValidate(CFE_SB_Buffer_t *BufPtr, CFE_SB_BufferD_t ** return CFE_SB_BAD_ARGUMENT; } - /* - * Calculate descriptor pointer from buffer pointer - - * The buffer is just a member (offset) in the descriptor - */ - BufDscAddr = (cpuaddr)BufPtr - offsetof(CFE_SB_BufferD_t, Content); - *BufDscPtr = (CFE_SB_BufferD_t *)BufDscAddr; + CFE_SB_LockSharedData(__func__, __LINE__); - /* - * Check that the descriptor is actually a "zero copy" type, - */ - if (!CFE_RESOURCEID_TEST_DEFINED((*BufDscPtr)->AppId)) + Status = CFE_SB_ZeroCopyBufferValidate(BufPtr, &BufDscPtr); + if (Status == CFE_SUCCESS) { - return CFE_SB_BUFFER_INVALID; + /* Clear the ownership app ID and decrement use count (may also free) */ + BufDscPtr->AppId = CFE_ES_APPID_UNDEFINED; + CFE_SB_DecrBufUseCnt(BufDscPtr); } - /* Basic sanity check passed */ - return CFE_SUCCESS; + CFE_SB_UnlockSharedData(__func__, __LINE__); + + return Status; } /*---------------------------------------------------------------- @@ -2091,25 +1471,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 EnableUpdate) { - 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_TransmitTxn_SetUpdate(Txn, EnableUpdate); + + CFE_SB_TransmitTxn_Execute(Txn, BufPtr); } - CFE_SB_UnlockSharedData(__func__, __LINE__); + /* send an event for each pipe write error that may have occurred */ + CFE_SB_MessageTxn_ReportEvents(Txn); - return Status; + return CFE_SB_MessageTxn_GetStatus(Txn); } /*---------------------------------------------------------------- @@ -2118,52 +1504,100 @@ CFE_Status_t CFE_SB_ReleaseMessageBuffer(CFE_SB_Buffer_t *BufPtr) * See description in header file for argument/return detail * *-----------------------------------------------------------------*/ -CFE_Status_t CFE_SB_TransmitBuffer(CFE_SB_Buffer_t *BufPtr, bool UpdateHeader) +CFE_Status_t CFE_SB_TransmitMsg(const CFE_MSG_Message_t *MsgPtr, bool EnableUpdate) { - 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); + } + + 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) + { + CFE_SB_MessageTxn_SetEventAndStatus(Txn, CFE_SB_GET_BUF_ERR_EID, CFE_SB_BUF_ALOC_ERR); + } + } + + /* + * 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)) + { + /* Copy actual message content into buffer */ + memcpy(&BufPtr->Msg, MsgPtr, CFE_SB_MessageTxn_GetContentSize(Txn)); + + /* Save passed-in parameters */ + CFE_SB_TransmitTxn_SetUpdate(Txn, EnableUpdate); + + CFE_SB_TransmitTxn_Execute(Txn, BufPtr); /* - * 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). + * The broadcast function consumes the buffer, so it should not be + * accessed in this function anymore */ - if (Status == CFE_SUCCESS) - { - BufDscPtr->NeedsUpdate = UpdateHeader; - CFE_MSG_GetType(&BufPtr->Msg, &BufDscPtr->ContentType); + BufPtr = NULL; + } - /* Now broadcast the message, which consumes the buffer */ - CFE_SB_BroadcastBufferToRoute(BufDscPtr, RouteId); + /* send an event for each pipe write error that may have occurred */ + CFE_SB_MessageTxn_ReportEvents(Txn); - /* - * IMPORTANT - the descriptor might be freed at any time after this, - * so the descriptor should not be accessed again after this point. - */ - BufDscPtr = NULL; - } + return CFE_SB_MessageTxn_GetStatus(Txn); +} + +/*---------------------------------------------------------------- + * + * Implemented per public API + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +CFE_Status_t CFE_SB_TransmitBufferWithRoute(CFE_SB_Buffer_t *BufPtr, size_t ContentSize, CFE_SB_MsgId_t RoutingMsgId, + bool EnableUpdate, int32 TimeOut) +{ + CFE_SB_TransmitTxn_State_t TxnBuf; + CFE_SB_MessageTxn_State_t *Txn; + + Txn = CFE_SB_TransmitTxn_Init(&TxnBuf, BufPtr); + + /* + * Sanity Check that the pointers are not NULL + */ + if (CFE_SB_MessageTxn_IsOK(Txn)) + { + /* Save passed-in parameters and execute */ + CFE_SB_MessageTxn_SetContentSize(Txn, ContentSize); + CFE_SB_MessageTxn_SetRoutingMsgId(Txn, RoutingMsgId); + CFE_SB_MessageTxn_SetTimeout(Txn, TimeOut); + CFE_SB_TransmitTxn_SetUpdate(Txn, EnableUpdate); } - if (Status != CFE_SUCCESS) + 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__); + CFE_SB_TransmitTxn_Execute(Txn, BufPtr); } - 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..4d04b7dc2 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,1050 @@ int32 CFE_SB_ZeroCopyReleaseAppId(CFE_ES_AppId_t AppId) return CFE_SUCCESS; } + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +int32 CFE_SB_ZeroCopyBufferValidate(CFE_SB_Buffer_t *BufPtr, CFE_SB_BufferD_t **BufDscPtr) +{ + cpuaddr BufDscAddr; + + /* + * Calculate descriptor pointer from buffer pointer - + * The buffer is just a member (offset) in the descriptor + */ + BufDscAddr = (cpuaddr)BufPtr - offsetof(CFE_SB_BufferD_t, Content); + *BufDscPtr = (CFE_SB_BufferD_t *)BufDscAddr; + + /* + * Check that the descriptor is actually a "zero copy" type, + */ + if (!CFE_RESOURCEID_TEST_DEFINED((*BufDscPtr)->AppId)) + { + return CFE_SB_BUFFER_INVALID; + } + + /* Basic sanity check passed */ + return CFE_SUCCESS; +} + +/****************************************************************** + * + * MESSAGE TRANSACTION IMPLEMENTATION FUNCTIONS + * + ******************************************************************/ + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +void CFE_SB_MessageTxn_Init(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_PipeSetEntry_t *PipeSet, uint16 MaxPipes, + const void *RefMemPtr) +{ + memset(TxnPtr, 0, offsetof(CFE_SB_TransmitTxn_State_t, DestSet)); + TxnPtr->PipeSet = PipeSet; + TxnPtr->MaxPipes = MaxPipes; + + /* The reference pointer is kept just for (potential) reporting in events, etc. + * It is not dereferenced from here. */ + TxnPtr->RefMemPtr = RefMemPtr; +} + +/*---------------------------------------------------------------- + * + * Local Helper function + * Not invoked outside of this unit + * + *-----------------------------------------------------------------*/ +void CFE_SB_MessageTxn_SetTimeout(CFE_SB_MessageTxn_State_t *TxnPtr, int32 Timeout) +{ + TxnPtr->UserTimeoutParam = Timeout; + if (Timeout > 0) + { + /* Convert to an absolute timeout */ + TxnPtr->TimeoutMode = CFE_SB_MessageTxn_TimeoutMode_TIMED; + CFE_PSP_GetTime(&TxnPtr->AbsTimeout); + TxnPtr->AbsTimeout = OS_TimeAdd(TxnPtr->AbsTimeout, OS_TimeFromTotalMilliseconds(Timeout)); + } + else if (Timeout == CFE_SB_POLL) + { + TxnPtr->TimeoutMode = CFE_SB_MessageTxn_TimeoutMode_POLL; + } + else if (Timeout == CFE_SB_PEND_FOREVER) + { + TxnPtr->TimeoutMode = CFE_SB_MessageTxn_TimeoutMode_PEND; + } + else + { + CFE_SB_MessageTxn_SetEventAndStatus(TxnPtr, CFE_SB_RCV_BAD_ARG_EID, CFE_SB_BAD_ARGUMENT); + } +} + +/*---------------------------------------------------------------- + * + * Local Helper function + * Not invoked outside of this unit + * + *-----------------------------------------------------------------*/ +void CFE_SB_MessageTxn_GetEventDetails(const CFE_SB_MessageTxn_State_t *TxnPtr, const CFE_SB_PipeSetEntry_t *ContextPtr, + uint16 EventId, CFE_ES_TaskId_t TskId, char *EvtMsg, size_t EvtMsgSize, + CFE_EVS_EventType_Enum_t *EventType, int32 *ReqBit) +{ + char FullName[(OS_MAX_API_NAME * 2)]; + char PipeName[OS_MAX_API_NAME]; + int32 LocalOsStatus; + + if (ContextPtr == NULL) + { + LocalOsStatus = INT32_MIN; /* should not be used; do not alias any actual OS status */ + } + else + { + LocalOsStatus = ContextPtr->OsStatus; + if (ContextPtr->PendingEventId == CFE_SB_BAD_PIPEID_EID) + { + /* do not attempt to map to a pipe name if reporting a bad pipeID event - use its ID */ + snprintf(PipeName, sizeof(PipeName), "%lu", CFE_RESOURCEID_TO_ULONG(ContextPtr->PipeId)); + } + else + { + CFE_SB_GetPipeName(PipeName, sizeof(PipeName), ContextPtr->PipeId); + } + } + + switch (EventId) + { + case CFE_SB_SEND_BAD_ARG_EID: + *ReqBit = CFE_SB_SEND_BAD_ARG_EID_BIT; + *EventType = CFE_EVS_EventType_ERROR; + + snprintf(EvtMsg, EvtMsgSize, "Send Err:Bad input argument,Arg 0x%lx,App %s", + (unsigned long)TxnPtr->RefMemPtr, CFE_SB_GetAppTskName(TskId, FullName)); + break; + + case CFE_SB_SEND_INV_MSGID_EID: + *ReqBit = CFE_SB_SEND_INV_MSGID_EID_BIT; + *EventType = CFE_EVS_EventType_ERROR; + + snprintf(EvtMsg, EvtMsgSize, "Send Err:Invalid MsgId(0x%x)in msg,App %s", + (unsigned int)CFE_SB_MsgIdToValue(TxnPtr->RoutingMsgId), CFE_SB_GetAppTskName(TskId, FullName)); + break; + + case CFE_SB_MSG_TOO_BIG_EID: + *ReqBit = CFE_SB_MSG_TOO_BIG_EID_BIT; + *EventType = CFE_EVS_EventType_ERROR; + + snprintf(EvtMsg, EvtMsgSize, "Send Err:Msg Too Big MsgId=0x%x,app=%s,size=%d,MaxSz=%d", + (unsigned int)CFE_SB_MsgIdToValue(TxnPtr->RoutingMsgId), CFE_SB_GetAppTskName(TskId, FullName), + (int)TxnPtr->ContentSize, CFE_MISSION_SB_MAX_SB_MSG_SIZE); + break; + + case CFE_SB_SEND_NO_SUBS_EID: + *ReqBit = CFE_SB_SEND_NO_SUBS_EID_BIT; + *EventType = CFE_EVS_EventType_INFORMATION; + + snprintf(EvtMsg, EvtMsgSize, "No subscribers for MsgId 0x%x,sender %s", + (unsigned int)CFE_SB_MsgIdToValue(TxnPtr->RoutingMsgId), CFE_SB_GetAppTskName(TskId, FullName)); + break; + + case CFE_SB_GET_BUF_ERR_EID: + *ReqBit = CFE_SB_GET_BUF_ERR_EID_BIT; + *EventType = CFE_EVS_EventType_ERROR; + + snprintf(EvtMsg, EvtMsgSize, "Send Err:Request for Buffer Failed. MsgId 0x%x,app %s,size %d", + (unsigned int)CFE_SB_MsgIdToValue(TxnPtr->RoutingMsgId), CFE_SB_GetAppTskName(TskId, FullName), + (int)TxnPtr->ContentSize); + break; + + case CFE_SB_MSGID_LIM_ERR_EID: + *ReqBit = CFE_SB_MSGID_LIM_ERR_EID_BIT; + *EventType = CFE_EVS_EventType_ERROR; + + snprintf(EvtMsg, EvtMsgSize, "Msg Limit Err,MsgId 0x%x,pipe %s,sender %s", + (unsigned int)CFE_SB_MsgIdToValue(TxnPtr->RoutingMsgId), PipeName, + CFE_SB_GetAppTskName(TskId, FullName)); + break; + + case CFE_SB_Q_FULL_ERR_EID: + *ReqBit = CFE_SB_Q_FULL_ERR_EID_BIT; + *EventType = CFE_EVS_EventType_ERROR; + + snprintf(EvtMsg, EvtMsgSize, "Pipe Overflow,MsgId 0x%x,pipe %s,sender %s", + (unsigned int)CFE_SB_MsgIdToValue(TxnPtr->RoutingMsgId), PipeName, + CFE_SB_GetAppTskName(TskId, FullName)); + break; + + case CFE_SB_Q_WR_ERR_EID: + *ReqBit = CFE_SB_Q_WR_ERR_EID_BIT; + *EventType = CFE_EVS_EventType_ERROR; + + snprintf(EvtMsg, EvtMsgSize, "Pipe Write Err,MsgId 0x%x,pipe %s,sender %s,stat %ld", + (unsigned int)CFE_SB_MsgIdToValue(TxnPtr->RoutingMsgId), PipeName, + CFE_SB_GetAppTskName(TskId, FullName), (long)LocalOsStatus); + break; + + case CFE_SB_Q_RD_ERR_EID: + *ReqBit = -1; + *EventType = CFE_EVS_EventType_ERROR; + + snprintf(EvtMsg, EvtMsgSize, "Pipe Read Err,pipe %s,app %s,stat %ld", PipeName, + CFE_SB_GetAppTskName(TskId, FullName), (long)LocalOsStatus); + break; + case CFE_SB_RCV_BAD_ARG_EID: + *ReqBit = -1; + *EventType = CFE_EVS_EventType_ERROR; + + snprintf(EvtMsg, EvtMsgSize, "Rcv Err:Bad Input Arg:BufPtr 0x%lx,pipe %s,t/o %d,app %s", + (unsigned long)TxnPtr->RefMemPtr, PipeName, (int)TxnPtr->UserTimeoutParam, + CFE_SB_GetAppTskName(TskId, FullName)); + break; + case CFE_SB_BAD_PIPEID_EID: + *ReqBit = -1; + *EventType = CFE_EVS_EventType_ERROR; + + snprintf(EvtMsg, EvtMsgSize, "Rcv Err:PipeId %s does not exist,app %s", PipeName, + CFE_SB_GetAppTskName(TskId, FullName)); + break; + + default: + EvtMsg[0] = 0; + *ReqBit = 0; + *EventType = 0; + break; + } +} + +/*---------------------------------------------------------------- + * + * Local Helper function + * Not invoked outside of this unit + * + *-----------------------------------------------------------------*/ +bool CFE_SB_MessageTxn_ReportSingleEvent(const CFE_SB_MessageTxn_State_t *TxnPtr, + const CFE_SB_PipeSetEntry_t *ContextPtr, uint16 EventId) +{ + CFE_ES_TaskId_t TskId; + CFE_EVS_EventType_Enum_t EventType; + char Message[CFE_MISSION_EVS_MAX_MESSAGE_LENGTH]; + int32 ReqBit; + + /* get task id for events and Sender Info*/ + CFE_ES_GetTaskID(&TskId); + + CFE_SB_MessageTxn_GetEventDetails(TxnPtr, ContextPtr, EventId, TskId, Message, sizeof(Message), &EventType, + &ReqBit); + + if (EventType > 0 && CFE_SB_RequestToSendEvent(TskId, ReqBit) == CFE_SB_GRANTED) + { + CFE_EVS_SendEventWithAppID(EventId, EventType, CFE_SB_Global.AppId, "%s", Message); + + /* clear the bit so the task may send this event again */ + CFE_SB_FinishSendEvent(TskId, ReqBit); + } + + /* If the event type was an error, this should increment the general error counter */ + return (EventType >= CFE_EVS_EventType_ERROR); +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +void CFE_SB_MessageTxn_ReportEvents(const CFE_SB_MessageTxn_State_t *TxnPtr) +{ + uint32 i; + uint32 NumErrors; + bool IsError; + + NumErrors = 0; + + if (TxnPtr->TransactionEventId != 0) + { + IsError = CFE_SB_MessageTxn_ReportSingleEvent(TxnPtr, NULL, TxnPtr->TransactionEventId); + if (IsError) + { + ++NumErrors; + } + } + + for (i = 0; i < TxnPtr->NumPipes; ++i) + { + if (TxnPtr->PipeSet[i].PendingEventId != 0) + { + IsError = + CFE_SB_MessageTxn_ReportSingleEvent(TxnPtr, &TxnPtr->PipeSet[i], TxnPtr->PipeSet[i].PendingEventId); + if (IsError) + { + ++NumErrors; + } + } + } + + if (NumErrors > 0) + { + /* + * Increment the error only if there was a real error, + * such as a validation issue or failure to allocate a buffer. + * + * Note that transmit side just has one error counter, whereas + * receive side has two - these differeniate between a bad passed-in + * arg vs some other internal error such as queue access. + */ + CFE_SB_LockSharedData(__func__, __LINE__); + + if (TxnPtr->IsTransmit) + { + CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter++; + } + else if (TxnPtr->Status == CFE_SB_BAD_ARGUMENT) + { + ++CFE_SB_Global.HKTlmMsg.Payload.MsgReceiveErrorCounter; + } + else + { + /* For any other unexpected error (e.g. CFE_SB_Q_RD_ERR_EID) */ + ++CFE_SB_Global.HKTlmMsg.Payload.InternalErrorCounter; + } + + CFE_SB_UnlockSharedData(__func__, __LINE__); + } +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +void CFE_SB_MessageTxn_SetEventAndStatus(CFE_SB_MessageTxn_State_t *TxnPtr, uint16 EventId, CFE_Status_t Status) +{ + if (TxnPtr->TransactionEventId == 0) + { + TxnPtr->TransactionEventId = EventId; + } + if (TxnPtr->Status == CFE_SUCCESS) + { + TxnPtr->Status = Status; + } +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +bool CFE_SB_MessageTxn_IsOK(const CFE_SB_MessageTxn_State_t *TxnPtr) +{ + return (TxnPtr->Status == CFE_SUCCESS && TxnPtr->TransactionEventId == 0); +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +void CFE_SB_MessageTxn_SetRoutingMsgId(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_MsgId_t RoutingMsgId) +{ + /* validate the msgid in the message */ + if (!CFE_SB_IsValidMsgId(RoutingMsgId)) + { + CFE_SB_MessageTxn_SetEventAndStatus(TxnPtr, CFE_SB_SEND_INV_MSGID_EID, CFE_SB_BAD_ARGUMENT); + } + else + { + TxnPtr->RoutingMsgId = RoutingMsgId; + } +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +void CFE_SB_MessageTxn_SetContentSize(CFE_SB_MessageTxn_State_t *TxnPtr, size_t ContentSize) +{ + if (ContentSize > CFE_MISSION_SB_MAX_SB_MSG_SIZE) + { + CFE_SB_MessageTxn_SetEventAndStatus(TxnPtr, CFE_SB_MSG_TOO_BIG_EID, CFE_SB_MSG_TOO_BIG); + } + else + { + TxnPtr->ContentSize = ContentSize; + } +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +void CFE_SB_TransmitTxn_SetupFromMsg(CFE_SB_MessageTxn_State_t *TxnPtr, const CFE_MSG_Message_t *MsgPtr) +{ + CFE_Status_t Status; + CFE_MSG_Size_t MsgSize; + CFE_SB_MsgId_t MsgId; + + if (CFE_SB_MessageTxn_IsOK(TxnPtr)) + { + /* In this context, the user should have set the the size and MsgId in the content */ + Status = CFE_MSG_GetMsgId(MsgPtr, &MsgId); + if (Status == CFE_SUCCESS) + { + CFE_SB_MessageTxn_SetRoutingMsgId(TxnPtr, MsgId); + } + else + { + CFE_SB_MessageTxn_SetEventAndStatus(TxnPtr, CFE_SB_SEND_BAD_ARG_EID, Status); + } + } + + if (CFE_SB_MessageTxn_IsOK(TxnPtr)) + { + Status = CFE_MSG_GetSize(MsgPtr, &MsgSize); + if (Status == CFE_SUCCESS) + { + CFE_SB_MessageTxn_SetContentSize(TxnPtr, MsgSize); + } + else + { + CFE_SB_MessageTxn_SetEventAndStatus(TxnPtr, CFE_SB_SEND_BAD_ARG_EID, Status); + } + } +} + +/****************************************************************** + * + * TRANSMIT TRANSACTION IMPLEMENTATION FUNCTIONS + * + ******************************************************************/ + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +CFE_SB_MessageTxn_State_t *CFE_SB_TransmitTxn_Init(CFE_SB_TransmitTxn_State_t *TxnPtr, const void *RefMemPtr) +{ + CFE_SB_MessageTxn_Init(&TxnPtr->MessageTxn_State, TxnPtr->DestSet, CFE_PLATFORM_SB_MAX_DEST_PER_PKT, RefMemPtr); + TxnPtr->MessageTxn_State.IsTransmit = true; + + /* No matter what, the mem pointer from the caller should not be NULL */ + if (RefMemPtr == NULL) + { + CFE_SB_MessageTxn_SetEventAndStatus(&TxnPtr->MessageTxn_State, CFE_SB_SEND_BAD_ARG_EID, CFE_SB_BAD_ARGUMENT); + } + + return &TxnPtr->MessageTxn_State; +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +void CFE_SB_TransmitTxn_SetUpdate(CFE_SB_MessageTxn_State_t *TxnPtr, bool EnableUpdate) +{ + TxnPtr->UpdateVerifyReq = EnableUpdate; +} + +/*---------------------------------------------------------------- + * + * 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; + + /* + * 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)) + { + /* Set the seq count if requested (while locked) before actually sending */ + if (TxnPtr->UpdateVerifyReq) + { + CFE_SBR_IncrementSequenceCounter(BufDscPtr->DestRouteId); + + /* Update all MSG headers based on the current sequence */ + CFE_MSG_UpdateHeader(&BufDscPtr->Content.Msg, CFE_SBR_GetSequenceCounter(BufDscPtr->DestRouteId), + TxnPtr->ContentSize); + + /* Clear the flag, just in case */ + TxnPtr->UpdateVerifyReq = false; + } + + /* 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__); +} + +/*---------------------------------------------------------------- + * + * 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_ReceiveTxn_SetVerify(CFE_SB_MessageTxn_State_t *TxnPtr, bool EnableVerify) +{ + TxnPtr->UpdateVerifyReq = EnableVerify; +} + +/*---------------------------------------------------------------- + * + * 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 VerifyStatus; + + 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->UpdateVerifyReq) + { + CFE_MSG_Verify(&BufDscPtr->Content.Msg, &VerifyStatus); + } + else + { + /* If no verification being done, consider everything "good" */ + VerifyStatus = true; + } + + if (VerifyStatus) + { + /* + * 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_VALIDATION_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..b91c5a4a7 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 UpdateVerifyReq; + + 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,434 @@ CFE_SB_DestinationD_t *CFE_SB_GetDestPtr(CFE_SBR_RouteId_t RouteId, CFE_SB_PipeI **/ size_t CFE_SB_MsgHdrSize(const CFE_MSG_Message_t *MsgPtr); +/* + * Message Transmit/Receive Transaction implementation functions + */ + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Set up transaction initial state + * + * Helper function to initialize the common parts of a transmit/receive transaction + * + * \param[out] TxnPtr Transaction object to initialize + * \param[inout] PipeSet Pointer to pipe entry buffer + * \param[in] MaxPipes Number of entries in PipeSet + * \param[in] RefMemPtr User-supplied Opaque object pointer for event logging + */ +void CFE_SB_MessageTxn_Init(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_PipeSetEntry_t *PipeSet, uint16 MaxPipes, + const void *RefMemPtr); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Check transaction current state + * + * Checks if the transaction is in a good state. + * + * \param[in] TxnPtr Transaction object to check + * \returns Boolean indicating transaction condition + * \retval true if the transaction is OK + * \retval false if the transaction has an error + */ +bool CFE_SB_MessageTxn_IsOK(const CFE_SB_MessageTxn_State_t *TxnPtr); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Get transaction content size + * + * Obtains the size of the object/message being passed in this transaction + * + * \param[in] TxnPtr Transaction object + * \returns Size of message/object being passed + */ +static inline size_t CFE_SB_MessageTxn_GetContentSize(const CFE_SB_MessageTxn_State_t *TxnPtr) +{ + return TxnPtr->ContentSize; +} + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Get transaction MsgId used for routing + * + * Obtains the MsgId used for routing of the object/message being passed in this transaction + * + * \note This MsgId may be different than the one embedded within the message + * + * \param[in] TxnPtr Transaction object + * \returns MsgId used for routing of message/object being passed + */ +static inline CFE_SB_MsgId_t CFE_SB_MessageTxn_GetRoutingMsgId(CFE_SB_MessageTxn_State_t *TxnPtr) +{ + return TxnPtr->RoutingMsgId; +} + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Get transaction status code + * + * Obtains the CFE status code associated with the transaction + * + * \param[in] TxnPtr Transaction object + * \returns CFE Status code + */ +static inline CFE_Status_t CFE_SB_MessageTxn_GetStatus(CFE_SB_MessageTxn_State_t *TxnPtr) +{ + return TxnPtr->Status; +} + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Set transaction status code and event + * + * If an off-nominal condition occurs, this routine sets the specified status and/or event code + * that should be returned to the caller to indicate that condition. + * + * A value of 0 (which is an alias to #CFE_SUCCESS for status codes) can be passed for either + * parameter if there is no defined code for the particular condition. + * + * The transaction object stores the first non-zero value for event ID or status code. + * + * \param[inout] TxnPtr Transaction object + * \param[in] EventId The event ID to set + * \param[in] Status The status code to set + */ +void CFE_SB_MessageTxn_SetEventAndStatus(CFE_SB_MessageTxn_State_t *TxnPtr, uint16 EventId, CFE_Status_t Status); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Set transaction MsgId used for routing + * + * Configures the MsgId used for routing of the object/message being passed in this transaction + * + * \note This MsgId may be different than the one embedded within the message + * + * \param[inout] TxnPtr Transaction object + * \param[in] RoutingMsgId MsgId used for routing of message/object being passed + */ +void CFE_SB_MessageTxn_SetRoutingMsgId(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_MsgId_t RoutingMsgId); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Set transaction content size + * + * Configures the size of the object/message being passed in this transaction + * + * \param[inout] TxnPtr Transaction object + * \param[in] ContentSize Size of message/object being passed + */ +void CFE_SB_MessageTxn_SetContentSize(CFE_SB_MessageTxn_State_t *TxnPtr, size_t ContentSize); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Configure transaction time out + * + * Configures the relative timeout (in milliseconds) for this transaction. This is based + * on sampling the PSP clock at the time this function is invoked, and adding the relative + * time to it. + * + * This should be called early in the transaction configuration process. + * + * \param[inout] TxnPtr Transaction object + * \param[in] Timeout Timeout for message/object transaction + */ +void CFE_SB_MessageTxn_SetTimeout(CFE_SB_MessageTxn_State_t *TxnPtr, int32 Timeout); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief 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 Configure message verification during a receive transaction + * + * Enables message verification during the execution of a receive transaction. If configured + * to "true", then the CFE_MSG_Verify() API will be invoked before the message buffer + * is returned to the caller. If this 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). + * + * \param[inout] TxnPtr Transaction object + * \param[in] EnableVerify Pass as "true" to enable verification during execution + */ +void CFE_SB_ReceiveTxn_SetVerify(CFE_SB_MessageTxn_State_t *TxnPtr, bool EnableVerify); + +/*---------------------------------------------------------------------------------------*/ +/** + * \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 Configure header updates during a transmit transaction + * + * Enables header updates during the execution of a transmit transaction. If configured + * to "true", then the CFE_MSG_UpdateHeader() API will be invoked before the message buffer + * is broadcast to subscribers. + * + * \note Some of the information that is included in a header update comes from the Route - + * and thus needs to be done synchronously with message routing (that is, the header update + * cannot be performed before message routing is performed). That is why the header update, + * if requested, needs be done as part of the message routing and not prior to it. + * + * \param[inout] TxnPtr Transaction object + * \param[in] EnableUpdate Pass as "true" to enable header update during execution + */ +void CFE_SB_TransmitTxn_SetUpdate(CFE_SB_MessageTxn_State_t *TxnPtr, bool EnableUpdate); + +/*---------------------------------------------------------------------------------------*/ +/** + * \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 78c89bab5..a173a1dd6 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); @@ -2841,6 +2841,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->UpdateVerifyReq); +} + +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 */ @@ -2859,14 +3444,62 @@ 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_TransmitBufferWithRoute); + SB_UT_ADD_SUBTEST(Test_AllocateMessageBuffer); SB_UT_ADD_SUBTEST(Test_ReleaseMessageBuffer); } +void Test_TransmitBufferWithRoute(void) +{ + /* Test case for: + * CFE_Status_t CFE_SB_TransmitBufferWithRoute(CFE_SB_Buffer_t *BufPtr, CFE_MSG_Size_t BufSize, CFE_SB_MsgId_t + * RoutingMsgId, bool EnableUpdate, int32 TimeOut); + */ + + CFE_SB_Buffer_t * SendPtr; + const CFE_SB_Buffer_t *ReceivePtr = NULL; + CFE_SB_PipeId_t PipeId = CFE_SB_INVALID_PIPE; + CFE_SB_MsgId_t MsgId = SB_UT_TLM_MID; + CFE_SB_MsgId_t RecvMsgId = CFE_SB_INVALID_MSG_ID; + size_t RecvSize; + + static const uint8_t RandomBytes[] = {0x4a, 0x75, 0x62, 0x34, 0x1d, 0xa5, 0x48, 0x5b, 0xc6, 0xb4, 0x40, 0x3f}; + + CFE_UtAssert_SETUP(CFE_SB_CreatePipe(&PipeId, 3, "TestPipe")); + CFE_UtAssert_SETUP(CFE_SB_Subscribe(MsgId, PipeId)); + + UtAssert_NOT_NULL(SendPtr = CFE_SB_AllocateMessageBuffer(sizeof(SB_UT_Test_Tlm_t))); + memcpy(SendPtr, RandomBytes, sizeof(RandomBytes)); + + /* Test a successful pass */ + CFE_UtAssert_SUCCESS(CFE_SB_TransmitBufferWithRoute(SendPtr, sizeof(RandomBytes), MsgId, false, CFE_SB_POLL)); + CFE_UtAssert_SUCCESS(CFE_SB_ReceiveBufferWithRoute(PipeId, &ReceivePtr, &RecvSize, &RecvMsgId, false, CFE_SB_POLL)); + + UtAssert_ADDRESS_EQ(SendPtr, ReceivePtr); + UtAssert_EQ(size_t, RecvSize, sizeof(RandomBytes)); + CFE_UtAssert_MSGID_EQ(RecvMsgId, MsgId); + + /* Test bad input */ + UtAssert_INT32_EQ(CFE_SB_TransmitBufferWithRoute(NULL, 0, MsgId, false, CFE_SB_POLL), CFE_SB_BAD_ARGUMENT); + UtAssert_INT32_EQ(CFE_SB_TransmitBufferWithRoute(SendPtr, sizeof(RandomBytes), MsgId, false, -1000), + CFE_SB_BAD_ARGUMENT); + + CFE_UtAssert_TEARDOWN(CFE_SB_DeletePipe(PipeId)); +} + /* ** Test response to sending a null message on the software bus */ @@ -3257,9 +3890,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); @@ -3458,113 +4088,95 @@ 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; + bool *VerifyStatus = UT_Hook_GetArgValueByName(Context, "VerifyStatus", bool *); - memset(&TlmPkt, 0, sizeof(TlmPkt)); - - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetSize), &Size, sizeof(Size), false); - - UtAssert_INT32_EQ(CFE_SB_TransmitMsgValidate(CFE_MSG_PTR(TlmPkt.TelemetryHeader), &MsgIdRtn, &SizeRtn, &RouteIdRtn), - CFE_SB_MSG_TOO_BIG); - CFE_UtAssert_MSGID_EQ(MsgIdRtn, MsgId); - UtAssert_INT32_EQ(SizeRtn, Size); - - CFE_UtAssert_EVENTCOUNT(1); - - CFE_UtAssert_EVENTSENT(CFE_SB_MSG_TOO_BIG_EID); + /* Return alternating false/true (this is needed to avoid an endless loop) */ + *VerifyStatus = (UT_GetStubCount(FuncKey) & 1) == 0; } -/* -** Test response to sending a message which has no subscribers -*/ -void Test_TransmitMsgValidate_NoSubscribers(void) +void Test_ReceiveBufferWithRoute(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 case for: + * CFE_Status_t CFE_SB_ReceiveBufferWithRoute(CFE_SB_Buffer_t **BufPtr, CFE_MSG_Size_t *BufSize, CFE_SB_PipeId_t + * RxRoute, bool EnableVerify, int32 TimeOut); + */ -/* -** 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; + CFE_SB_BufferD_t BufDsc[2]; + const void * BufDscP[2]; + CFE_SB_PipeId_t PipeId; + CFE_SB_PipeD_t * PipeDscPtr; + // CFE_SB_MsgId_t TestMsgId = SB_UT_TLM_MID; + CFE_SB_MsgId_t RecvMsgId; + size_t TestSize; + size_t RecvSize; + const CFE_SB_Buffer_t *RecvBufPtr; + + UT_SetHandlerFunction(UT_KEY(CFE_MSG_Verify), UT_CFE_MSG_Verify_CustomHandler, NULL); + CFE_UtAssert_SETUP(CFE_SB_CreatePipe(&PipeId, 3, "TestPipe")); + CFE_UtAssert_SETUP(CFE_SB_Subscribe(SB_UT_TLM_MID, PipeId)); + CFE_UtAssert_SETUP(CFE_SB_Subscribe(SB_UT_CMD_MID, PipeId)); + PipeDscPtr = CFE_SB_LocatePipeDescByID(PipeId); + TestSize = 5; + + memset(BufDsc, 0, sizeof(BufDsc)); + BufDscP[0] = &BufDsc[0]; + BufDsc[0].ContentSize = TestSize; + BufDsc[0].MsgId = SB_UT_TLM_MID; + BufDscP[1] = &BufDsc[1]; + BufDsc[1].ContentSize = TestSize + 1; + BufDsc[1].MsgId = SB_UT_CMD_MID; + CFE_SB_TrackingListReset(&BufDsc[0].Link); + CFE_SB_TrackingListReset(&BufDsc[1].Link); + + /* Bad Input (all subsequent steps skipped) */ + UtAssert_INT32_EQ(CFE_SB_ReceiveBufferWithRoute(PipeId, NULL, NULL, NULL, false, CFE_SB_POLL), CFE_SB_BAD_ARGUMENT); + UtAssert_STUB_COUNT(OS_QueueGet, 0); + UtAssert_STUB_COUNT(CFE_MSG_Verify, 0); - memset(&TlmPkt, 0, sizeof(TlmPkt)); + /* + * Note for these test cases - + * the default stub handlers for OS_QueueGet registers data based on + * its OSAL ID rather than the function name key as is typical. + */ - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); + /* Nominal - no verify */ + RecvBufPtr = NULL; + RecvMsgId = CFE_SB_INVALID_MSG_ID; + RecvSize = 0; + UT_SetDataBuffer(OS_ObjectIdToInteger(PipeDscPtr->SysQueueId), &BufDscP[0], sizeof(BufDscP[0]), false); + UtAssert_INT32_EQ(CFE_SB_ReceiveBufferWithRoute(PipeId, &RecvBufPtr, &RecvSize, &RecvMsgId, false, CFE_SB_POLL), + CFE_SUCCESS); + UtAssert_ADDRESS_EQ(RecvBufPtr, &BufDsc[0].Content); + UtAssert_EQ(size_t, RecvSize, TestSize); + CFE_UtAssert_MSGID_EQ(RecvMsgId, SB_UT_TLM_MID); + UtAssert_STUB_COUNT(CFE_MSG_Verify, 0); + UtAssert_STUB_COUNT(OS_QueueGet, 1); + + /* With verify - two bufs in queue, fail verify first, then verify success */ + RecvMsgId = CFE_SB_INVALID_MSG_ID; + RecvSize = 0; + UT_SetDataBuffer(OS_ObjectIdToInteger(PipeDscPtr->SysQueueId), BufDscP, sizeof(BufDscP), false); + UtAssert_INT32_EQ(CFE_SB_ReceiveBufferWithRoute(PipeId, &RecvBufPtr, &RecvSize, &RecvMsgId, true, CFE_SB_POLL), + CFE_SUCCESS); + UtAssert_ADDRESS_EQ(RecvBufPtr, &BufDsc[1].Content); + UtAssert_EQ(size_t, RecvSize, TestSize + 1); + CFE_UtAssert_MSGID_EQ(RecvMsgId, SB_UT_CMD_MID); + UtAssert_STUB_COUNT(CFE_MSG_Verify, 2); + UtAssert_STUB_COUNT(OS_QueueGet, 3); + + /* With verify - one buf in queue, fails verify, then empty queue */ + UT_SetDataBuffer(OS_ObjectIdToInteger(PipeDscPtr->SysQueueId), &BufDscP[1], sizeof(BufDscP[1]), false); + UtAssert_INT32_EQ(CFE_SB_ReceiveBufferWithRoute(PipeId, &RecvBufPtr, &RecvSize, &RecvMsgId, true, CFE_SB_POLL), + CFE_SB_NO_MESSAGE); + UtAssert_NULL(RecvBufPtr); + UtAssert_EQ(size_t, RecvSize, 0); + CFE_UtAssert_MSGID_EQ(RecvMsgId, CFE_SB_INVALID_MSG_ID); + UtAssert_STUB_COUNT(CFE_MSG_Verify, 3); + UtAssert_STUB_COUNT(OS_QueueGet, 5); - 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); + CFE_UtAssert_TEARDOWN(CFE_SB_DeletePipe(PipeId)); } /* @@ -3579,21 +4191,23 @@ void Test_ReceiveBuffer_API(void) SB_UT_ADD_SUBTEST(Test_ReceiveBuffer_PipeReadError); SB_UT_ADD_SUBTEST(Test_ReceiveBuffer_PendForever); SB_UT_ADD_SUBTEST(Test_ReceiveBuffer_InvalidBufferPtr); + SB_UT_ADD_SUBTEST(Test_ReceiveBufferWithRoute); } 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 */ @@ -3653,8 +4267,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 */ @@ -3746,7 +4360,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); @@ -4140,11 +4754,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); } @@ -4314,281 +4923,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 6a4f59270..8a357a266 100644 --- a/modules/sb/ut-coverage/sb_UT.h +++ b/modules/sb/ut-coverage/sb_UT.h @@ -1803,6 +1803,21 @@ void Test_TransmitMsg_NullPtr(void); ******************************************************************************/ void Test_TransmitMsg_InvalidMsgId(void); +/*****************************************************************************/ +/** +** \brief Test response to sending a source-routed message +** +** \par Description +** This function tests the response to sending a source-routed message +** +** \par Assumptions, External Events, and Notes: +** None +** +** \returns +** This function does not return a value. +******************************************************************************/ +void Test_TransmitBufferWithRoute(void); + /*****************************************************************************/ /** ** \brief Test response to sending a message which has no subscribers @@ -2032,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. @@ -2043,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); /*****************************************************************************/ /** @@ -2075,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); /*****************************************************************************/ /** @@ -2092,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); /*****************************************************************************/ /** @@ -2442,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