diff --git a/modules/core_api/fsw/inc/cfe_sb.h b/modules/core_api/fsw/inc/cfe_sb.h new file mode 100644 index 000000000..ebcdc01bb --- /dev/null +++ b/modules/core_api/fsw/inc/cfe_sb.h @@ -0,0 +1,834 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/** + * @file + * + * Purpose: + * This header file contains all definitions for the cFE Software Bus + * Application Programmer's Interface. + * + * Author: R.McGraw/SSI + * + */ + +#ifndef CFE_SB_H +#define CFE_SB_H + +/* +** Includes +*/ +#include "common_types.h" +#include "cfe_error.h" +#include "cfe_sb_api_typedefs.h" +#include "cfe_es_api_typedefs.h" + +/* +** Macro Definitions +*/ +#define CFE_BIT(x) (1 << (x)) /**< \brief Places a one at bit positions 0 - 31*/ +#define CFE_SET(i, x) ((i) |= CFE_BIT(x)) /**< \brief Sets bit x of i */ +#define CFE_CLR(i, x) ((i) &= ~CFE_BIT(x)) /**< \brief Clears bit x of i */ +#define CFE_TST(i, x) (((i)&CFE_BIT(x)) != 0) /**< \brief true(non zero) if bit x of i is set */ + +/****************** Function Prototypes **********************/ + +/** @defgroup CFEAPISBPipe cFE Pipe Management APIs + * @{ + */ + +/*****************************************************************************/ +/** +** \brief Creates a new software bus pipe. +** +** \par Description +** This routine creates and initializes an input pipe that the calling +** application can use to receive software bus messages. By default, no +** messages are routed to the new pipe. So, the application must use +** #CFE_SB_Subscribe to specify which messages it wants to receive on +** this pipe. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param[in, out] PipeIdPtr A pointer to a variable of type #CFE_SB_PipeId_t, +** which will be filled in with the pipe ID information +** by the #CFE_SB_CreatePipe routine. *PipeIdPtr is the identifier for the created pipe. +** +** \param[in] Depth The maximum number of messages that will be allowed on +** this pipe at one time. +** +** \param[in] PipeName A string to be used to identify this pipe in error messages +** and routing information telemetry. The string must be no +** longer than #OS_MAX_API_NAME (including terminator). +** Longer strings will be truncated. +** +** \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_MAX_PIPES_MET \copybrief CFE_SB_MAX_PIPES_MET +** \retval #CFE_SB_PIPE_CR_ERR \copybrief CFE_SB_PIPE_CR_ERR +** +** \sa #CFE_SB_DeletePipe #CFE_SB_GetPipeOpts #CFE_SB_SetPipeOpts #CFE_SB_GetPipeIdByName +**/ +CFE_Status_t CFE_SB_CreatePipe(CFE_SB_PipeId_t *PipeIdPtr, uint16 Depth, const char *PipeName); + +/*****************************************************************************/ +/** +** \brief Delete a software bus pipe. +** +** \par Description +** This routine deletes an input pipe and cleans up all data structures +** associated with the pipe. All subscriptions made for this pipe by +** calls to #CFE_SB_Subscribe will be automatically removed from the +** SB routing tables. Any messages in the pipe will be discarded. +** +** Applications should not call this routine for all of their +** SB pipes as part of their orderly shutdown process, as the +** pipe will be deleted by the support framework at the +** appropriate time. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param[in] PipeId The pipe ID (obtained previously from #CFE_SB_CreatePipe) +** of the pipe to be deleted. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_SB_BAD_ARGUMENT \copybrief CFE_SB_BAD_ARGUMENT +** +** \sa #CFE_SB_CreatePipe #CFE_SB_GetPipeOpts #CFE_SB_SetPipeOpts #CFE_SB_GetPipeIdByName +**/ +CFE_Status_t CFE_SB_DeletePipe(CFE_SB_PipeId_t PipeId); + +/** + * @brief Obtain an index value correlating to an SB Pipe ID + * + * This calculates a zero based integer value that may be used for indexing + * into a local resource table/array. + * + * Index values are only guaranteed to be unique for resources of the same + * type. For instance, the indices corresponding to two [valid] application + * IDs will never overlap, but the index of a pipe ID and an app ID + * may be the same. Furthermore, indices may be reused if a resource is + * deleted and re-created. + * + * @note There is no inverse of this function - indices cannot be converted + * back to the original PipeID value. The caller should retain the original ID + * for future use. + * + * @param[in] PipeID Pipe ID to convert + * @param[out] Idx Buffer where the calculated index will be stored + * + * @return Execution status, see @ref CFEReturnCodes + * @retval #CFE_SUCCESS @copybrief CFE_SUCCESS + * @retval #CFE_ES_ERR_RESOURCEID_NOT_VALID @copybrief CFE_ES_ERR_RESOURCEID_NOT_VALID + */ +CFE_Status_t CFE_SB_PipeId_ToIndex(CFE_SB_PipeId_t PipeID, uint32 *Idx); + +/*****************************************************************************/ +/** +** \brief Set options on a pipe. +** +** \par Description +** This routine sets (or clears) options to alter the pipe's behavior. +** Options are (re)set every call to this routine. +** +** \param[in] PipeId The pipe ID of the pipe to set options on. +** +** \param[in] Opts A bit field of options. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_SB_BAD_ARGUMENT \copybrief CFE_SB_BAD_ARGUMENT +** +** \sa #CFE_SB_CreatePipe #CFE_SB_DeletePipe #CFE_SB_GetPipeOpts #CFE_SB_GetPipeIdByName #CFE_SB_PIPEOPTS_IGNOREMINE +**/ +CFE_Status_t CFE_SB_SetPipeOpts(CFE_SB_PipeId_t PipeId, uint8 Opts); + +/*****************************************************************************/ +/** +** \brief Get options on a pipe. +** +** \par Description +** This routine gets the current options on a pipe. +** +** \param[in] PipeId The pipe ID of the pipe to get options from. +** +** \param[out] *OptPtr A bit field of options. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_SB_BAD_ARGUMENT \copybrief CFE_SB_BAD_ARGUMENT +** +** \sa #CFE_SB_CreatePipe #CFE_SB_DeletePipe #CFE_SB_SetPipeOpts #CFE_SB_GetPipeIdByName #CFE_SB_PIPEOPTS_IGNOREMINE +**/ +CFE_Status_t CFE_SB_GetPipeOpts(CFE_SB_PipeId_t PipeId, uint8 *OptPtr); + +/*****************************************************************************/ +/** +** \brief Get the pipe name for a given id. +** +** \par Description +** This routine finds the pipe name for a pipe id. +** +** \param[out] PipeNameBuf The buffer to receive the pipe name. +** +** \param[in] PipeNameSize The size (in chars) of the PipeName buffer. +** +** \param[in] PipeId The PipeId for that name. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_SB_BAD_ARGUMENT \copybrief CFE_SB_BAD_ARGUMENT +** +** \sa #CFE_SB_CreatePipe #CFE_SB_DeletePipe #CFE_SB_SetPipeOpts #CFE_SB_GetPipeIdByName +**/ +CFE_Status_t CFE_SB_GetPipeName(char *PipeNameBuf, size_t PipeNameSize, CFE_SB_PipeId_t PipeId); + +/*****************************************************************************/ +/** +** \brief Get pipe id by pipe name. +** +** \par Description +** This routine finds the pipe id for a pipe name. +** +** \param[in] PipeName The name of the pipe. +** +** \param[out] PipeIdPtr The PipeId for that name. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_SB_BAD_ARGUMENT \copybrief CFE_SB_BAD_ARGUMENT +** +** \sa #CFE_SB_CreatePipe #CFE_SB_DeletePipe #CFE_SB_SetPipeOpts #CFE_SB_PIPEOPTS_IGNOREMINE +**/ +CFE_Status_t CFE_SB_GetPipeIdByName(CFE_SB_PipeId_t *PipeIdPtr, const char *PipeName); +/**@}*/ + +/** @defgroup CFEAPISBSubscription cFE Message Subscription Control APIs + * @{ + */ + +/*****************************************************************************/ +/** +** \brief Subscribe to a message on the software bus +** +** \par Description +** This routine adds the specified pipe to the destination list associated +** with the specified message ID. +** +** \par Assumptions, External Events, and Notes: +** Note: As subscriptions are received, the destinations are added to +** the head of a linked list. During the sending of a message, the list +** is traversed beginning at the head of the list. Therefore the +** message will first be sent to the last subscriber. If an application +** has timing constraints and needs to receive a message in the +** shortest possible time, the developer may consider holding off its +** subscription until other applications have subscribed to the message. +** +** \param[in] MsgId The message ID of the message to be subscribed to. +** +** \param[in] PipeId The pipe ID of the pipe the subscribed message +** should be sent to. +** +** \param[in] Quality The requested Quality of Service (QoS) required of +** the messages. Most callers will use #CFE_SB_DEFAULT_QOS +** for this parameter. +** +** \param[in] MsgLim The maximum number of messages with this Message ID to +** allow in this pipe at the same time. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_SB_MAX_MSGS_MET \copybrief CFE_SB_MAX_MSGS_MET +** \retval #CFE_SB_MAX_DESTS_MET \copybrief CFE_SB_MAX_DESTS_MET +** \retval #CFE_SB_BAD_ARGUMENT \copybrief CFE_SB_BAD_ARGUMENT +** \retval #CFE_SB_BUF_ALOC_ERR \copybrief CFE_SB_BUF_ALOC_ERR +** +** \sa #CFE_SB_Subscribe, #CFE_SB_SubscribeLocal, #CFE_SB_Unsubscribe, #CFE_SB_UnsubscribeLocal +**/ +CFE_Status_t CFE_SB_SubscribeEx(CFE_SB_MsgId_t MsgId, CFE_SB_PipeId_t PipeId, CFE_SB_Qos_t Quality, uint16 MsgLim); + +/*****************************************************************************/ +/** +** \brief Subscribe to a message on the software bus with default parameters +** +** \par Description +** This routine adds the specified pipe to the destination list for +** the specified message ID. This is the same as #CFE_SB_SubscribeEx +** with the Quality field set to #CFE_SB_DEFAULT_QOS and MsgLim set +** to #CFE_PLATFORM_SB_DEFAULT_MSG_LIMIT (4). +** +** \par Assumptions, External Events, and Notes: +** Note: As subscriptions are received, the destinations are added to +** the head of a linked list. During the sending of a message, the list +** is traversed beginning at the head of the list. Therefore the +** message will first be sent to the last subscriber. If an application +** has timing constraints and needs to receive a message in the +** shortest possible time, the developer may consider holding off its +** subscription until other applications have subscribed to the message. +** +** \param[in] MsgId The message ID of the message to be subscribed to. +** +** \param[in] PipeId The pipe ID of the pipe the subscribed message +** should be sent to. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_SB_MAX_MSGS_MET \copybrief CFE_SB_MAX_MSGS_MET +** \retval #CFE_SB_MAX_DESTS_MET \copybrief CFE_SB_MAX_DESTS_MET +** \retval #CFE_SB_BAD_ARGUMENT \copybrief CFE_SB_BAD_ARGUMENT +** \retval #CFE_SB_BUF_ALOC_ERR \copybrief CFE_SB_BUF_ALOC_ERR +** +** \sa #CFE_SB_SubscribeEx, #CFE_SB_SubscribeLocal, #CFE_SB_Unsubscribe, #CFE_SB_UnsubscribeLocal +**/ +CFE_Status_t CFE_SB_Subscribe(CFE_SB_MsgId_t MsgId, CFE_SB_PipeId_t PipeId); + +/*****************************************************************************/ +/** +** \brief Subscribe to a message while keeping the request local to a cpu +** +** \par Description +** This routine adds the specified pipe to the destination list for +** the specified message ID. This is similar to #CFE_SB_SubscribeEx +** with the Quality field set to #CFE_SB_DEFAULT_QOS and MsgLim set +** to #CFE_PLATFORM_SB_DEFAULT_MSG_LIMIT, but will not report the subscription. +** Subscription Reporting is enabled for interprocessor communication +** by way of the Software Bus Network (SBN) Application. +** +** \par Assumptions, External Events, and Notes: +** - This API is typically only used by Software Bus Network (SBN) Application +** +** \param[in] MsgId The message ID of the message to be subscribed to. +** +** \param[in] PipeId The pipe ID of the pipe the subscribed message +** should be sent to. +** +** \param[in] MsgLim The maximum number of messages with this Message ID to +** allow in this pipe at the same time. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_SB_MAX_MSGS_MET \copybrief CFE_SB_MAX_MSGS_MET +** \retval #CFE_SB_MAX_DESTS_MET \copybrief CFE_SB_MAX_DESTS_MET +** \retval #CFE_SB_BAD_ARGUMENT \copybrief CFE_SB_BAD_ARGUMENT +** \retval #CFE_SB_BUF_ALOC_ERR \copybrief CFE_SB_BUF_ALOC_ERR +** +** \sa #CFE_SB_Subscribe, #CFE_SB_SubscribeEx, #CFE_SB_Unsubscribe, #CFE_SB_UnsubscribeLocal +**/ +CFE_Status_t CFE_SB_SubscribeLocal(CFE_SB_MsgId_t MsgId, CFE_SB_PipeId_t PipeId, uint16 MsgLim); + +/*****************************************************************************/ +/** +** \brief Remove a subscription to a message on the software bus +** +** \par Description +** This routine removes the specified pipe from the destination +** list for the specified message ID. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param[in] MsgId The message ID of the message to be unsubscribed. +** +** \param[in] PipeId The pipe ID of the pipe the subscribed message +** should no longer be sent to. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_SB_NO_SUBSCRIBERS \copybrief CFE_SB_NO_SUBSCRIBERS +** \retval #CFE_SB_INTERNAL_ERR \copybrief CFE_SB_INTERNAL_ERR +** +** \sa #CFE_SB_Subscribe, #CFE_SB_SubscribeEx, #CFE_SB_SubscribeLocal, #CFE_SB_UnsubscribeLocal +**/ +CFE_Status_t CFE_SB_Unsubscribe(CFE_SB_MsgId_t MsgId, CFE_SB_PipeId_t PipeId); + +/*****************************************************************************/ +/** +** \brief Remove a subscription to a message on the software bus on the current CPU +** +** \par Description +** This routine removes the specified pipe from the destination +** list for the specified message ID on the current CPU. +** +** \par Assumptions, External Events, and Notes: +** - This API is typically only used by Software Bus Network (SBN) Application +** +** \param[in] MsgId The message ID of the message to be unsubscribed. +** +** \param[in] PipeId The pipe ID of the pipe the subscribed message +** should no longer be sent to. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_SB_NO_SUBSCRIBERS \copybrief CFE_SB_NO_SUBSCRIBERS +** \retval #CFE_SB_INTERNAL_ERR \copybrief CFE_SB_INTERNAL_ERR +** +** \sa #CFE_SB_Subscribe, #CFE_SB_SubscribeEx, #CFE_SB_SubscribeLocal, #CFE_SB_Unsubscribe +**/ +CFE_Status_t CFE_SB_UnsubscribeLocal(CFE_SB_MsgId_t MsgId, CFE_SB_PipeId_t PipeId); +/**@}*/ + +/** @defgroup CFEAPISBMessage cFE Send/Receive Message APIs + * @{ + */ + +/*****************************************************************************/ +/** +** \brief Transmit a message +** +** \par Description +** This routine copies the specified message into a software bus +** buffer which is then transmitted to all subscribers. The +** software bus will read the message ID from the message header to +** determine which pipes should receive the message. +** +** \par Assumptions, External Events, and Notes: +** - This routine will not normally wait for the receiver tasks to +** process the message before returning control to the caller's task. +** - However, if a higher priority task is pending and subscribed to +** this message, that task may get to run before returning +** control to the caller. +** +** \param[in] MsgPtr A pointer to the message to be sent. This must point +** to the first byte of the message header. +** \param[in] IncrementSequenceCount Boolean to increment the internally tracked +** sequence count and update the message if the +** buffer contains a telemetry 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 +** \retval #CFE_SB_BUF_ALOC_ERR \copybrief CFE_SB_BUF_ALOC_ERR +**/ +CFE_Status_t CFE_SB_TransmitMsg(CFE_MSG_Message_t *MsgPtr, bool IncrementSequenceCount); + +/*****************************************************************************/ +/** +** \brief Receive a message from a software bus pipe +** +** \par Description +** This routine retrieves the next message from the specified pipe. +** If the pipe is empty, this routine will block until either a new +** message comes in or the timeout value is reached. +** +** \par Assumptions, External Events, and Notes: +** Note - If an error occurs in this API, the *BufPtr value may be NULL or +** random. Therefore, it is recommended that the return code be tested +** for CFE_SUCCESS before processing the message. +** +** \param[in, out] BufPtr A pointer to the software bus buffer to receive to. +** Typically a caller declares a ptr of type CFE_SB_Buffer_t +** (i.e. CFE_SB_Buffer_t *Ptr) then gives the address of that +** pointer (&Ptr) as this parmeter. After a successful +** receipt of a message, *BufPtr will point to the first +** byte of the software bus buffer. This should be +** used as a read-only pointer (in systems with an MMU, +** writes to this pointer may cause a memory protection fault). +** The *BufPtr is valid only until the next call to +** CFE_SB_ReceiveBuffer for the same pipe. +** +** \param[in] PipeId The pipe ID of the pipe containing the message to be obtained. +** +** \param[in] TimeOut The number of milliseconds to wait for a new message if the +** pipe is empty at the time of the call. This can also be set +** to #CFE_SB_POLL for a non-blocking receive or +** #CFE_SB_PEND_FOREVER to wait forever for a message to arrive. +** +** \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 \copybrief CFE_SB_PIPE_RD_ERR +** \retval #CFE_SB_NO_MESSAGE \copybrief CFE_SB_NO_MESSAGE +**/ +CFE_Status_t CFE_SB_ReceiveBuffer(CFE_SB_Buffer_t **BufPtr, CFE_SB_PipeId_t PipeId, int32 TimeOut); +/** @} */ + +/** @defgroup CFEAPISBZeroCopy cFE Zero Copy APIs + * @{ + */ + +/*****************************************************************************/ +/** +** \brief Get a buffer pointer to use for "zero copy" SB sends. +** +** \par Description +** This routine can be used to get a pointer to one of the software bus' +** internal memory buffers that are used for sending messages. The caller +** can use this memory buffer to build an SB message, then send it using +** the CFE_SB_TransmitBuffer() function. This interface avoids an extra +** copy of the message from the user's memory buffer to the software bus +** internal buffer. +** +** \par Assumptions, External Events, and Notes: +** -# The pointer returned by CFE_SB_AllocateMessageBuffer() is only good for one +** call to CFE_SB_TransmitBuffer(). +** -# Once a buffer has been successfully transmitted (as indicated by a successful +** return from CFE_SB_TransmitBuffer()) the buffer becomes owned by the SB application. +** It will automatically be freed by SB once all recipients have finished reading it. +** -# Applications must not de-reference the message pointer (for reading +** or writing) after the call to CFE_SB_TransmitBuffer(). +** +** \param[in] MsgSize The size of the SB message buffer the caller wants +** (including the SB message header). +** +** \return A pointer to a memory buffer that message data can be written to +** for use with CFE_SB_TransmitBuffer(). +**/ +CFE_SB_Buffer_t *CFE_SB_AllocateMessageBuffer(size_t MsgSize); + +/*****************************************************************************/ +/** +** \brief Release an unused "zero copy" buffer pointer. +** +** \par Description +** This routine can be used to release a pointer to one of the software +** bus' internal memory buffers. +** +** \par Assumptions, External Events, and Notes: +** -# This function is not needed for normal "zero copy" transfers. It +** is needed only for cleanup when an application gets a pointer using +** CFE_SB_AllocateMessageBuffer(), but (due to some error condition) never +** uses that pointer in a call to CFE_SB_TransmitBuffer(). +** +** \param[in] BufPtr A pointer to the SB internal buffer. This must be a +** pointer returned by a call to CFE_SB_AllocateMessageBuffer(), +** but never used in a call to CFE_SB_TransmitBuffer(). +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_SB_BUFFER_INVALID \copybrief CFE_SB_BUFFER_INVALID +**/ +CFE_Status_t CFE_SB_ReleaseMessageBuffer(CFE_SB_Buffer_t *BufPtr); + +/*****************************************************************************/ +/** +** \brief Transmit a buffer +** +** \par Description +** This routine sends a message that has been created directly in an +** internal SB message buffer by an application (after a call to +** #CFE_SB_AllocateMessageBuffer). This interface is more complicated than +** the normal #CFE_SB_TransmitMsg interface, but it avoids an extra copy of +** the message from the user's memory buffer to the software bus +** internal buffer. The "zero copy" interface can be used to improve +** performance in high-rate, high-volume software bus traffic. +** +** \par Assumptions, External Events, and Notes: +** -# A handle returned by #CFE_SB_AllocateMessageBuffer is "consumed" by +** a _successful_ call to #CFE_SB_TransmitBuffer. +** -# If this function returns CFE_SUCCESS, this indicates the zero copy handle is +** now owned by software bus, and is no longer owned by the calling application, +** and should not be re-used. +** -# Howver if this function fails (returns any error status) it does not change +** the state of the buffer at all, meaning the calling application still owns it. +** (a failure means the buffer is left in the same state it was before the call). +** -# Applications should be written as if #CFE_SB_AllocateMessageBuffer is +** equivalent to a \c malloc() and a successful call to #CFE_SB_TransmitBuffer +** is equivalent to a \c free(). +** -# Applications must not de-reference the message pointer (for reading +** or writing) after a successful call to #CFE_SB_TransmitBuffer. +** -# This function will increment and apply the internally tracked +** sequence counter if set to do so. +** +** \param[in] BufPtr A pointer to the buffer to be sent. +** \param[in] IncrementSequenceCount Boolean to increment the internally tracked +** sequence count and update the message if the +** buffer contains a telemetry 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 IncrementSequenceCount); + +/** @} */ + +/** @defgroup CFEAPISBSetMessage cFE Setting Message Characteristics APIs + * @{ + */ + +/*****************************************************************************/ +/** +** \brief Sets the length of user data in a software bus message. +** +** \par Description +** This routine sets the field in the SB message header that determines +** the size of the user data in a software bus message. SB message header +** formats can be different for each deployment of the cFE. So, applications +** should use this function rather than trying to poke a length value directly +** into their SB message buffers. +** +** \par Assumptions, External Events, and Notes: +** - You must set a valid message ID in the SB message header before +** calling this function. +** +** \param[in] MsgPtr A pointer to the buffer that contains the software bus message. +** This must point to the first byte of the message header. +** +** \param[in] DataLength The length to set (size of the user data, in bytes). +**/ +void CFE_SB_SetUserDataLength(CFE_MSG_Message_t *MsgPtr, size_t DataLength); + +/*****************************************************************************/ +/** +** \brief Sets the time field in a software bus message with the current spacecraft time. +** +** \par Description +** This routine sets the time of a software bus message with the current +** spacecraft time. This will be the same time that is returned by the +** function #CFE_TIME_GetTime. +** +** \par Assumptions, External Events, and Notes: +** - If the underlying implementation of software bus messages does not +** include a time field, then this routine will do nothing. +** +** \param[in] MsgPtr A pointer to the buffer that contains the software bus message. +** This must point to the first byte of the message header. +**/ +void CFE_SB_TimeStampMsg(CFE_MSG_Message_t *MsgPtr); + +/******************************************************************************/ +/** +** \brief Copies a string into a software bus message +** +** \par Description +** Strings within software bus messages have a defined/fixed maximum length, and +** may not necessarily be null terminated within the message. This presents a possible +** issue when using the C library functions to copy strings out of a message. +** +** This performs a very similar function to "strncpy()" except that the sizes +** of _both_ buffers are passed in. Neither buffer is required to be null-terminated, +** but copying will stop after the first termination character is encountered. +** +** If the destination buffer is not completely filled by the source data (such as if +** the supplied string was shorter than the allotted length) the destination buffer +** will be padded with NUL characters up to the size of the buffer, similar to what +** strncpy() does. This ensures that the entire destination buffer is set. +** +** \note +** If the source string buffer is already guaranteed to be null terminated, +** then there is no difference between the C library "strncpy()" function and this +** implementation. It is only necessary to use this when termination of the source +** buffer is not guaranteed. +** +** \param[out] DestStringPtr Pointer to destination buffer (component of SB message definition) +** \param[in] SourceStringPtr Pointer to source buffer +** \param[in] DestMaxSize Size of destination buffer as defined by the message definition +** \param[in] SourceMaxSize Size of source buffer +** +** \return Number of characters copied or error code, see \ref CFEReturnCodes +** +*/ +int32 CFE_SB_MessageStringSet(char *DestStringPtr, const char *SourceStringPtr, size_t DestMaxSize, + size_t SourceMaxSize); +/** @} */ + +/** @defgroup CFEAPIGetMessage cFE Getting Message Characteristics APIs + * @{ + */ + +/*****************************************************************************/ +/** +** \brief Get a pointer to the user data portion of a software bus message. +** +** \par Description +** This routine returns a pointer to the user data portion of a software +** bus message. SB message header formats can be different for each +** deployment of the cFE. So, applications should use this function and +** avoid hard coding offsets into their SB message buffers. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param[in] MsgPtr A pointer to the buffer that contains the software bus message. +** +** \return A pointer to the first byte of user data within the software bus message. +**/ +void *CFE_SB_GetUserData(CFE_MSG_Message_t *MsgPtr); + +/*****************************************************************************/ +/** +** \brief Gets the length of user data in a software bus message. +** +** \par Description +** This routine returns the size of the user data in a software bus message. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param[in] MsgPtr A pointer to the buffer that contains the software bus message. +** This must point to the first byte of the message header. +** +** \return The size (in bytes) of the user data in the software bus message. +**/ +size_t CFE_SB_GetUserDataLength(const CFE_MSG_Message_t *MsgPtr); + +/******************************************************************************/ +/** +** \brief Copies a string out of a software bus message +** +** \par Description +** Strings within software bus messages have a defined/fixed maximum length, and +** may not necessarily be null terminated within the message. This presents a possible +** issue when using the C library functions to copy strings out of a message. +** +** This function should replace use of C library functions such as strcpy/strncpy +** when copying strings out of software bus messages to local storage buffers. +** +** Up to [SourceMaxSize] or [DestMaxSize-1] (whichever is smaller) characters will be +** coped from the source buffer to the destination buffer, and a NUL termination +** character will be written to the destination buffer as the last character. +** +** If the DefaultString pointer is non-NULL, it will be used in place of the source +** string if the source is an empty string. This is typically a string constant that +** comes from the platform configuration, allowing default values to be assumed for +** fields that are unspecified. +** +** IMPORTANT - the default string, if specified, must be null terminated. This will +** be the case if a string literal is passed in (the typical/expected use case). +** +** If the default is NULL, then only the source string will be copied, and the result +** will be an empty string if the source was empty. +** +** If the destination buffer is too small to store the entire string, it will be +** truncated, but it will still be null terminated. +** +** \param[out] DestStringPtr Pointer to destination buffer +** \param[in] SourceStringPtr Pointer to source buffer (component of SB message definition) +** \param[in] DefaultString Default string to use if source is empty +** \param[in] DestMaxSize Size of destination storage buffer (must be at least 2) +** \param[in] SourceMaxSize Size of source buffer as defined by the message definition +** +** \return Number of characters copied or error code, see \ref CFEReturnCodes +** +*/ +int32 CFE_SB_MessageStringGet(char *DestStringPtr, const char *SourceStringPtr, const char *DefaultString, + size_t DestMaxSize, size_t SourceMaxSize); +/** @} */ + +/** @defgroup CFEAPISBMessageID cFE Message ID APIs + * @{ + */ + +/*****************************************************************************/ +/** + * \brief Identifies whether a given CFE_SB_MsgId_t is valid + * + * \par Description + * Implements a basic sanity check on the value provided + * + * \return Boolean message ID validity indicator + * \retval true Message ID is within the valid range + * \retval false Message ID is not within the valid range + */ +bool CFE_SB_IsValidMsgId(CFE_SB_MsgId_t MsgId); + +/*****************************************************************************/ +/** + * \brief Identifies whether two #CFE_SB_MsgId_t values are equal + * + * \par Description + * In cases where the #CFE_SB_MsgId_t type is not a simple integer + * type, it may not be possible to do a direct equality check. + * This inline function provides an abstraction for the equality + * check between two #CFE_SB_MsgId_t values. + * + * Applications should transition to using this function to compare + * MsgId values for equality to remain compatible with future versions + * of cFE. + * + * \return Boolean message ID equality indicator + * \retval true Message IDs are Equal + * \retval false Message IDs are not Equal + */ +static inline bool CFE_SB_MsgId_Equal(CFE_SB_MsgId_t MsgId1, CFE_SB_MsgId_t MsgId2) +{ + return CFE_SB_MSGID_UNWRAP_VALUE(MsgId1) == CFE_SB_MSGID_UNWRAP_VALUE(MsgId2); +} + +/*****************************************************************************/ +/** + * \brief Converts a #CFE_SB_MsgId_t to a normal integer + * + * \par Description + * In cases where the #CFE_SB_MsgId_t type is not a simple integer + * type, it is not possible to directly display the value in a + * printf-style statement, use it in a switch() statement, or other + * similar use cases. + * + * This inline function provides the ability to map a #CFE_SB_MsgId_t + * type back into a simple integer value. + * + * Applications should transition to using this function wherever a + * #CFE_SB_MsgId_t type needs to be used as an integer. + * + * \par Assumptions and Notes: + * This negates the type safety that was gained by using a non- + * integer type for the #CFE_SB_MsgId_t value. This should only be used + * in specific cases such as UI display (printf, events, etc) where the + * value is being sent externally. Any internal API calls should be + * updated to use the #CFE_SB_MsgId_t type directly, rather than an + * integer type. + * + * \return Integer representation of the #CFE_SB_MsgId_t + */ +static inline CFE_SB_MsgId_Atom_t CFE_SB_MsgIdToValue(CFE_SB_MsgId_t MsgId) +{ + return CFE_SB_MSGID_UNWRAP_VALUE(MsgId); +} + +/*****************************************************************************/ +/** + * \brief Converts a normal integer into a #CFE_SB_MsgId_t + * + * \par Description + * In cases where the #CFE_SB_MsgId_t type is not a simple integer + * type, it is not possible to directly use an integer value + * supplied via a define or similar method. + * + * This inline function provides the ability to map an integer value + * into a corresponding #CFE_SB_MsgId_t value. + * + * Applications should transition to using this function wherever an + * integer needs to be used for a #CFE_SB_MsgId_t. + * + * \par Assumptions and Notes: + * This negates the type safety that was gained by using a non- + * integer type for the #CFE_SB_MsgId_t value. This should only be + * used in specific cases where the value is coming from an external + * source. Any internal API calls should be updated to return the + * #CFE_SB_MsgId_t type directly, rather than an integer type. + * + * \return #CFE_SB_MsgId_t representation of the integer + */ +static inline CFE_SB_MsgId_t CFE_SB_ValueToMsgId(CFE_SB_MsgId_Atom_t MsgIdValue) +{ + CFE_SB_MsgId_t Result = CFE_SB_MSGID_WRAP_VALUE(MsgIdValue); + return Result; +} +/** @} */ + +#endif /* CFE_SB_H */ diff --git a/modules/core_api/fsw/inc/cfe_sb_api_typedefs.h b/modules/core_api/fsw/inc/cfe_sb_api_typedefs.h new file mode 100644 index 000000000..5cc2e9366 --- /dev/null +++ b/modules/core_api/fsw/inc/cfe_sb_api_typedefs.h @@ -0,0 +1,160 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/** + * @file + * + * Purpose: + * This header file contains all definitions for the cFE Software Bus + * Application Programmer's Interface. + * + * Author: R.McGraw/SSI + * + */ + +#ifndef CFE_SB_API_TYPEDEFS_H +#define CFE_SB_API_TYPEDEFS_H + +/* +** Includes +*/ +#include "common_types.h" +#include "cfe_sb_extern_typedefs.h" +#include "cfe_msg_api_typedefs.h" +#include "cfe_resourceid_api_typedefs.h" +#include "cfe_msg_hdr.h" + +/* +** Defines +*/ +#define CFE_SB_POLL 0 /**< \brief Option used with #CFE_SB_ReceiveBuffer to request immediate pipe status */ +#define CFE_SB_PEND_FOREVER -1 /**< \brief Option used with #CFE_SB_ReceiveBuffer to force a wait for next message */ +#define CFE_SB_SUBSCRIPTION 0 /**< \brief Subtype specifier used in #CFE_SB_SingleSubscriptionTlm_t by SBN App */ +#define CFE_SB_UNSUBSCRIPTION 1 /**< \brief Subtype specified used in #CFE_SB_SingleSubscriptionTlm_t by SBN App */ + +/* ------------------------------------------------------ */ +/* Macro Constants for use with the CFE_SB_MsgId_t type */ +/* ------------------------------------------------------ */ + +/** + * \brief Translation macro to convert from MsgId integer values to opaque/abstract API values + * + * This conversion exists in macro form to allow compile-time evaluation for constants, and + * should not be used directly in application code. + * + * For applications, use the CFE_SB_ValueToMsgId() inline function instead. + * + * \sa CFE_SB_ValueToMsgId() + */ +#define CFE_SB_MSGID_WRAP_VALUE(val) ((CFE_SB_MsgId_t)(val)) + +/** + * \brief Translation macro to convert to MsgId integer values from opaque/abstract API values + * + * This conversion exists in macro form to allow compile-time evaluation for constants, and + * should not be used directly in application code. + * + * For applications, use the CFE_SB_MsgIdToValue() inline function instead. + * + * \sa CFE_SB_MsgIdToValue() + */ +#define CFE_SB_MSGID_UNWRAP_VALUE(mid) ((CFE_SB_MsgId_Atom_t)(mid)) + +/** + * \brief Reserved value for CFE_SB_MsgId_t that will not match any valid MsgId + * + * This rvalue macro can be used for static/compile-time data initialization to ensure that + * the initialized value does not alias to a valid MsgId object. + */ +#define CFE_SB_MSGID_RESERVED CFE_SB_MSGID_WRAP_VALUE(-1) + +/** + * \brief A literal of the CFE_SB_MsgId_t type representing an invalid ID + * + * This value should be used for runtime initialization of CFE_SB_MsgId_t values. + * + * \note This may be a compound literal in a future revision. Per C99, compound + * literals are lvalues, not rvalues, so this value should not be used in + * static/compile-time data initialization. For static data initialization + * purposes (rvalue), #CFE_SB_MSGID_RESERVED should be used instead. + * However, in the current implementation, they are equivalent. + */ +#define CFE_SB_INVALID_MSG_ID CFE_SB_MSGID_RESERVED + +/** + * \brief Cast/Convert a generic CFE_ResourceId_t to a CFE_SB_PipeId_t + */ +#define CFE_SB_PIPEID_C(val) ((CFE_SB_PipeId_t)CFE_RESOURCEID_WRAP(val)) + +/** + * \brief A CFE_SB_PipeId_t value which is always invalid + * + * This may be used as a safe initializer for CFE_SB_PipeId_t values + */ +#define CFE_SB_INVALID_PIPE CFE_SB_PIPEID_C(CFE_RESOURCEID_UNDEFINED) + +/* +** Pipe option bit fields. +*/ +#define CFE_SB_PIPEOPTS_IGNOREMINE \ + 0x00000001 /**< \brief Messages sent by the app that owns this pipe will not be sent to this pipe. */ + +#define CFE_SB_DEFAULT_QOS ((CFE_SB_Qos_t) {0}) /**< \brief Default Qos macro */ + +/* +** Type Definitions +*/ + +/** \brief Software Bus generic message */ +typedef union CFE_SB_Msg +{ + CFE_MSG_Message_t Msg; /**< \brief Base message type without enforced alignment */ + long long int LongInt; /**< \brief Align to support Long Integer */ + long double LongDouble; /**< \brief Align to support Long Double */ +} CFE_SB_Buffer_t; + +#ifndef CFE_OMIT_DEPRECATED_6_8 + +/** \brief Deperecated type to minimize required changes */ +typedef CFE_SB_Buffer_t CFE_SB_Msg_t; + +/** \brief Deperecated type to minimize required changes */ +typedef CFE_MSG_CommandHeader_t CFE_SB_CmdHdr_t; + +/** \brief Deperecated type to minimize required changes */ +typedef CFE_MSG_TelemetryHeader_t CFE_SB_TlmHdr_t; + +#define CFE_SB_CMD_HDR_SIZE (sizeof(CFE_MSG_CommandHeader_t)) /**< \brief Size of command header */ +#define CFE_SB_TLM_HDR_SIZE (sizeof(CFE_MSG_TelemetryHeader_t)) /**< \brief Size of telemetry header */ + +/** \brief Pointer to an SB Message */ +typedef CFE_MSG_Message_t *CFE_SB_MsgPtr_t; + +/** \brief CFE_SB_MsgPayloadPtr_t defined as an opaque pointer to a message Payload portion */ +typedef uint8 *CFE_SB_MsgPayloadPtr_t; + +#define CFE_SB_Default_Qos CFE_SB_DEFAULT_QOS /**< \deprecated use CFE_SB_DEFAULT_QOS */ + +#define CFE_SB_CMD_HDR_SIZE (sizeof(CFE_MSG_CommandHeader_t)) /**< \brief Size of command header */ +#define CFE_SB_TLM_HDR_SIZE (sizeof(CFE_MSG_TelemetryHeader_t)) /**< \brief Size of telemetry header */ + +#endif /* CFE_OMIT_DEPRECATED_6_8 */ + +#endif /* CFE_SB_API_TYPEDEFS_H */ diff --git a/modules/core_api/fsw/inc/cfe_sb_core_internal.h b/modules/core_api/fsw/inc/cfe_sb_core_internal.h new file mode 100644 index 000000000..25ee96929 --- /dev/null +++ b/modules/core_api/fsw/inc/cfe_sb_core_internal.h @@ -0,0 +1,88 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/** + * @file + * + * Purpose: + * This header file contains all definitions for the cFE Software Bus + * Application Programmer's Interface. + * + * Author: R.McGraw/SSI + * + */ + +#ifndef CFE_SB_CORE_INTERNAL_H +#define CFE_SB_CORE_INTERNAL_H + +#include "common_types.h" +#include "cfe_es_extern_typedefs.h" + +/* + * The internal APIs prototyped within this block are only intended to be invoked from + * other CFE core apps. They still need to be prototyped in the shared header such that + * they can be called from other core modules, but applications should not call these. + */ + +/** @defgroup CFEAPISBCoreInternal cFE Internal Software Bus APIs, internal to CFE core + * @{ + */ + +/*****************************************************************************/ +/** +** \brief Entry Point for cFE Core Application +** +** \par Description +** This is the entry point to the cFE SB Core Application. +** +** \par Assumptions, External Events, and Notes: +** None +** +******************************************************************************/ +extern void CFE_SB_TaskMain(void); + +/*****************************************************************************/ +/** +** \brief Initializes the cFE core module API Library +** +** \par Description +** Initializes the cFE core module API Library +** +** \par Assumptions, External Events, and Notes: +** -# This function MUST be called before any module API's are called. +** +******************************************************************************/ +extern int32 CFE_SB_EarlyInit(void); + +/*****************************************************************************/ +/** +** \brief Removes SB resources associated with specified Application +** +** \par Description +** This function is called by cFE Executive Services to cleanup after +** an Application has been terminated. It frees resources +** that have been allocated to the specified Application. +** +******************************************************************************/ +extern int32 CFE_SB_CleanUpApp(CFE_ES_AppId_t AppId); + +/**@}*/ + +#endif /* CFE_SB_CORE_INTERNAL_H */ diff --git a/modules/core_api/fsw/inc/cfe_sb_extern_typedefs.h b/modules/core_api/fsw/inc/cfe_sb_extern_typedefs.h new file mode 100644 index 000000000..ae115a7ca --- /dev/null +++ b/modules/core_api/fsw/inc/cfe_sb_extern_typedefs.h @@ -0,0 +1,141 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/** + * @file + * + * Declarations and prototypes for cfe_sb_extern_typedefs module + */ + +#ifndef CFE_SB_EXTERN_TYPEDEFS_H +#define CFE_SB_EXTERN_TYPEDEFS_H + +/* This header may be generated from an EDS file, + * tools are available and the feature is enabled */ +#ifdef CFE_EDS_ENABLED_BUILD + +/* Use the EDS generated version of these types */ +#include "cfe_sb_eds_typedefs.h" + +#else +/* Use the local definitions of these types */ + +#include "common_types.h" +#include "cfe_mission_cfg.h" +#include "cfe_resourceid_typedef.h" + +#define CFE_SB_SUB_ENTRIES_PER_PKT 20 /**< \brief Configuration parameter used by SBN App */ + +/** + * @brief Label definitions associated with CFE_SB_QosPriority_Enum_t + */ +enum CFE_SB_QosPriority +{ + + /** + * @brief Normal priority level + */ + CFE_SB_QosPriority_LOW = 0, + + /** + * @brief High priority + */ + CFE_SB_QosPriority_HIGH = 1 +}; + +/** + * @brief Selects the priorty level for message routing + * + * @sa enum CFE_SB_QosPriority + */ +typedef uint8 CFE_SB_QosPriority_Enum_t; + +/** + * @brief Label definitions associated with CFE_SB_QosReliability_Enum_t + */ +enum CFE_SB_QosReliability +{ + + /** + * @brief Normal (best-effort) reliability + */ + CFE_SB_QosReliability_LOW = 0, + + /** + * @brief High reliability + */ + CFE_SB_QosReliability_HIGH = 1 +}; + +/** + * @brief Selects the reliability level for message routing + * + * @sa enum CFE_SB_QosReliability + */ +typedef uint8 CFE_SB_QosReliability_Enum_t; + +/** + * @brief An integer type that should be used for indexing into the Routing Table + */ +typedef uint16 CFE_SB_RouteId_Atom_t; + +/** + * @brief CFE_SB_MsgId_Atom_t primitive type definition + * + * This is an integer type capable of holding any Message ID value + * Note: This value is limited via #CFE_PLATFORM_SB_HIGHEST_VALID_MSGID + */ +typedef uint32 CFE_SB_MsgId_Atom_t; + +/** + * @brief CFE_SB_MsgId_t type definition + * + * Software Bus message identifier used in many SB APIs + * + * Currently this is directly mapped to the underlying holding type (not wrapped) for + * compatibility with existing usage semantics in apps (mainly switch/case statements) + * + * @note In a future version it could become a type-safe wrapper similar to the route index, + * to avoid message IDs getting mixed between other integer values. + */ +typedef CFE_SB_MsgId_Atom_t CFE_SB_MsgId_t; + +/** \brief CFE_SB_PipeId_t to primitive type definition + * + * Software Bus pipe identifier used in many SB APIs, as well as SB Telemetry messages + * and data files. + */ +typedef CFE_RESOURCEID_BASE_TYPE CFE_SB_PipeId_t; + +/** \brief Quality Of Service Type Definition +** +** Currently an unused parameter in #CFE_SB_SubscribeEx +** Intended to be used for interprocessor communication only +**/ +typedef struct +{ + uint8 Priority; /**< \brief Specify high(1) or low(0) message priority for off-board routing, currently unused */ + uint8 Reliability; /**< \brief Specify high(1) or low(0) message transfer reliability for off-board routing, + currently unused */ +} CFE_SB_Qos_t; + +#endif /* CFE_EDS_ENABLED_BUILD */ + +#endif /* CFE_SB_EXTERN_TYPEDEFS_H */ diff --git a/modules/core_private/fsw/inc/cfe_sb_destination_typedef.h b/modules/core_private/fsw/inc/cfe_sb_destination_typedef.h new file mode 100644 index 000000000..daaf9f95c --- /dev/null +++ b/modules/core_private/fsw/inc/cfe_sb_destination_typedef.h @@ -0,0 +1,54 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/** + * @file + * + * Definition of the CFE_SB_DestinationD_t structure type + * This was moved into its own header file since it is referenced by multiple CFE modules. + */ + +#ifndef CFE_SB_DESTINATION_TYPEDEF_H +#define CFE_SB_DESTINATION_TYPEDEF_H + +#include "common_types.h" +#include "cfe_sb_extern_typedefs.h" /* Required for CFE_SB_PipeId_t definition */ + +/****************************************************************************** + * This structure defines a DESTINATION DESCRIPTOR used to specify + * each destination pipe for a message. + * + * Note: Changing the size of this structure may require the memory pool + * block sizes to change. + */ +typedef struct +{ + CFE_SB_PipeId_t PipeId; + uint8 Active; + uint16 MsgId2PipeLim; + uint16 BuffCount; + uint16 DestCnt; + uint8 Scope; + uint8 Spare[3]; + void * Prev; + void * Next; +} CFE_SB_DestinationD_t; + +#endif /* CFE_SB_DESTINATION_TYPEDEF_H */ diff --git a/modules/core_private/fsw/inc/cfe_sbr.h b/modules/core_private/fsw/inc/cfe_sbr.h new file mode 100644 index 000000000..a35d5f4c2 --- /dev/null +++ b/modules/core_private/fsw/inc/cfe_sbr.h @@ -0,0 +1,175 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/** + * @file + * + * Purpose: + * Prototypes for private functions and type definitions for SB + * routing internal use. + */ + +#ifndef CFE_SBR_H +#define CFE_SBR_H + +/* + * Includes + */ +#include "common_types.h" +#include "cfe_sbr_api_typedefs.h" +#include "cfe_msg_api_typedefs.h" +#include "cfe_sb_destination_typedef.h" + +#include "cfe_platform_cfg.h" + +/****************************************************************************** + * Function prototypes + */ + +/** + * \brief Initialize software bus routing module + */ +void CFE_SBR_Init(void); + +/** + * \brief Add a route for the given a message id + * + * Called for the first subscription to a message ID, uses up one + * element in the routing table. Assumes check for existing + * route was already performed or routes could leak + * + * \param[in] MsgId Message ID of the route to add + * \param[out] CollisionsPtr Number of collisions (if not null) + * + * \returns Route ID, will be invalid if route can not be added + */ +CFE_SBR_RouteId_t CFE_SBR_AddRoute(CFE_SB_MsgId_t MsgId, uint32 *CollisionsPtr); + +/** + * \brief Obtain the route id given a message id + * + * \param[in] MsgId Message ID of the route to get + * + * \returns Route ID, will be invalid if can't be returned + */ +CFE_SBR_RouteId_t CFE_SBR_GetRouteId(CFE_SB_MsgId_t MsgId); + +/** + * \brief Obtain the message id given a route id + * + * \param[in] RouteId Route ID of the message id to get + * + * \returns Message ID, will be invalid if cant be returned + */ +CFE_SB_MsgId_t CFE_SBR_GetMsgId(CFE_SBR_RouteId_t RouteId); + +/** + * \brief Obtain the destination list head pointer given a route id + * + * \param[in] RouteId Route ID + * + * \returns Destination list head pointer for the given route id. + * Will be null if route doesn't exist or no subscribers. + */ +CFE_SB_DestinationD_t *CFE_SBR_GetDestListHeadPtr(CFE_SBR_RouteId_t RouteId); + +/** + * \brief Set the destination list head pointer for given route id + * + * \param[in] RouteId Route Id + * \param[in] DestPtr Destination list head pointer + */ +void CFE_SBR_SetDestListHeadPtr(CFE_SBR_RouteId_t RouteId, CFE_SB_DestinationD_t *DestPtr); + +/** + * \brief Increment the sequence counter associated with the supplied route ID + * + * \param[in] RouteId Route ID + */ +void CFE_SBR_IncrementSequenceCounter(CFE_SBR_RouteId_t RouteId); + +/** + * \brief Get the sequence counter associated with the supplied route ID + * + * \param[in] RouteId Route ID + * + * \returns the sequence counter + */ +CFE_MSG_SequenceCount_t CFE_SBR_GetSequenceCounter(CFE_SBR_RouteId_t RouteId); + +/** + * \brief Call the supplied callback function for all routes + * + * Invokes callback for each route in the table. Message ID order + * depends on the routing table implementation. Possiblities include + * in subscription order and in order if incrementing message ids. + * + * \param[in] CallbackPtr Function to invoke for each matching ID + * \param[in] ArgPtr Opaque argument to pass to callback function + * \param[in,out] ThrottlePtr Throttling structure, NULL for no throttle + */ +void CFE_SBR_ForEachRouteId(CFE_SBR_CallbackPtr_t CallbackPtr, void *ArgPtr, CFE_SBR_Throttle_t *ThrottlePtr); + +/****************************************************************************** +** Inline functions +*/ + +/** + * \brief Identifies whether a given CFE_SBR_RouteId_t is valid + * + * Implements a basic sanity check on the value provided + * + * \returns true if sanity checks passed, false otherwise. + */ +static inline bool CFE_SBR_IsValidRouteId(CFE_SBR_RouteId_t RouteId) +{ + return (RouteId.RouteId != 0 && RouteId.RouteId <= CFE_PLATFORM_SB_MAX_MSG_IDS); +} + +/** + * \brief Converts from raw value to CFE_SBR_RouteId_t + * + * Converts the supplied "bare number" into a type-safe CFE_SBR_RouteId_t value + * + * \returns A CFE_SBR_RouteId_t + */ +static inline CFE_SBR_RouteId_t CFE_SBR_ValueToRouteId(CFE_SB_RouteId_Atom_t Value) +{ + return ((CFE_SBR_RouteId_t) {.RouteId = 1 + Value}); +} + +/** + * \brief Converts from CFE_SBR_RouteId_t to raw value + * + * Converts the supplied route id into a "bare number" suitable for performing + * array lookups or other tasks for which the holding structure cannot be used directly. + * + * Use with caution, as this removes the type safety information from the value. + * + * \note It is assumed the value has already been validated using CFE_SB_IsValidRouteId() + * + * \returns The underlying value + */ +static inline CFE_SB_RouteId_Atom_t CFE_SBR_RouteIdToValue(CFE_SBR_RouteId_t RouteId) +{ + return (RouteId.RouteId - 1); +} + +#endif /* CFE_SBR_H */ diff --git a/modules/core_private/fsw/inc/cfe_sbr_api_typedefs.h b/modules/core_private/fsw/inc/cfe_sbr_api_typedefs.h new file mode 100644 index 000000000..8175e14b8 --- /dev/null +++ b/modules/core_private/fsw/inc/cfe_sbr_api_typedefs.h @@ -0,0 +1,76 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/** + * @file + * + * Purpose: + * Prototypes for private functions and type definitions for SB + * routing internal use. + */ + +#ifndef CFE_SBR_API_TYPEDEFS_H +#define CFE_SBR_API_TYPEDEFS_H + +/* + * Includes + */ +#include "common_types.h" +#include "cfe_sb_extern_typedefs.h" + +/* + * Macro Definitions + */ + +/** \brief Invalid route id */ +#define CFE_SBR_INVALID_ROUTE_ID ((CFE_SBR_RouteId_t) {.RouteId = 0}) + +/****************************************************************************** + * Type Definitions + */ + +/** + * \brief Routing table id + * + * This is intended as a form of "strong typedef" where direct assignments should + * be restricted. Software bus uses numeric indexes into multiple tables to perform + * its duties, and it is important that these index values are distinct and separate + * and not mixed together. + * + * Using this holding structure prevents assignment directly into a different index + * or direct usage as numeric value. + */ +typedef struct +{ + CFE_SB_RouteId_Atom_t RouteId; /**< \brief Holding value, do not use directly in code */ +} CFE_SBR_RouteId_t; + +/** \brief Callback throttling structure */ +typedef struct +{ + uint32 StartIndex; /**< /brief 0 based index to start at */ + uint32 MaxLoop; /**< /brief Max number to process */ + uint32 NextIndex; /**< /brief Next start index (output), 0 if completed */ +} CFE_SBR_Throttle_t; + +/** \brief For each id callback function prototype */ +typedef void (*CFE_SBR_CallbackPtr_t)(CFE_SBR_RouteId_t RouteId, void *ArgPtr); + +#endif /* CFE_SBR_API_TYPEDEFS_H */ diff --git a/modules/sb/CMakeLists.txt b/modules/sb/CMakeLists.txt new file mode 100644 index 000000000..4362a2774 --- /dev/null +++ b/modules/sb/CMakeLists.txt @@ -0,0 +1,39 @@ +################################################################## +# +# cFE Software Bus (SB) module CMake build recipe +# +################################################################## + +project(CFE_SB C) + +# Software Bus source files +set(sb_SOURCES + fsw/src/cfe_sb_api.c + fsw/src/cfe_sb_buf.c + fsw/src/cfe_sb_init.c + fsw/src/cfe_sb_msg_id_util.c + fsw/src/cfe_sb_priv.c + fsw/src/cfe_sb_task.c + fsw/src/cfe_sb_util.c + fsw/src/cfe_sb_api.c + fsw/src/cfe_sb_buf.c + fsw/src/cfe_sb_init.c + fsw/src/cfe_sb_msg_id_util.c + fsw/src/cfe_sb_priv.c + fsw/src/cfe_sb_task.c + fsw/src/cfe_sb_util.c +) +add_library(sb STATIC ${sb_SOURCES}) + +target_include_directories(sb PUBLIC fsw/inc) +target_link_libraries(sb PRIVATE core_private) + +# Add unit test coverage subdirectory +if(ENABLE_UNIT_TESTS) + add_subdirectory(ut-coverage) +endif(ENABLE_UNIT_TESTS) + +cfs_app_check_intf(${DEP} + cfe_sb_msg.h + cfe_sb_events.h +) diff --git a/modules/sb/fsw/inc/cfe_sb_events.h b/modules/sb/fsw/inc/cfe_sb_events.h new file mode 100644 index 000000000..682b3fe35 --- /dev/null +++ b/modules/sb/fsw/inc/cfe_sb_events.h @@ -0,0 +1,906 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/** + * @file + * + * Purpose: + * cFE Software Bus (SB) Event IDs + * + * References: + * Flight Software Branch C Coding Standard Version 1.0a + * cFE Flight Software Application Developers Guide + * + */ + +#ifndef CFE_SB_EVENTS_H +#define CFE_SB_EVENTS_H + +/* ************************** +** ****** Maximum EID. ****** +** ************************** +** The EID's below are not necessarily in order, so it can be difficult to +** determine what the next EID is to use. When you add EID's, start with MAX_EID + 1 +** and when you're done adding, set this to the highest EID you used. It may +** be worthwhile to, on occasion, re-number the EID's to put them back in order. +*/ +#define CFE_SB_MAX_EID 67 + +/* +** SB task event message ID's. +*/ +/* +** Event ID's +*/ + +/** \brief 'cFE SB Initialized' +** \event 'cFE SB Initialized' +** +** \par Type: INFORMATION +** +** \par Cause: +** +** This event message is issued when the Software Bus Task completes its +** initialization. +**/ +#define CFE_SB_INIT_EID 1 + +/** \brief 'CreatePipeErr:Bad Input Arg:app=\%s,ptr=0x\%x,depth=\%d,maxdepth=\%d' +** \event 'CreatePipeErr:Bad Input Arg:app=\%s,ptr=0x\%x,depth=\%d,maxdepth=\%d' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when the #CFE_SB_CreatePipe API receives a bad +** argument. In this case, a bad argument is defined by the following: +** A NULL PipeIdPtr, PipeDepth = 0 and PipeDepth > maximum pipe depth +**/ +#define CFE_SB_CR_PIPE_BAD_ARG_EID 2 + +/** \brief 'CreatePipeErr:Max Pipes(\%d)In Use.app \%s' +** \event 'CreatePipeErr:Max Pipes(\%d)In Use.app \%s' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when the #CFE_SB_CreatePipe API is called and +** the maximum number of pipes (defined by cfg param #CFE_PLATFORM_SB_MAX_PIPES) are in use. +**/ +#define CFE_SB_MAX_PIPES_MET_EID 3 + +/** \brief 'CreatePipeErr:OS_QueueCreate returned \%d,app \%s' +** \event 'CreatePipeErr:OS_QueueCreate returned \%d,app \%s' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when the #CFE_SB_CreatePipe API is called and +** the OS returns an error when the OS returns an error from the OS_QueueCreate API. +** The error status returned by the OS is displayed in the event. Most commonly, +** this event is displayed as a result of trying to create pipes with the same name. +**/ +#define CFE_SB_CR_PIPE_ERR_EID 4 + +/** \brief 'Pipe Created:name \%s,id \%d,app \%s' +** \event 'Pipe Created:name \%s,id \%d,app \%s' +** +** \par Type: DEBUG +** +** \par Cause: +** +** This debug event message is issued when a pipe was successfully created in the +** #CFE_SB_CreatePipe API. +**/ +#define CFE_SB_PIPE_ADDED_EID 5 + +/** \brief 'SetPipeOptsErr:Invalid pipe id (\%d).app \%s' +** \event 'SetPipeOptsErr:Invalid pipe id (\%d).app \%s' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when the #CFE_SB_SetPipeOpts API is called and +** the PipeID is invalid. +**/ +#define CFE_SB_SETPIPEOPTS_ID_ERR_EID 55 + +/** \brief 'SetPipeOptsErr:Caller not owner (\%d).app \%s' +** \event 'SetPipeOptsErr:Caller not owner (\%d).app \%s' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when the #CFE_SB_SetPipeOpts API is called and +** the pipe is owned by another app ID. +**/ +#define CFE_SB_SETPIPEOPTS_OWNER_ERR_EID 56 + +/** \brief 'SetPipeOpts: Options set (\%d). app \%s' +** \event 'SetPipeOpts: Options set (\%d). app \%s' +** +** \par Type: DEBUG +** +** \par Cause: +** +** This debug event is generated when options are set. +**/ +#define CFE_SB_SETPIPEOPTS_EID 57 + +/** \brief 'GetPipeOptsErr:Invalid pipe id (\%d).app \%s' +** \event 'GetPipeOptsErr:Invalid pipe id (\%d).app \%s' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when the #CFE_SB_GetPipeOpts API is called and +** the PipeID is invalid. +**/ +#define CFE_SB_GETPIPEOPTS_ID_ERR_EID 58 + +/** \brief 'GetPipeOptsErr:Invalid opts ptr.app \%s' +** \event 'GetPipeOptsErr:Invalid opts ptr.app \%s' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when the #CFE_SB_GetPipeOpts API is called and +** the pointer is invalid. +**/ +#define CFE_SB_GETPIPEOPTS_PTR_ERR_EID 59 + +/** \brief 'GetPipeOpts: Options retrieved. app \%s' +** \event 'GetPipeOpts: Options retrieved. app \%s' +** +** \par Type: DEBUG +** +** \par Cause: +** +** This debug event is generated when options are retrieved. +**/ +#define CFE_SB_GETPIPEOPTS_EID 60 + +/** \brief 'GetPipeName: Name retrieved. NameOut \%s,Id \%d, app \%s' +** \event 'GetPipeName: Name retrieved. NameOut \%s,Id \%d, app \%s' +** +** \par Type: DEBUG +** +** \par Cause: +** +** This debug event is generated when name is retrieved by id. +**/ +#define CFE_SB_GETPIPENAME_EID 62 + +/** \brief 'GetPipeName: Null ptr error. Id \%d, app \%s' +** \event 'GetPipeName: Null ptr error. Id \%d, app \%s' +** +** \par Type: ERROR +** +** \par Cause: +** +** This debug event is generated when the name buffer ptr is null. +**/ +#define CFE_SB_GETPIPENAME_NULL_PTR_EID 63 + +/** \brief 'GetPipeName: Id error. NameOut \%s,Id \%d, app \%s' +** \event 'GetPipeName: Id error. NameOut \%s,Id \%d, app \%s' +** +** \par Type: ERROR +** +** \par Cause: +** +** This debug event is generated when name is retrieved by id. +**/ +#define CFE_SB_GETPIPENAME_ID_ERR_EID 64 + +/** \brief 'GetPipeIdByName: ID retrieved. Name \%s,IdOut 0x\%x, app \%s' +** \event 'GetPipeIdByName: ID retrieved. Name \%s,IdOut 0x\%x, app \%s' +** +** \par Type: DEBUG +** +** \par Cause: +** +** This debug event is generated when id is retrieved by name. +**/ +#define CFE_SB_GETPIPEIDBYNAME_EID 65 + +/** \brief 'GetPipeIdByName Err:Bad input argument,Name 0x\%x,IdOut 0x%x,App \%s' +** \event 'GetPipeIdByName Err:Bad input argument,Name 0x\%x,IdOut 0x%x,App \%s' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when the #CFE_SB_GetPipeIdByName API receives a +** NULL ptr as an argument. +**/ +#define CFE_SB_GETPIPEIDBYNAME_NULL_ERR_EID 66 + +/** \brief 'GetPipeIdByName Err:Name not found,Name \%s,IdOut 0x%x,App \%s' +** \event 'GetPipeIdByName Err:Name not found,Name \%s,IdOut 0x%x,App \%s' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when the #CFE_SB_GetPipeIdByName API receives an +** invalid name. +**/ +#define CFE_SB_GETPIPEIDBYNAME_NAME_ERR_EID 67 + +/** \brief 'Subscribe Err:Bad Arg,MsgId 0x\%x,PipeId \%d,app \%s,scope \%d' +** \event 'Subscribe Err:Bad Arg,MsgId 0x\%x,PipeId \%d,app \%s,scope \%d' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when one of the Subscribe API's are called +** with an invalid MsgId. An invalid MsgId is defined as being greater than the +** cfg param #CFE_PLATFORM_SB_HIGHEST_VALID_MSGID. +** +**/ +#define CFE_SB_SUB_ARG_ERR_EID 6 + +/** \brief 'Duplicate Subscription,MsgId 0x\%x on \%s pipe,app \%s' +** \event 'Duplicate Subscription,MsgId 0x\%x on \%s pipe,app \%s' +** +** \par Type: INFORMATION +** +** \par Cause: +** +** This info event message is issued when a subscription request is received that +** already exists in the routing table. A duplicate subscription is defined by a +** matching MsgId and PipeId. No other parameters are used in detecting a duplicate +** subscription. +** NOTE: By default, SB filters this event. The EVS filter algorithm allows the +** first event to pass through the filter, but all subsequent events with this +** event id will be filtered. A command must be sent to unfilter this event if +** the user desires to see it. +**/ +#define CFE_SB_DUP_SUBSCRIP_EID 7 + +/** \brief 'Subscribe Err:Max Msgs(\%d)In Use,MsgId 0x\%x,pipe \%s,app \%s' +** \event 'Subscribe Err:Max Msgs(\%d)In Use,MsgId 0x\%x,pipe \%s,app \%s' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when one of the SB subscribe APIs are called +** with a new MsgId, and SB cannot accommodate the new MsgId because the maximum +** number of MsgIds are in use. The maximum number of MsgIds is defined by cfg param +** #CFE_PLATFORM_SB_MAX_MSG_IDS. This cfg param dictates the number of elements in the SB +** routing table. There is one element per MsgId. The user may monitor the routing +** table utilization figures (msgids currently in use, high water mark and max +** allowed) by sending the SB cmd to dump the SB statistics data. +**/ +#define CFE_SB_MAX_MSGS_MET_EID 8 + +/** \brief 'Subscribe Err:Max Dests(\%d)In Use For Msg 0x\%x,pipe \%s,app \%s' +** \event 'Subscribe Err:Max Dests(\%d)In Use For Msg 0x\%x,pipe \%s,app \%s' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when a subscription request is received and +** all destinations for that MsgId are in use. The number of destinations per msgid +** is a configuration parameter named #CFE_PLATFORM_SB_MAX_DEST_PER_PKT. A destination is +** defined as a pipe. +**/ +#define CFE_SB_MAX_DESTS_MET_EID 9 + +/** \brief 'Subscription Rcvd:MsgId 0x\%x on \%s(\%d),app \%s' +** \event 'Subscription Rcvd:MsgId 0x\%x on \%s(\%d),app \%s' +** +** \par Type: DEBUG +** +** \par Cause: +** +** This debug event message is issued when a subscription is successfully made +** through one of the SB Subscribe API's +**/ +#define CFE_SB_SUBSCRIPTION_RCVD_EID 10 + +/** \brief 'UnSubscribe Err:Bad Arg,MsgId 0x\%x,PipeId \%d,app \%s,scope \%d' +** \event 'UnSubscribe Err:Bad Arg,MsgId 0x\%x,PipeId \%d,app \%s,scope \%d' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when a request to unsubscribe fails due to +** an invalid msgid or an invalid pipeid in one of SB's unsubscribe API's. The msgid +** must be less than cfg param #CFE_PLATFORM_SB_HIGHEST_VALID_MSGID and the pipeid must have +** been created and have a value less than cfg param #CFE_PLATFORM_SB_MAX_PIPES. The SB pipe +** table may be viewed to verify its value or existence. +**/ +#define CFE_SB_UNSUB_ARG_ERR_EID 11 + +/** \brief 'Unsubscribe Err:No subs for Msg 0x\%x on \%s,app \%s' +** \event 'Unsubscribe Err:No subs for Msg 0x\%x on \%s,app \%s' +** +** \par Type: INFORMATION +** +** \par Cause: +** +** This info event message is issued when a request to unsubscribe fails due to +** a non existent msgid/pipeid combination in the SB routing table. The SB routing +** table may be viewed to see a list of valid msgid/pipeid combinations. +**/ +#define CFE_SB_UNSUB_NO_SUBS_EID 12 + +/** \brief 'Send Err:Bad input argument,Arg 0x\%x,App \%s' +** \event 'Send Err:Bad input argument,Arg 0x\%x,App \%s' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when a transmit API receives an +** invalid (possibly NULL) ptr as an argument. +**/ +#define CFE_SB_SEND_BAD_ARG_EID 13 + +/** \brief 'No subscribers for MsgId 0x\%x,sender \%s' +** \event 'No subscribers for MsgId 0x\%x,sender \%s' +** +** \par Type: INFORMATION +** +** \par Cause: +** +** This info event message is issued when a transmit API is called and there +** are no subscribers (therefore no destinations) for the message to be sent. Each +** time the SB detects this situation, the corresponding SB telemetry point is +** incremented.. +** NOTE: By default, SB filters this event. The EVS filter algorithm allows the +** first event to pass through the filter, but all subsequent events with this +** event id will be filtered. A command must be sent to unfilter this event if +** the user desires to see it. +**/ +#define CFE_SB_SEND_NO_SUBS_EID 14 + +/** \brief 'Send Err:Msg Too Big MsgId=0x\%x,app=\%s,size=\%d,MaxSz=\%d' +** \event 'Send Err:Msg Too Big MsgId=0x\%x,app=\%s,size=\%d,MaxSz=\%d' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when a transmit API is called and the +** packet length field in the message header implies that the message size exceeds +** the max size defined by mission cfg param #CFE_MISSION_SB_MAX_SB_MSG_SIZE. The request to +** send the message is denied, there is no partial packet sent. +**/ +#define CFE_SB_MSG_TOO_BIG_EID 15 + +/** \brief 'Send Err:Request for Buffer Failed. MsgId 0x\%x,app \%s,size \%d' +** \event 'Send Err:Request for Buffer Failed. MsgId 0x\%x,app \%s,size \%d' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when a transmit API fails to receive +** the necessary buffer memory from the ES memory pool. This could be an indication +** that the cfg param #CFE_PLATFORM_SB_BUF_MEMORY_BYTES is set too low. To check this, send SB +** cmd to dump the SB statistics pkt and view the buffer memory parameters. +**/ +#define CFE_SB_GET_BUF_ERR_EID 16 + +/** \brief 'Send Err:Msg Limit Err MsgId 0x\%x,pipe \%s,sender \%s' +** \event 'Send Err:Msg Limit Err MsgId 0x\%x,pipe \%s,sender \%s' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when a transmit API cannot route the +** MsgId (displayed in event) to the pipe (displayed in the event) because the pipe +** currently contains the maximum number of messages of this type (MsgId). This is +** typically an indication that the receiver is not reading its pipe fast enough, or +** at all. A less typical scenerio is that the sender is sending a burst of pkts of +** this type (or MsgId) and the receiver (owner of 'pipe') cannot keep up. The +** subscriber of the message dictates this limit count in the 'MsgLim' parameter of +** the #CFE_SB_SubscribeEx API or uses the default value of 4 if using the +** #CFE_SB_Subscribe API. +**/ +#define CFE_SB_MSGID_LIM_ERR_EID 17 + +/** \brief 'Rcv Err:Bad Input Arg:BufPtr 0x\%x,pipe \%d,t/o \%d,app \%s' +** \event 'Rcv Err:Bad Input Arg:BufPtr 0x\%x,pipe \%d,t/o \%d,app \%s' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when an invalid paramter is passed into the +** #CFE_SB_ReceiveBuffer API. Two possibile problems would be the first parameter (*BufPtr) +** being NULL or the third paramter (TimeOut) being less than -1. +**/ +#define CFE_SB_RCV_BAD_ARG_EID 18 + +/** \brief 'Rcv Err:PipeId \%d does not exist,app \%s' +** \event 'Rcv Err:PipeId \%d does not exist,app \%s' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when an invalid PipeId is passed into the +** #CFE_SB_ReceiveBuffer API. The SB Pipe Table shows all valid PipeIds and may be viewed +** for verification. +**/ +#define CFE_SB_BAD_PIPEID_EID 19 + +/** \brief 'Subscribe Err:Request for Destination Blk failed for Msg 0x\%x,Pipe \%s' +** \event 'Subscribe Err:Request for Destination Blk failed for Msg 0x\%x,Pipe \%s' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when the SB receives an error from the memory +** pool in the attempt to obtain a new destination block. Then memory pool statistics +** may be viewed by sending the related ES command. +**/ +#define CFE_SB_DEST_BLK_ERR_EID 20 + +/** \brief 'Send Err:Invalid msgid in msg,MsgId 0x\%x,App \%s' +** \event 'Send Err:Invalid msgid in msg,MsgId 0x\%x,App \%s' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when a transmit API is called and +** the SB discovers that the message to send has a msg id that is invalid. It may be +** due to a msg id that is greater than cfg parameter #CFE_PLATFORM_SB_HIGHEST_VALID_MSGID +**/ +#define CFE_SB_SEND_INV_MSGID_EID 21 + +/** \brief 'Sending Subscription Report Msg=0x\%x,Pipe=\%d,Stat=0x\%x' +** \event 'Sending Subscription Report Msg=0x\%x,Pipe=\%d,Stat=0x\%x' +** +** \par Type: DEBUG +** +** \par Cause: +** +** This debug event message is issued when SB subscription reporting is enabled, +** (which is disabled by default) and a subscription is successfully received. +**/ +#define CFE_SB_SUBSCRIPTION_RPT_EID 22 + +/** \brief 'Msg hash collision: MsgId = 0x\%x, collisions = \%u' +** \event 'Msg hash collision: MsgId = 0x\%x, collisions = \%u' +** +** \par Type: DEBUG +** +** \par Cause: +** +** This event message is generated when a message id hash collision occurs when subscribing +** to a message. Collisions indicate how many slots were incremented to find an opening. +** +** Number of collisions will directly impact software bus performance. These can be resolved +** by adjusting MsgId values or increasing CFE_PLATFORM_SB_MAX_MSG_IDS. +**/ +#define CFE_SB_HASHCOLLISION_EID 23 + +/** \brief 'Pipe Overflow,MsgId 0x\%x,pipe \%s,stat 0x\%x,app \%s' +** \event 'Pipe Overflow,MsgId 0x\%x,pipe \%s,stat 0x\%x,app \%s' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when a transmit API is called and +** encounters an error when attempting to write the msg to the destination pipe +** (which is an underlying queue). This could indicate that the owner of the pipe is +** not readings its messages fast enough or at all. It may also mean that the +** pipe depth is not deep enough. The pipe depth is an input parameter to the +** #CFE_SB_CreatePipe API. +**/ +#define CFE_SB_Q_FULL_ERR_EID 25 + +/** \brief 'Pipe Write Err,MsgId 0x\%x,pipe \%s,stat 0x\%x,app \%s' +** \event 'Pipe Write Err,MsgId 0x\%x,pipe \%s,stat 0x\%x,app \%s' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when a transmit API is called and +** encounters an error when attempting to write the msg to the destination pipe +** (which is an underlying queue). More precisely, the OS API #OS_QueuePut has +** returned an unexpected error. The return code is displayed in the event. For +** more information, the user may look up the return code in the OSAL documention or +** source code. +**/ +#define CFE_SB_Q_WR_ERR_EID 26 + +/** \brief 'Pipe Read Err,pipe \%s,app \%s,stat 0x\%x' +** \event 'Pipe Read Err,pipe \%s,app \%s,stat 0x\%x' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when a transmit API is called and +** encounters an error when attempting to read the msg from the destination pipe +** (which is an underlying queue). More precisely, the OS API #OS_QueueGet has +** returned an unexpected error. The return code is displayed in the event. For +** more information, the user may look up the return code in the OSAL documention or +** source code. +**/ +#define CFE_SB_Q_RD_ERR_EID 27 + +/** \brief 'No-op Cmd Rcvd' +** \event 'No-op Cmd Rcvd' +** +** \par Type: INFORMATION +** +** \par Cause: +** +** This info event message is issued in response an SB NO-OP command +**/ +#define CFE_SB_CMD0_RCVD_EID 28 + +/** \brief 'Reset Counters Cmd Rcvd' +** \event 'Reset Counters Cmd Rcvd' +** +** \par Type: DEBUG +** +** \par Cause: +** +** This debug event message is issued in response an SB Reset Counters command +**/ +#define CFE_SB_CMD1_RCVD_EID 29 + +/** \brief 'Software Bus Statistics packet sent' +** \event 'Software Bus Statistics packet sent' +** +** \par Type: DEBUG +** +** \par Cause: +** +** This debug event message is issued when SB receives a cmd to send the SB +** statistics pkt. +**/ +#define CFE_SB_SND_STATS_EID 32 + +/** \brief 'Enbl Route Cmd:Route does not exist.Msg 0x\%x,Pipe \%d' +** \event 'Enbl Route Cmd:Route does not exist.Msg 0x\%x,Pipe \%d' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when SB receives a cmd to enable a route that +** does not exist in the routing table. A route is defined by a MsgId, PipeId pair. +**/ +#define CFE_SB_ENBL_RTE1_EID 33 + +/** \brief 'Enabling Route,Msg 0x\%x,Pipe \%d' +** \event 'Enabling Route,Msg 0x\%x,Pipe \%d' +** +** \par Type: DEBUG +** +** \par Cause: +** +** This debug event message is issued when SB receives a cmd to enable a route and +** the request is successfully executed. +**/ +#define CFE_SB_ENBL_RTE2_EID 34 + +/** \brief 'Enbl Route Cmd:Invalid Param.Msg 0x\%x,Pipe \%d' +** \event 'Enbl Route Cmd:Invalid Param.Msg 0x\%x,Pipe \%d' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when SB receives a cmd to enable a route and +** the MsgId or PipeId does not pass the validation checks. The MsgId must be less +** than cfg param #CFE_PLATFORM_SB_HIGHEST_VALID_MSGID. The PipeId must exist and be less than +** cfg param #CFE_PLATFORM_SB_MAX_PIPES. The SB pipe table may be viewed to verify the PipeId +** existence. +**/ +#define CFE_SB_ENBL_RTE3_EID 35 + +/** \brief 'Disable Route Cmd:Route does not exist,Msg 0x\%x,Pipe \%d' +** \event 'Disable Route Cmd:Route does not exist,Msg 0x\%x,Pipe \%d' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when SB receives a cmd to disable a route that +** does not exist in the routing table. A route is defined by a MsgId, PipeId pair. +**/ +#define CFE_SB_DSBL_RTE1_EID 36 + +/** \brief 'Route Disabled,Msg 0x\%x,Pipe \%d' +** \event 'Route Disabled,Msg 0x\%x,Pipe \%d' +** +** \par Type: DEBUG +** +** \par Cause: +** +** This debug event message is issued when SB receives a cmd to disable a route and +** the request is successfully executed. +**/ +#define CFE_SB_DSBL_RTE2_EID 37 + +/** \brief 'Disable Route Cmd:Invalid Param.Msg 0x\%x,Pipe \%d' +** \event 'Disable Route Cmd:Invalid Param.Msg 0x\%x,Pipe \%d' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when SB receives a cmd to disable a route and +** the MsgId or PipeId does not pass the validation checks. The MsgId must be less +** than cfg param #CFE_PLATFORM_SB_HIGHEST_VALID_MSGID. The PipeId must exist and be less than +** cfg param #CFE_PLATFORM_SB_MAX_PIPES. The SB pipe table may be viewed to verify the PipeId +** existence. +**/ +#define CFE_SB_DSBL_RTE3_EID 38 + +/** \brief '\%s written:Size=\%d,Entries=\%d' +** \event '\%s written:Size=\%d,Entries=\%d' +** +** \par Type: DEBUG +** +** \par Cause: +** +** This debug event message is issued after the SB routing info file, pipe info +** file or the map info file is written and closed. This is done is response to +** the SB 'Send Routing Info' cmd, the SB 'Send pipe Info' cmd or the SB 'Send +** Map Info' cmd, respectively. +**/ +#define CFE_SB_SND_RTG_EID 39 + +/** \brief 'Error creating file \%s, stat=0x\%x' +** \event 'Error creating file \%s, stat=0x\%x' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when the SB 'Send Routing Info' cmd is +** received and the file create fails. The event displays the status received from +** the OS. +**/ +#define CFE_SB_SND_RTG_ERR1_EID 40 + +/** \brief 'Invalid Cmd, Unexpected Command Code \%d' +** \event 'Invalid Cmd, Unexpected Command Code \%d' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when the SB receives a cmd that has an +** unexpected cmd code. +**/ +#define CFE_SB_BAD_CMD_CODE_EID 42 + +/** \brief 'Invalid Cmd, Unexpected Msg Id: 0x\%x' +** \event 'Invalid Cmd, Unexpected Msg Id: 0x\%x' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when the SB receives a msg that has an +** unexpected msg id. +**/ +#define CFE_SB_BAD_MSGID_EID 43 + +/** \brief 'Full Sub Pkt \%d Sent,Entries=\%d,Stat=0x\%x\n' +** \event 'Full Sub Pkt \%d Sent,Entries=\%d,Stat=0x\%x\n' +** +** \par Type: DEBUG +** +** \par Cause: +** +** This debug event message is issued in response to the +** 'Send Previous Subscriptions' command and a full pkt segment is sent. +**/ +#define CFE_SB_FULL_SUB_PKT_EID 44 + +/** \brief 'Partial Sub Pkt \%d Sent,Entries=\%d,Stat=0x\%x' +** \event 'Partial Sub Pkt \%d Sent,Entries=\%d,Stat=0x\%x' +** +** \par Type: DEBUG +** +** \par Cause: +** +** This debug event message is issued in response to the +** 'Send Previous Subscriptions' command and a partial pkt segment is sent. +**/ +#define CFE_SB_PART_SUB_PKT_EID 45 + +/** \brief 'Pipe Delete Error:Bad Argument,PipedId \%d,Requestor \%s,Idx \%d,Stat \%d' +** \event 'Pipe Delete Error:Bad Argument,PipedId \%d,Requestor \%s,Idx \%d,Stat \%d' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued from CFE_SB_DeletePipeFull when an +** invalid pipe ID is passed in +**/ +#define CFE_SB_DEL_PIPE_ERR1_EID 46 + +/** \brief 'Pipe Deleted:id \%d,owner \%s' +** \event 'Pipe Deleted:id \%d,owner \%s' +** +** \par Type: DEBUG +** +** \par Cause: +** +** This debug event message is issued when the #CFE_SB_DeletePipe API is called and +** the request is successfully completed. +**/ +#define CFE_SB_PIPE_DELETED_EID 47 + +/** \brief 'Subscription Removed:Msg 0x\%x on pipe \%d,app \%s' +** \event 'Subscription Removed:Msg 0x\%x on pipe \%d,app \%s' +** +** \par Type: DEBUG +** +** \par Cause: +** +** This debug event message is issued when #CFE_SB_Unsubscribe API is called +** and the request is successfully completed. +**/ +#define CFE_SB_SUBSCRIPTION_REMOVED_EID 48 + +/** \brief 'File write,byte cnt err,file \%s,request=\%d,actual=\%d' +** \event 'File write,byte cnt err,file \%s,request=\%d,actual=\%d' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when one of many SB's file write operations +** is unsuccessful. This event is a result of #CFE_FS_WriteHeader or OS_write +** returning something other than the number of bytes requested to be written. +** The requested value and the return value are displayed in the event. +**/ +#define CFE_SB_FILEWRITE_ERR_EID 49 + +/** \brief 'Subscribe Err:Invalid Pipe Id,Msg=0x\%x,PipeId=\%d,App \%s' +** \event 'Subscribe Err:Invalid Pipe Id,Msg=0x\%x,PipeId=\%d,App \%s' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when the input PipeId has a value that is not +** listed in the pipe table. This typically means that the pipe does not exist. +** The pipe table may be viewed for verification. +**/ +#define CFE_SB_SUB_INV_PIPE_EID 50 + +/** \brief 'Subscribe Err:Caller(\%s) is not the owner of pipe \%d, Msg=0x\%x' +** \event 'Subscribe Err:Caller(\%s) is not the owner of pipe \%d, Msg=0x\%x' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when one of the SB subscribe API's are called +** and the requestor is not the owner of the pipe. Only the owner of the pipe may +** subscribe to messages on the pipe. +**/ +#define CFE_SB_SUB_INV_CALLER_EID 51 + +/** \brief 'Unsubscribe Err:Invalid Pipe Id Msg=0x\%x,Pipe=\%d,app=\%s' +** \event 'Unsubscribe Err:Invalid Pipe Id Msg=0x\%x,Pipe=\%d,app=\%s' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when one of the SB unsubscribe API's are +** called and the input parameter PipeId is not listed in the pipe table. +** This typically means that the pipe does not exist. The pipe table may be viewed +** for verification. +**/ +#define CFE_SB_UNSUB_INV_PIPE_EID 52 + +/** \brief 'Unsubscribe Err:Caller(\%s) is not the owner of pipe \%d,Msg=0x\%x' +** \event 'Unsubscribe Err:Caller(\%s) is not the owner of pipe \%d,Msg=0x\%x' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when one of the SB unsubscribe API's are +** called and the requestor is not the owner of the pipe (or ES). Only the owner of +** the pipe(or ES for cleanup purposes)may unsubscribe messages from a pipe. +**/ +#define CFE_SB_UNSUB_INV_CALLER_EID 53 + +/** \brief 'Pipe Delete Error:Caller(\%s) is not the owner of pipe \%d' +** \event 'Pipe Delete Error:Caller(\%s) is not the owner of pipe \%d' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when the #CFE_SB_DeletePipe API is called by a +** task that is not the owner of the pipe. Pipes may be deleted only by the task +** that created the pipe or ES(for cleanup purposes). +**/ +#define CFE_SB_DEL_PIPE_ERR2_EID 54 + +/** \brief 'Invalid cmd length: ID = 0x\%X, CC = \%d, Exp Len = \%d, Len = \%d' +** \event 'Invalid cmd length: ID = 0x\%X, CC = \%d, Exp Len = \%d, Len = \%d' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated when a message with the #CFE_SB_CMD_MID +** message ID has arrived but whose packet length does not match the expected +** length for the specified command code. +** +** The \c ID field in the event message specifies the Message ID (in hex), the \c CC field +** specifies the Command Code (in decimal), the \c Exp Len field specified the Expected +** Length (in decimal ), and \c Len specifies the message Length (in decimal) +** found in the message. +**/ +#define CFE_SB_LEN_ERR_EID 61 + +/** \brief 'CreatePipeErr:Name Taken:app=\%s,ptr=0x\%x,depth=\%d,maxdepth=\%d' +** \event 'CreatePipeErr:Name Taken:app=\%s,ptr=0x\%x,depth=\%d,maxdepth=\%d' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when the #CFE_SB_CreatePipe API tries to create +** a pipe with a name that is in use. +**/ +#define CFE_SB_CR_PIPE_NAME_TAKEN_EID 62 + +/** \brief 'CreatePipeErr:No Free:app=\%s,ptr=0x\%x,depth=\%d,maxdepth=\%d' +** \event 'CreatePipeErr:No Free:app=\%s,ptr=0x\%x,depth=\%d,maxdepth=\%d' +** +** \par Type: ERROR +** +** \par Cause: +** +** This error event message is issued when the #CFE_SB_CreatePipe API is unable to +** create a queue because there are no queues free. +**/ +#define CFE_SB_CR_PIPE_NO_FREE_EID 63 + +#endif /* CFE_SB_EVENTS_H */ diff --git a/modules/sb/fsw/inc/cfe_sb_msg.h b/modules/sb/fsw/inc/cfe_sb_msg.h new file mode 100644 index 000000000..93a9de958 --- /dev/null +++ b/modules/sb/fsw/inc/cfe_sb_msg.h @@ -0,0 +1,787 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/** + * @file + * + * Purpose: + * This header file contains structure definitions for all SB command and + * telemetry packets + * + * Author: R.McGraw/SSI + * + */ + +#ifndef CFE_SB_MSG_H +#define CFE_SB_MSG_H + +/* +** Includes +*/ +#include "common_types.h" /* Basic data types */ +#include "cfe_msg_hdr.h" /* for header definitions */ +#include "cfe_sb_extern_typedefs.h" +#include "cfe_es_extern_typedefs.h" + +/**************************************** +** SB task command packet command codes +****************************************/ + +/** \cfesbcmd Software Bus No-Op +** +** \par Description +** This command performs no other function than to increment the +** command execution counter. The command may be used to verify +** general aliveness of the Software Bus task. +** +** \cfecmdmnemonic \SB_NOOP +** +** \par Command Structure +** #CFE_SB_NoopCmd_t +** +** \par Command Verification +** Successful execution of this command may be verified with the +** following telemetry: +** - \b \c \SB_CMDPC - command execution counter will +** increment +** - The #CFE_SB_CMD0_RCVD_EID informational event message will +** be generated +** +** \par Error Conditions +** There are no error conditions for this command. If the Software +** Bus receives the command, the event is sent (although it +** may be filtered by EVS) and the counter is incremented +** unconditionally. +** +** \par Criticality +** None +** +** \sa +*/ +#define CFE_SB_NOOP_CC 0 + +/** \cfesbcmd Software Bus Reset Counters +** +** \par Description +** This command resets the following counters within the Software +** Bus housekeeping telemetry: +** - Command Execution Counter (\SB_CMDPC) +** - Command Error Counter (\SB_CMDEC) +** +** \cfecmdmnemonic \SB_RESETCTRS +** +** \par Command Structure +** #CFE_SB_ResetCountersCmd_t +** +** \par Command Verification +** Successful execution of this command may be verified with the +** following telemetry: +** - \b \c \SB_CMDPC - command execution counter will +** increment +** - The #CFE_SB_CMD1_RCVD_EID informational event message will +** be generated +** +** \par Error Conditions +** There are no error conditions for this command. If the Software +** Bus receives the command, the event is sent (although it +** may be filtered by EVS) and the counter is incremented +** unconditionally. +** +** \par Criticality +** This command is not inherently dangerous. However, it is +** possible for ground systems and on-board safing procedures +** to be designed such that they react to changes in the counter +** values that are reset by this command. +** +** \sa +*/ +#define CFE_SB_RESET_COUNTERS_CC 1 + +/** \cfesbcmd Send Software Bus Statistics +** +** \par Description +** This command will cause the SB task to send a statistics packet +** containing current utilization figures and high water marks which +** may be useful for checking the margin of the SB platform configuration +** settings. +** +** \cfecmdmnemonic \SB_DUMPSTATS +** +** \par Command Structure +** #CFE_SB_SendSbStatsCmd_t +** +** \par Command Verification +** Successful execution of this command may be verified with the +** following telemetry: +** - \b \c \SB_CMDPC - command execution counter will increment +** - Receipt of statistics packet with MsgId #CFE_SB_STATS_TLM_MID +** - The #CFE_SB_SND_STATS_EID debug event message will be generated. All +** debug events are filtered by default. +** +** \par Error Conditions +** There are no error conditions for this command. If the Software +** Bus receives the command, the debug event is sent and the counter +** is incremented unconditionally. +** +** \par Criticality +** This command is not inherently dangerous. It will create and send +** a message on the software bus. If performed repeatedly, it is +** possible that receiver pipes may overflow. +** +** \sa +*/ +#define CFE_SB_SEND_SB_STATS_CC 2 + +/** \cfesbcmd Write Software Bus Routing Info to a File +** +** \par Description +** This command will create a file containing the software bus routing +** information. The routing information contains information about every +** subscription that has been received through the SB subscription APIs. +** An abosulte path and filename may be specified in the command. +** If this command field contains an empty string (NULL terminator as +** the first character) the default file path and name is used. +** The default file path and name is defined in the platform +** configuration file as #CFE_PLATFORM_SB_DEFAULT_ROUTING_FILENAME. +** +** \cfecmdmnemonic \SB_WRITEROUTING2FILE +** +** \par Command Structure +** #CFE_SB_WriteRoutingInfoCmd_t +** +** \par Command Verification +** Successful execution of this command may be verified with the +** following telemetry: +** - \b \c \SB_CMDPC - command execution counter will increment. +** - Specified filename created at specified location. See description. +** - The #CFE_SB_SND_RTG_EID debug event message will be generated. All +** debug events are filtered by default. +** +** \par Error Conditions +** - Errors may occur during write operations to the file. Possible +** causes might be insufficient space in the file system or the +** filename or file path is improperly specified. +** +** Evidence of failure may be found in the following telemetry: +** - \b \c \SB_CMDEC - command error counter will increment +** - A command specific error event message is issued for all error +** cases. See #CFE_SB_SND_RTG_ERR1_EID and #CFE_SB_FILEWRITE_ERR_EID +** +** \par Criticality +** This command is not inherently dangerous. It will create a new +** file in the file system and could, if performed repeatedly without +** sufficient file management by the operator, fill the file system. +*/ +#define CFE_SB_WRITE_ROUTING_INFO_CC 3 + +/** \cfesbcmd Enable Software Bus Route +** +** \par Description +** This command will enable a particular destination. The destination +** is specified in terms of MsgID and PipeID. The MsgId and PipeID are +** parmaters in the command. All destinations are enabled by default. +** This command is needed only after a #CFE_SB_DISABLE_ROUTE_CC command +** is used. +** +** \cfecmdmnemonic \SB_ENAROUTE +** +** \par Command Structure +** #CFE_SB_EnableRouteCmd_t +** +** \par Command Verification +** Successful execution of this command may be verified with the +** following telemetry: +** - \b \c \SB_CMDPC - command execution counter will increment +** - View routing information #CFE_SB_WRITE_ROUTING_INFO_CC to verify +** enable/disable state change +** - The #CFE_SB_ENBL_RTE2_EID debug event message will be generated. All +** debug events are filtered by default. +** - Destination will begin receiving messages. +** +** \par Error Conditions +** An Error may occur if the MsgId or PipeId parmaters do not pass +** validation or the destination does not exist. + +** Evidence of failure may be found in the following telemetry: +** - \b \c \SB_CMDEC - command error counter will increment +** - A command specific error event message is issued for all error +** cases. See #CFE_SB_ENBL_RTE1_EID or #CFE_SB_ENBL_RTE3_EID +** +** \par Criticality +** This command is not inherently dangerous. +*/ +#define CFE_SB_ENABLE_ROUTE_CC 4 + +/** \cfesbcmd Disable Software Bus Route +** +** \par Description +** This command will disable a particular destination. The destination +** is specified in terms of MsgID and PipeID. The MsgId and PipeID are +** parmaters in the command. All destinations are enabled by default. +** +** \cfecmdmnemonic \SB_DISROUTE +** +** \par Command Structure +** #CFE_SB_DisableRouteCmd_t +** +** \par Command Verification +** Successful execution of this command may be verified with the +** following telemetry: +** - \b \c \SB_CMDPC - command execution counter will increment +** - View routing information #CFE_SB_WRITE_ROUTING_INFO_CC to verify +** enable/disable state change +** - The #CFE_SB_DSBL_RTE2_EID debug event message will be generated. All +** debug events are filtered by default. +** - Destination will stop receiving messages. +** +** \par Error Conditions +** An Error may occur if the MsgId or PipeId parmaters do not pass +** validation or the destination does not exist. + +** Evidence of failure may be found in the following telemetry: +** - \b \c \SB_CMDEC - command error counter will increment +** - A command specific error event message is issued for all error +** cases. See #CFE_SB_DSBL_RTE1_EID or #CFE_SB_DSBL_RTE3_EID +** +** \par Criticality +** This command is not intended to be used in nominal conditions. It is +** possible to get into a state where a destination cannot be re-enabled +** without reseting the processor. For instance, sending this command +** with #CFE_SB_CMD_MID and the SB_Cmd_Pipe would inhibit any ground +** commanding to the software bus until the processor was reset. There +** are similar problems that may occur when using this command. +*/ +#define CFE_SB_DISABLE_ROUTE_CC 5 + +/** \cfesbcmd Write Pipe Info to a File +** +** \par Description +** This command will create a file containing the software bus pipe +** information. The pipe information contains information about every +** pipe that has been created through the #CFE_SB_CreatePipe API. An +** abosulte path and filename may be specified in the command. +** If this command field contains an empty string (NULL terminator as +** the first character) the default file path and name is used. +** The default file path and name is defined in the platform +** configuration file as #CFE_PLATFORM_SB_DEFAULT_PIPE_FILENAME. +** +** \cfecmdmnemonic \SB_WRITEPIPE2FILE +** +** \par Command Structure +** #CFE_SB_WritePipeInfoCmd_t +** +** \par Command Verification +** Successful execution of this command may be verified with the +** following telemetry: +** - \b \c \SB_CMDPC - command execution counter will increment. +** - Specified filename created at specified location. See description. +** - The #CFE_SB_SND_RTG_EID debug event message will be generated. All +** debug events are filtered by default. +** +** \par Error Conditions +** - Errors may occur during write operations to the file. Possible +** causes might be insufficient space in the file system or the +** filename or file path is improperly specified. +** +** Evidence of failure may be found in the following telemetry: +** - \b \c \SB_CMDEC - command error counter will increment +** - A command specific error event message is issued for all error +** cases. See #CFE_SB_SND_RTG_ERR1_EID and #CFE_SB_FILEWRITE_ERR_EID +** +** \par Criticality +** This command is not inherently dangerous. It will create a new +** file in the file system and could, if performed repeatedly without +** sufficient file management by the operator, fill the file system. +*/ +#define CFE_SB_WRITE_PIPE_INFO_CC 7 + +/** \cfesbcmd Write Map Info to a File +** +** \par This command will create a file containing the software bus message +** map information. The message map is a lookup table (an array of +** uint16s)that allows fast access to the correct routing table element +** during a softeware bus send operation. This is diasgnostic +** information that may be needed due to the dynamic nature of the +** cFE software bus. An abosulte path and filename may be specified +** in the command. If this command field contains an empty string +** (NULL terminator as the first character) the default file path and +** name is used. The default file path and name is defined in the +** platform configuration file as #CFE_PLATFORM_SB_DEFAULT_MAP_FILENAME. +** +** \cfecmdmnemonic \SB_WRITEMAP2FILE +** +** \par Command Structure +** #CFE_SB_WriteMapInfoCmd_t +** +** \par Command Verification +** Successful execution of this command may be verified with the +** following telemetry: +** - \b \c \SB_CMDPC - command execution counter will increment. +** - Specified filename created at specified location. See description. +** - The #CFE_SB_SND_RTG_EID debug event message will be generated. All +** debug events are filtered by default. +** +** \par Error Conditions +** - Errors may occur during write operations to the file. Possible +** causes might be insufficient space in the file system or the +** filename or file path is improperly specified. +** +** Evidence of failure may be found in the following telemetry: +** - \b \c \SB_CMDEC - command error counter will increment +** - A command specific error event message is issued for all error +** cases. See #CFE_SB_SND_RTG_ERR1_EID and #CFE_SB_FILEWRITE_ERR_EID +** +** \par Criticality +** This command is not inherently dangerous. It will create a new +** file in the file system and could, if performed repeatedly without +** sufficient file management by the operator, fill the file system. +*/ +#define CFE_SB_WRITE_MAP_INFO_CC 8 + +/** \cfesbcmd Enable Subscription Reporting Command +** +** \par Description +** This command will enable subscription reporting and is intended to +** be used only by the CFS SBN (Software Bus Networking) Application. +** It is not intended to be sent from the ground or used by operations. +** When subscription reporting is enabled, SB will generate +** and send a software bus packet for each subscription received. +** The software bus packet that is sent contains the information +** received in the subscription API. This subscription report is +** neeeded by SBN if offboard routing is required. +* +** \cfecmdmnemonic \SB_ENASUBRPTG +** +** \par Command Structure +** #CFE_SB_EnableSubReportingCmd_t +** +** \par Command Verification +** Successful execution of this command will result in the sending +** of a packet (with the #CFE_SB_ONESUB_TLM_MID MsgId) for each +** subscription received by SB through the subscription APIs. +** +** \par Error Conditions +** None +** +** \par Criticality +** None +** +** \sa #CFE_SB_SingleSubscriptionTlm_t, #CFE_SB_DISABLE_SUB_REPORTING_CC, +** #CFE_SB_SEND_PREV_SUBS_CC +*/ +#define CFE_SB_ENABLE_SUB_REPORTING_CC 9 + +/** \cfesbcmd Disable Subscription Reporting Command +** +** \par Description +** This command will disable subscription reporting and is intended to +** be used only by the CFS SBN (Software Bus Networking) Application. +** It is not intended to be sent from the ground or used by operations. +** When subscription reporting is enabled, SB will generate +** and send a software bus packet for each subscription received. +** The software bus packet that is sent contains the information +** received in the subscription API. This subscription report is +** neeeded by SBN if offboard routing is required. +** +** \cfecmdmnemonic \SB_DISSUBRPTG +** +** \par Command Structure +** #CFE_SB_DisableSubReportingCmd_t +** +** \par Command Verification +** Successful execution of this command will result in the suppression +** of packets (with the #CFE_SB_ONESUB_TLM_MID MsgId) for each +** subscription received by SB through the subscription APIs. +** +** \par Error Conditions +** None +** +** \par Criticality +** None +** +** \sa #CFE_SB_SingleSubscriptionTlm_t, #CFE_SB_ENABLE_SUB_REPORTING_CC, +** #CFE_SB_SEND_PREV_SUBS_CC +*/ +#define CFE_SB_DISABLE_SUB_REPORTING_CC 10 + +/** \cfesbcmd Send Previous Subscriptions Command +** +** \par This command generates a series of packets that contain information +** regarding all subscriptions previously received by SB. +** This command is intended to be used only by the CFS SBN(Software Bus +** Networking) Application. +** It is not intended to be sent from the ground or used by operations. +** When this command is received the software bus will generate and +** send a series of packets containing information about all subscription +** previously received. +** +** \cfecmdmnemonic \SB_SENDPREVSUBS +** +** \par Command Structure +** #CFE_SB_SendPrevSubsCmd_t +** +** \par Command Verification +** Successful execution of this command will result in a series +** of packets (with the #CFE_SB_ALLSUBS_TLM_MID MsgId) being sent +** on the software bus. +** +** \par Error Conditions +** None +** +** \par Criticality +** None +** +** \sa #CFE_SB_AllSubscriptionsTlm_t, #CFE_SB_ENABLE_SUB_REPORTING_CC, +** #CFE_SB_DISABLE_SUB_REPORTING_CC +*/ +#define CFE_SB_SEND_PREV_SUBS_CC 11 + +/**************************** +** SB Command Formats ** +*****************************/ + +/* + * SB Messages which have no payload are each + * given unique typedefs to follow the command handler convention + * + * For the SB application these is mapped to the CFE_MSG_CommandHeader_t type, + * as they contain only a primary + command header. + */ +typedef CFE_MSG_CommandHeader_t CFE_SB_NoopCmd_t; +typedef CFE_MSG_CommandHeader_t CFE_SB_ResetCountersCmd_t; +typedef CFE_MSG_CommandHeader_t CFE_SB_EnableSubReportingCmd_t; +typedef CFE_MSG_CommandHeader_t CFE_SB_DisableSubReportingCmd_t; +typedef CFE_MSG_CommandHeader_t CFE_SB_SendSbStatsCmd_t; +typedef CFE_MSG_CommandHeader_t CFE_SB_SendPrevSubsCmd_t; + +/** +** \brief Write File Info Command Payload +** +** This structure contains a generic definition used by SB commands that write to a file +*/ +typedef struct CFE_SB_WriteFileInfoCmd_Payload +{ + char Filename[CFE_MISSION_MAX_PATH_LEN]; /**< \brief Path and Filename of data to be loaded */ +} CFE_SB_WriteFileInfoCmd_Payload_t; + +/** + * \brief Write File Info Command + */ +typedef struct CFE_SB_WriteFileInfoCmd +{ + CFE_MSG_CommandHeader_t Hdr; /**< \brief Command header */ + CFE_SB_WriteFileInfoCmd_Payload_t Payload; /**< \brief Command payload */ +} CFE_SB_WriteFileInfoCmd_t; + +/* + * Create a unique typedef for each of the commands that share this format. + */ +typedef CFE_SB_WriteFileInfoCmd_t CFE_SB_WriteRoutingInfoCmd_t; +typedef CFE_SB_WriteFileInfoCmd_t CFE_SB_WritePipeInfoCmd_t; +typedef CFE_SB_WriteFileInfoCmd_t CFE_SB_WriteMapInfoCmd_t; + +/** +** \brief Enable/Disable Route Command Payload +** +** This structure contains a definition used by two SB commands, +** 'Enable Route' #CFE_SB_ENABLE_ROUTE_CC and 'Disable Route' #CFE_SB_DISABLE_ROUTE_CC. +** A route is the destination pipe for a particular message and is therefore defined +** as a MsgId and PipeId combination. +*/ +typedef struct CFE_SB_RouteCmd_Payload +{ + + CFE_SB_MsgId_t MsgId; /**< \brief Message ID of route to be enabled or disabled #CFE_SB_MsgId_t */ + CFE_SB_PipeId_t Pipe; /**< \brief Pipe ID of route to be enabled or disabled #CFE_SB_PipeId_t */ + uint8 Spare; /**<\brief Spare byte to make command even number of bytes */ +} CFE_SB_RouteCmd_Payload_t; + +/** + * \brief Enable/Disable Route Command + */ +typedef struct CFE_SB_RouteCmd +{ + CFE_MSG_CommandHeader_t Hdr; /**< \brief Command header */ + CFE_SB_RouteCmd_Payload_t Payload; /**< \brief Command payload */ +} CFE_SB_RouteCmd_t; + +/* + * Create a unique typedef for each of the commands that share this format. + */ +typedef CFE_SB_RouteCmd_t CFE_SB_EnableRouteCmd_t; +typedef CFE_SB_RouteCmd_t CFE_SB_DisableRouteCmd_t; + +/**************************** +** SB Telemetry Formats ** +*****************************/ + +/** +** \cfesbtlm Software Bus task housekeeping Packet +*/ +typedef struct CFE_SB_HousekeepingTlm_Payload +{ + + uint8 CommandCounter; /**< \cfetlmmnemonic \SB_CMDPC + \brief Count of valid commands received */ + uint8 CommandErrorCounter; /**< \cfetlmmnemonic \SB_CMDEC + \brief Count of invalid commands received */ + uint8 NoSubscribersCounter; /**< \cfetlmmnemonic \SB_NOSUBEC + \brief Count pkts sent with no subscribers */ + uint8 MsgSendErrorCounter; /**< \cfetlmmnemonic \SB_MSGSNDEC + \brief Count of message send errors */ + + uint8 MsgReceiveErrorCounter; /**< \cfetlmmnemonic \SB_MSGRECEC + \brief Count of message receive errors */ + uint8 InternalErrorCounter; /**< \cfetlmmnemonic \SB_INTERNALEC + \brief Count of queue read or write errors */ + uint8 CreatePipeErrorCounter; /**< \cfetlmmnemonic \SB_NEWPIPEEC + \brief Count of errors in create pipe API */ + uint8 SubscribeErrorCounter; /**< \cfetlmmnemonic \SB_SUBSCREC + \brief Count of errors in subscribe API */ + uint8 PipeOptsErrorCounter; /**< \cfetlmmnemonic \SB_PIPEOPTSEC + \brief Count of errors in set/get pipe options API */ + uint8 DuplicateSubscriptionsCounter; /**< \cfetlmmnemonic \SB_DUPSUBCNT + \brief Count of duplicate subscriptions */ + uint8 GetPipeIdByNameErrorCounter; /**< \cfetlmmnemonic \SB_GETPIPEIDBYNAMEEC + \brief Count of errors in get pipe id by name API */ + uint8 Spare2Align[1]; /**< \cfetlmmnemonic \SB_SPARE2ALIGN + \brief Spare bytes to ensure alignment */ + + uint16 PipeOverflowErrorCounter; /**< \cfetlmmnemonic \SB_PIPEOVREC + \brief Count of pipe overflow errors */ + uint16 MsgLimitErrorCounter; /**< \cfetlmmnemonic \SB_MSGLIMEC + \brief Count of msg id to pipe errors */ + + CFE_ES_MemHandle_t MemPoolHandle; /**< \cfetlmmnemonic \SB_MEMPOOLHANDLE + \brief Handle to SB's Memory Pool */ + + uint32 MemInUse; /**< \cfetlmmnemonic \SB_MEMINUSE + \brief Memory in use */ + + uint32 UnmarkedMem; /**< \cfetlmmnemonic \SB_UNMARKEDMEM + \brief cfg param CFE_PLATFORM_SB_BUF_MEMORY_BYTES minus Peak Memory in use */ +} CFE_SB_HousekeepingTlm_Payload_t; + +typedef struct CFE_SB_HousekeepingTlm +{ + CFE_MSG_TelemetryHeader_t Hdr; /**< \brief Telemetry header */ + CFE_SB_HousekeepingTlm_Payload_t Payload; /**< \brief Telemetry payload */ +} CFE_SB_HousekeepingTlm_t; + +/** +** \brief SB Pipe Depth Statistics +** +** Used in SB Statistics Telemetry Packet #CFE_SB_StatsTlm_t +*/ +typedef struct CFE_SB_PipeDepthStats +{ + + CFE_SB_PipeId_t PipeId; /**< \cfetlmmnemonic \SB_PDPIPEID + \brief Pipe Id associated with the stats below */ + uint16 MaxQueueDepth; /**< \cfetlmmnemonic \SB_PDDEPTH + \brief Number of messages the pipe can hold */ + uint16 CurrentQueueDepth; /**< \cfetlmmnemonic \SB_PDINUSE + \brief Number of messages currently on the pipe */ + uint16 PeakQueueDepth; /**< \cfetlmmnemonic \SB_PDPKINUSE + \brief Peak number of messages that have been on the pipe */ + uint16 Spare; /**< \cfetlmmnemonic \SB_PDSPARE + \brief Spare word to ensure alignment */ + +} CFE_SB_PipeDepthStats_t; + +/** +** \brief SB Pipe Information File Entry +** +** This statistics structure is output as part of the CFE SB +** "Send Pipe Info" command (CFE_SB_SEND_PIPE_INFO_CC). +** +** Previous versions of CFE simply wrote the internal CFE_SB_PipeD_t object +** to the file, but this also contains information such as pointers which are +** not relevant outside the running CFE process. +** +** By defining the pipe info structure separately, it also provides some +** independence, such that the internal CFE_SB_PipeD_t definition +** can evolve without changing the binary format of the information +** file. +*/ +typedef struct CFE_SB_PipeInfoEntry +{ + CFE_SB_PipeId_t PipeId; /**< The runtime ID of the pipe */ + CFE_ES_AppId_t AppId; /**< The runtime ID of the application that owns the pipe */ + char PipeName[CFE_MISSION_MAX_API_LEN]; /**< The Name of the pipe */ + char AppName[CFE_MISSION_MAX_API_LEN]; /**< The Name of the application that owns the pipe */ + uint16 MaxQueueDepth; /**< The allocated depth of the pipe (max capacity) */ + uint16 CurrentQueueDepth; /**< The current depth of the pipe */ + uint16 PeakQueueDepth; /**< The peak depth of the pipe (high watermark) */ + uint16 SendErrors; /**< Number of errors when writing to this pipe */ + uint8 Opts; /**< Pipe options set (bitmask) */ + uint8 Spare[3]; /**< Padding to make this structure a multiple of 4 bytes */ + +} CFE_SB_PipeInfoEntry_t; + +/** +** \cfesbtlm SB Statistics Telemetry Packet +** +** SB Statistics packet sent in response to #CFE_SB_SEND_SB_STATS_CC +*/ +typedef struct CFE_SB_StatsTlm_Payload +{ + + uint32 MsgIdsInUse; /**< \cfetlmmnemonic \SB_SMMIDIU + \brief Current number of MsgIds with a destination */ + uint32 PeakMsgIdsInUse; /**< \cfetlmmnemonic \SB_SMPMIDIU + \brief Peak number of MsgIds with a destination */ + uint32 MaxMsgIdsAllowed; /**< \cfetlmmnemonic \SB_SMMMIDALW + \brief cFE Cfg Param \link #CFE_PLATFORM_SB_MAX_MSG_IDS \endlink */ + + uint32 PipesInUse; /**< \cfetlmmnemonic \SB_SMPIU + \brief Number of pipes currently in use */ + uint32 PeakPipesInUse; /**< \cfetlmmnemonic \SB_SMPPIU + \brief Peak number of pipes since last reboot */ + uint32 MaxPipesAllowed; /**< \cfetlmmnemonic \SB_SMMPALW + \brief cFE Cfg Param \link #CFE_PLATFORM_SB_MAX_PIPES \endlink */ + + uint32 MemInUse; /**< \cfetlmmnemonic \SB_SMBMIU + \brief Memory bytes currently in use for SB msg transfers */ + uint32 PeakMemInUse; /**< \cfetlmmnemonic \SB_SMPBMIU + \brief Peak memory bytes in use for SB msg transfers */ + uint32 MaxMemAllowed; /**< \cfetlmmnemonic \SB_SMMBMALW + \brief cFE Cfg Param \link #CFE_PLATFORM_SB_BUF_MEMORY_BYTES \endlink */ + + uint32 SubscriptionsInUse; /**< \cfetlmmnemonic \SB_SMSIU + \brief Number of current subscriptions */ + uint32 PeakSubscriptionsInUse; /**< \cfetlmmnemonic \SB_SMPSIU + \brief Peak number of subscriptions */ + uint32 MaxSubscriptionsAllowed; /**< \cfetlmmnemonic \SB_SMMSALW + \brief product of \link #CFE_PLATFORM_SB_MAX_MSG_IDS \endlink + and \link #CFE_PLATFORM_SB_MAX_DEST_PER_PKT \endlink */ + + uint32 SBBuffersInUse; /**< \cfetlmmnemonic \SB_SMSBBIU + \brief Number of SB message buffers currently in use */ + uint32 PeakSBBuffersInUse; /**< \cfetlmmnemonic \SB_SMPSBBIU + \brief Max number of SB message buffers in use */ + + uint32 MaxPipeDepthAllowed; /**< \cfetlmmnemonic \SB_SMMPDALW + \brief Maximum allowed pipe depth */ + CFE_SB_PipeDepthStats_t + PipeDepthStats[CFE_MISSION_SB_MAX_PIPES]; /**< \cfetlmmnemonic \SB_SMPDS + \brief Pipe Depth Statistics #CFE_SB_PipeDepthStats_t*/ +} CFE_SB_StatsTlm_Payload_t; + +typedef struct CFE_SB_StatsTlm +{ + CFE_MSG_TelemetryHeader_t Hdr; /**< \brief Telemetry header */ + CFE_SB_StatsTlm_Payload_t Payload; /**< \brief Telemetry payload */ +} CFE_SB_StatsTlm_t; + +/** +** \brief SB Routing File Entry +** +** Structure of one element of the routing information in response to #CFE_SB_WRITE_ROUTING_INFO_CC +*/ +typedef struct CFE_SB_RoutingFileEntry +{ + CFE_SB_MsgId_t MsgId; /**< \brief Message Id portion of the route */ + CFE_SB_PipeId_t PipeId; /**< \brief Pipe Id portion of the route */ + uint8 State; /**< \brief Route Enabled or Disabled */ + uint16 MsgCnt; /**< \brief Number of msgs with this MsgId sent to this PipeId */ + char AppName[CFE_MISSION_MAX_API_LEN]; /**< \brief Pipe Depth Statistics */ + char PipeName[CFE_MISSION_MAX_API_LEN]; /**< \brief Pipe Depth Statistics */ +} CFE_SB_RoutingFileEntry_t; + +/** +** \brief SB Map File Entry +** +** Structure of one element of the map information in response to #CFE_SB_WRITE_MAP_INFO_CC +*/ +typedef struct CFE_SB_MsgMapFileEntry +{ + CFE_SB_MsgId_t MsgId; /**< \brief Message Id which has been subscribed to */ + CFE_SB_RouteId_Atom_t Index; /**< \brief Routing raw index value (0 based, not Route ID) */ +} CFE_SB_MsgMapFileEntry_t; + +/** +** \cfesbtlm SB Subscription Report Packet +** +** This structure defines the pkt sent by SB when a subscription or +** a request to unsubscribe is received while subscription reporting is enabled. +** By default subscription reporting is disabled. This feature is intended to be +** used primarily by Software Bus Networking Application (SBN) +** +** \sa #CFE_SB_ENABLE_SUB_REPORTING_CC, #CFE_SB_DISABLE_SUB_REPORTING_CC +*/ +typedef struct CFE_SB_SingleSubscriptionTlm_Payload +{ + + uint8 SubType; /**< \brief Subscription or Unsubscription */ + CFE_SB_MsgId_t MsgId; /**< \brief MsgId subscribed or unsubscribe to */ + CFE_SB_Qos_t Qos; /**< \brief Quality of Service, used only for interprocessor communication */ + CFE_SB_PipeId_t Pipe; /**< \brief Destination pipe id to send above msg id */ + +} CFE_SB_SingleSubscriptionTlm_Payload_t; + +typedef struct CFE_SB_SingleSubscriptionTlm +{ + CFE_MSG_TelemetryHeader_t Hdr; /**< \brief Telemetry header */ + CFE_SB_SingleSubscriptionTlm_Payload_t Payload; /**< \brief Telemetry payload */ +} CFE_SB_SingleSubscriptionTlm_t; + +/** +** \brief SB Previous Subscriptions Entry +** +** This structure defines an entry used in the CFE_SB_PrevSubsPkt_t +** Intended to be used primarily by Software Bus Networking Application (SBN) +** +** Used in structure definition #CFE_SB_AllSubscriptionsTlm_t +*/ +typedef struct CFE_SB_SubEntries +{ + + CFE_SB_MsgId_t MsgId; /**< \brief MsgId portion of the subscription */ + CFE_SB_Qos_t Qos; /**< \brief Qos portion of the subscription */ + CFE_SB_PipeId_t Pipe; /**< \brief PipeId portion of the subscription */ + +} CFE_SB_SubEntries_t; + +/** +** \cfesbtlm SB Previous Subscriptions Packet +** +** This structure defines the pkt(s) sent by SB that contains a list of all current +** subscriptions. This pkt is generated on cmd and intended to be used primarily by +** the Software Bus Networking Application (SBN). Typically, when the cmd is received +** there are more subscriptions than can fit in one pkt. The complete list of +** subscriptions is sent via a series of segmented pkts. +*/ +typedef struct CFE_SB_AllSubscriptionsTlm_Payload +{ + + uint32 PktSegment; /**< \brief Pkt number(starts at 1) in the series */ + uint32 TotalSegments; /**< \brief Total number of pkts needed to complete the request */ + uint32 Entries; /**< \brief Number of entries in the pkt */ + CFE_SB_SubEntries_t Entry[CFE_SB_SUB_ENTRIES_PER_PKT]; /**< \brief Array of #CFE_SB_SubEntries_t entries */ +} CFE_SB_AllSubscriptionsTlm_Payload_t; + +typedef struct CFE_SB_AllSubscriptionsTlm +{ + CFE_MSG_TelemetryHeader_t Hdr; /**< \brief Telemetry header */ + CFE_SB_AllSubscriptionsTlm_Payload_t Payload; /**< \brief Telemetry payload */ +} CFE_SB_AllSubscriptionsTlm_t; + +#endif /* CFE_SB_MSG_H */ diff --git a/modules/sb/fsw/src/cfe_sb_api.c b/modules/sb/fsw/src/cfe_sb_api.c new file mode 100644 index 000000000..764f189cc --- /dev/null +++ b/modules/sb/fsw/src/cfe_sb_api.c @@ -0,0 +1,2195 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/****************************************************************************** +** File: cfe_sb_api.c +** +** Purpose: +** This file contains the source code for the SB API's. +** +** Notes: The following 4 terms have been, or are used in the cFS architecture and implementation +** +** StreamId - First 16 bits of CCSDS Space Packet Protocol (SPP) 133.0-B.1c2 Blue Book +** packet primary header. It contains the 3 bit Version Number, 1 bit Packet Type ID, +** 1 bit Secondary Header flag, and 11 bit Application Process ID +** It was used in earlier cFS implementaions and is defined here for historical reference +** It is NOT exposed to user applications. +** +** MsgId - Unique numeric message identifier within a mission namespace. It is used by cFS +** applications to the identify messages for publishing and subscribing +** It is used by the SB API and encoded in a mission defended way in the header of +** all cFS messages. +** It is exposed to all cFS applications +** +** ApId - CCSDS Application Process Id field in the primary header. +** It has default bit mask of 0x07FF and is part of the cFS message Id +** It should not be confused with the cFE Executive Services (ES) term appId which +** identifies the software application/component +** It is NOT exposed to user applications. +** +** MsgIdkey - This is a unique numeric key within a mission namespace that is used with +** cFS software bus internal structures. +** It is algorithmically created in a mission defined way from the MsgId to support +** efficient lookup and mapping implementations +** It is NOT exposed to user applications. +** +** Author: R.McGraw/SSI +** J.Wilmot/NASA +** +******************************************************************************/ + +/* +** Include Files +*/ +#include "cfe_sb_module_all.h" + +#include + +/* + * Macro to reflect size of PipeDepthStats Telemetry array - + * this may or may not be the same as CFE_SB_MSG_MAX_PIPES + */ +#define CFE_SB_TLM_PIPEDEPTHSTATS_SIZE \ + (sizeof(CFE_SB_Global.StatTlmMsg.Payload.PipeDepthStats) / \ + sizeof(CFE_SB_Global.StatTlmMsg.Payload.PipeDepthStats[0])) + +/* Local structure for remove pipe callbacks */ +typedef struct +{ + const char * FullName; /* Full name (app.task) for error reporting */ + CFE_SB_PipeId_t PipeId; /* Pipe id to remove */ +} CFE_SB_RemovePipeCallback_t; + +/* + * Function: CFE_SB_PipeId_ToIndex - See API and header file for details + */ +CFE_Status_t CFE_SB_PipeId_ToIndex(CFE_SB_PipeId_t PipeID, uint32 *Idx) +{ + return CFE_ResourceId_ToIndex(CFE_RESOURCEID_UNWRAP(PipeID), CFE_SB_PIPEID_BASE, CFE_PLATFORM_SB_MAX_PIPES, Idx); +} + +/* + * Function: CFE_SB_CreatePipe - See API and header file for details + */ +int32 CFE_SB_CreatePipe(CFE_SB_PipeId_t *PipeIdPtr, uint16 Depth, const char *PipeName) +{ + CFE_ES_AppId_t AppId; + CFE_ES_TaskId_t TskId; + osal_id_t SysQueueId; + int32 Status; + CFE_SB_PipeD_t * PipeDscPtr; + CFE_ResourceId_t PendingPipeId = CFE_RESOURCEID_UNDEFINED; + uint16 PendingEventId; + char FullName[(OS_MAX_API_NAME * 2)]; + + Status = CFE_SUCCESS; + SysQueueId = OS_OBJECT_ID_UNDEFINED; + PendingEventId = 0; + PipeDscPtr = NULL; + + /* + * Get caller AppId. + * + * This is needed for both success and failure cases, + * as it is stored in the Pipe Descriptor on success, + * and used for events on failure, so get it now. + */ + CFE_ES_GetAppID(&AppId); + + /* get callers TaskId */ + CFE_ES_GetTaskID(&TskId); + + /* check input parameters */ + if ((PipeIdPtr == NULL) || (Depth > OS_QUEUE_MAX_DEPTH) || (Depth == 0)) + { + PendingEventId = CFE_SB_CR_PIPE_BAD_ARG_EID; + Status = CFE_SB_BAD_ARGUMENT; + } + else + { + /* Get an available Pipe Descriptor which must be done while locked */ + CFE_SB_LockSharedData(__func__, __LINE__); + + /* get first available entry in pipe table */ + PendingPipeId = + CFE_ResourceId_FindNext(CFE_SB_Global.LastPipeId, CFE_PLATFORM_SB_MAX_PIPES, CFE_SB_CheckPipeDescSlotUsed); + PipeDscPtr = CFE_SB_LocatePipeDescByID(CFE_SB_PIPEID_C(PendingPipeId)); + + /* if pipe table is full, send event and return error */ + if (PipeDscPtr == NULL) + { + PendingEventId = CFE_SB_MAX_PIPES_MET_EID; + Status = CFE_SB_MAX_PIPES_MET; + } + else + { + /* Fully clear the entry, just in case of stale data */ + memset(PipeDscPtr, 0, sizeof(*PipeDscPtr)); + + CFE_SB_PipeDescSetUsed(PipeDscPtr, CFE_RESOURCEID_RESERVED); + CFE_SB_Global.LastPipeId = PendingPipeId; + } + + CFE_SB_UnlockSharedData(__func__, __LINE__); + } + + if (Status == CFE_SUCCESS) + { + /* create the queue */ + Status = OS_QueueCreate(&SysQueueId, PipeName, Depth, sizeof(CFE_SB_BufferD_t *), 0); + if (Status == OS_SUCCESS) + { + /* just translate the RC to CFE */ + Status = CFE_SUCCESS; + } + else + { + if (Status == OS_ERR_NAME_TAKEN) + { + PendingEventId = CFE_SB_CR_PIPE_NAME_TAKEN_EID; + } + else if (Status == OS_ERR_NO_FREE_IDS) + { + PendingEventId = CFE_SB_CR_PIPE_NO_FREE_EID; + } + else + { + /* some other unexpected error */ + PendingEventId = CFE_SB_CR_PIPE_ERR_EID; + } + + /* translate OSAL error to CFE error code */ + Status = CFE_SB_PIPE_CR_ERR; + } + } + + CFE_SB_LockSharedData(__func__, __LINE__); + + if (Status == CFE_SUCCESS) + { + /* fill in the pipe table fields */ + PipeDscPtr->SysQueueId = SysQueueId; + PipeDscPtr->MaxQueueDepth = Depth; + PipeDscPtr->AppId = AppId; + + CFE_SB_PipeDescSetUsed(PipeDscPtr, PendingPipeId); + + /* Increment the Pipes in use ctr and if it's > the high water mark,*/ + /* adjust the high water mark */ + CFE_SB_Global.StatTlmMsg.Payload.PipesInUse++; + if (CFE_SB_Global.StatTlmMsg.Payload.PipesInUse > CFE_SB_Global.StatTlmMsg.Payload.PeakPipesInUse) + { + CFE_SB_Global.StatTlmMsg.Payload.PeakPipesInUse = CFE_SB_Global.StatTlmMsg.Payload.PipesInUse; + } /* end if */ + } + else + { + /* + * If a descriptor had been allocated, then free it. + */ + if (PipeDscPtr != NULL) + { + CFE_SB_PipeDescSetFree(PipeDscPtr); + PipeDscPtr = NULL; + } + PendingPipeId = CFE_RESOURCEID_UNDEFINED; + + /* Increment error counter for all errors */ + CFE_SB_Global.HKTlmMsg.Payload.CreatePipeErrorCounter++; + } + + CFE_SB_UnlockSharedData(__func__, __LINE__); + + /* Send any pending events now, after final unlock */ + if (Status == CFE_SUCCESS) + { + /* send debug event */ + CFE_EVS_SendEventWithAppID(CFE_SB_PIPE_ADDED_EID, CFE_EVS_EventType_DEBUG, CFE_SB_Global.AppId, + "Pipe Created:name %s,id %d,app %s", PipeName, + (int)CFE_ResourceId_ToInteger(PendingPipeId), CFE_SB_GetAppTskName(TskId, FullName)); + + /* give the pipe handle to the caller */ + *PipeIdPtr = CFE_SB_PIPEID_C(PendingPipeId); + } + else + { + switch (PendingEventId) + { + case CFE_SB_CR_PIPE_BAD_ARG_EID: + CFE_EVS_SendEventWithAppID(CFE_SB_CR_PIPE_BAD_ARG_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, + "CreatePipeErr:Bad Input Arg:app=%s,ptr=0x%lx,depth=%d,maxdepth=%d", + CFE_SB_GetAppTskName(TskId, FullName), (unsigned long)PipeIdPtr, (int)Depth, + OS_QUEUE_MAX_DEPTH); + break; + + case CFE_SB_MAX_PIPES_MET_EID: + CFE_EVS_SendEventWithAppID(CFE_SB_MAX_PIPES_MET_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, + "CreatePipeErr:Max Pipes(%d)In Use.app %s", CFE_PLATFORM_SB_MAX_PIPES, + CFE_SB_GetAppTskName(TskId, FullName)); + break; + case CFE_SB_CR_PIPE_NAME_TAKEN_EID: + CFE_EVS_SendEventWithAppID(CFE_SB_CR_PIPE_NAME_TAKEN_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, + "CreatePipeErr:OS_QueueCreate failed, name taken (app=%s, name=%s)", + CFE_SB_GetAppTskName(TskId, FullName), PipeName); + break; + case CFE_SB_CR_PIPE_NO_FREE_EID: + CFE_EVS_SendEventWithAppID(CFE_SB_CR_PIPE_NO_FREE_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, + "CreatePipeErr:OS_QueueCreate failed, no free id's (app=%s)", + CFE_SB_GetAppTskName(TskId, FullName)); + break; + case CFE_SB_CR_PIPE_ERR_EID: + CFE_EVS_SendEventWithAppID(CFE_SB_CR_PIPE_ERR_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, + "CreatePipeErr:OS_QueueCreate returned %d,app %s", (int)Status, + CFE_SB_GetAppTskName(TskId, FullName)); + break; + } + } + + return Status; + +} /* end CFE_SB_CreatePipe */ + +/* + * Function: CFE_SB_DeletePipe - See API and header file for details + */ +int32 CFE_SB_DeletePipe(CFE_SB_PipeId_t PipeId) +{ + CFE_ES_AppId_t CallerId; + int32 Status = 0; + + /* get the callers Application Id */ + CFE_ES_GetAppID(&CallerId); + + Status = CFE_SB_DeletePipeFull(PipeId, CallerId); + + return Status; + +} /* end CFE_SB_DeletePipe */ + +/****************************************************************************** +** Function: CFE_SB_DeletePipeWithAppId() +** +** Purpose: +** +** +** Arguments: +** PipeId - The ID of the pipe to delete. +** +** Return: +** CFE_SUCCESS or cFE Error Code +*/ +int32 CFE_SB_DeletePipeWithAppId(CFE_SB_PipeId_t PipeId, CFE_ES_AppId_t AppId) +{ + int32 Status = 0; + + Status = CFE_SB_DeletePipeFull(PipeId, AppId); + + return Status; + +} /* end CFE_SB_DeletePipeWithAppId */ + +/****************************************************************************** + * Local callback helper for deleting a pipe from a route + */ +void CFE_SB_RemovePipeFromRoute(CFE_SBR_RouteId_t RouteId, void *ArgPtr) +{ + CFE_SB_DestinationD_t * destptr; + CFE_SB_RemovePipeCallback_t *args; + + args = (CFE_SB_RemovePipeCallback_t *)ArgPtr; + + destptr = CFE_SB_GetDestPtr(RouteId, args->PipeId); + + if (destptr != NULL) + { + CFE_SB_RemoveDest(RouteId, destptr); + } +} + +/****************************************************************************** +** Function: CFE_SB_DeletePipeFull() +** +** Purpose: +** Will unsubscribe to all routes associated with the given pipe id, then remove +** pipe from the pipe table. +** +** NOTE:This function cannot be called directly, it would not be semaphore protected +** +** Arguments: +** PipeId - The ID of the pipe to delete. +** +** Return: +** CFE_SUCCESS or cFE Error Code +*/ +int32 CFE_SB_DeletePipeFull(CFE_SB_PipeId_t PipeId, CFE_ES_AppId_t AppId) +{ + CFE_SB_PipeD_t * PipeDscPtr; + int32 Status; + CFE_ES_TaskId_t TskId; + CFE_SB_BufferD_t * BufDscPtr; + osal_id_t SysQueueId; + char FullName[(OS_MAX_API_NAME * 2)]; + size_t BufDscSize; + CFE_SB_RemovePipeCallback_t Args; + uint16 PendingEventID; + + Status = CFE_SUCCESS; + PendingEventID = 0; + SysQueueId = OS_OBJECT_ID_UNDEFINED; + BufDscPtr = NULL; + + /* take semaphore to prevent a task switch during this call */ + CFE_SB_LockSharedData(__func__, __LINE__); + + /* check input parameter */ + PipeDscPtr = CFE_SB_LocatePipeDescByID(PipeId); + if (!CFE_SB_PipeDescIsMatch(PipeDscPtr, PipeId)) + { + PendingEventID = CFE_SB_DEL_PIPE_ERR1_EID; + Status = CFE_SB_BAD_ARGUMENT; + } + /* check that the given AppId is the owner of the pipe */ + else if (!CFE_RESOURCEID_TEST_EQUAL(AppId, PipeDscPtr->AppId)) + { + PendingEventID = CFE_SB_DEL_PIPE_ERR2_EID; + Status = CFE_SB_BAD_ARGUMENT; + } + else + { + /* Remove the pipe from all routes */ + Args.PipeId = PipeId; + Args.FullName = FullName; + CFE_SBR_ForEachRouteId(CFE_SB_RemovePipeFromRoute, &Args, NULL); + + /* + * With the route removed there should be no new messages written to this pipe, + * + * but the pipe ID itself also needs to be invalidated now (before releasing lock) to make + * sure that no no subscriptions/routes can be added either. + * + * However we must first save certain state data for later deletion. + */ + SysQueueId = PipeDscPtr->SysQueueId; + BufDscPtr = PipeDscPtr->LastBuffer; + + /* + * Mark entry as "reserved" so other resources can be deleted + * while the SB global is unlocked. This prevents other tasks + * from trying to use this Pipe Desc slot, and also should prevents + * any task from re-subscribing to this pipe. + */ + CFE_SB_PipeDescSetUsed(PipeDscPtr, CFE_RESOURCEID_RESERVED); + } + + CFE_SB_UnlockSharedData(__func__, __LINE__); + + /* remove any messages that might be on the pipe */ + if (Status == CFE_SUCCESS) + { + while (true) + { + /* decrement refcount of any previous buffer */ + if (BufDscPtr != NULL) + { + CFE_SB_LockSharedData(__func__, __LINE__); + CFE_SB_DecrBufUseCnt(BufDscPtr); + CFE_SB_UnlockSharedData(__func__, __LINE__); + BufDscPtr = NULL; + } + + if (OS_QueueGet(SysQueueId, &BufDscPtr, sizeof(BufDscPtr), &BufDscSize, OS_CHECK) != OS_SUCCESS) + { + /* no more messages */ + break; + } + } + + /* Delete the underlying OS queue */ + OS_QueueDelete(SysQueueId); + } + + /* + * Final cleanup with global data locked + */ + CFE_SB_LockSharedData(__func__, __LINE__); + + if (Status == CFE_SUCCESS) + { + CFE_SB_PipeDescSetFree(PipeDscPtr); + --CFE_SB_Global.StatTlmMsg.Payload.PipesInUse; + } + else if (PendingEventID != 0) + { + CFE_SB_Global.HKTlmMsg.Payload.CreatePipeErrorCounter++; + } + + CFE_SB_UnlockSharedData(__func__, __LINE__); + + if (Status == CFE_SUCCESS) + { + /* + * Get the app name of the actual pipe owner for the event string + * as this may be different than the task doing the deletion. + * + * Note: If this fails (e.g. bad AppID, it returns an empty string + */ + CFE_ES_GetAppName(FullName, AppId, sizeof(FullName)); + + CFE_EVS_SendEventWithAppID(CFE_SB_PIPE_DELETED_EID, CFE_EVS_EventType_DEBUG, CFE_SB_Global.AppId, + "Pipe Deleted:id %d,owner %s", (int)CFE_RESOURCEID_TO_ULONG(PipeId), FullName); + } + else + { + /* get TaskId and name of caller for events */ + CFE_ES_GetTaskID(&TskId); + CFE_SB_GetAppTskName(TskId, FullName); + + switch (PendingEventID) + { + case CFE_SB_DEL_PIPE_ERR1_EID: + CFE_EVS_SendEventWithAppID(CFE_SB_DEL_PIPE_ERR1_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, + "Pipe Delete Error:Bad Argument,PipedId %ld,Requestor %s", + CFE_RESOURCEID_TO_ULONG(PipeId), FullName); + break; + case CFE_SB_DEL_PIPE_ERR2_EID: + CFE_EVS_SendEventWithAppID(CFE_SB_DEL_PIPE_ERR2_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, + "Pipe Delete Error:Caller(%s) is not the owner of pipe %ld", FullName, + CFE_RESOURCEID_TO_ULONG(PipeId)); + break; + } + } + + return Status; + +} /* end CFE_SB_DeletePipeFull */ + +/* + * Function: CFE_SB_SetPipeOpts - See API and header file for details + */ +int32 CFE_SB_SetPipeOpts(CFE_SB_PipeId_t PipeId, uint8 Opts) +{ + CFE_SB_PipeD_t *PipeDscPtr; + CFE_ES_AppId_t AppID; + CFE_ES_TaskId_t TskId; + uint16 PendingEventID; + int32 Status; + char FullName[(OS_MAX_API_NAME * 2)]; + + PendingEventID = 0; + + Status = CFE_ES_GetAppID(&AppID); + if (Status != CFE_SUCCESS) + { + /* shouldn't happen... */ + return Status; + } + + /* take semaphore to prevent a task switch during this call */ + CFE_SB_LockSharedData(__func__, __LINE__); + + /* check input parameter */ + PipeDscPtr = CFE_SB_LocatePipeDescByID(PipeId); + if (!CFE_SB_PipeDescIsMatch(PipeDscPtr, PipeId)) + { + PendingEventID = CFE_SB_SETPIPEOPTS_ID_ERR_EID; + Status = CFE_SB_BAD_ARGUMENT; + } + /* check that the caller AppId is the owner of the pipe */ + else if (!CFE_RESOURCEID_TEST_EQUAL(AppID, PipeDscPtr->AppId)) + { + PendingEventID = CFE_SB_SETPIPEOPTS_OWNER_ERR_EID; + Status = CFE_SB_BAD_ARGUMENT; + } + else + { + PipeDscPtr->Opts = Opts; + } + + /* If anything went wrong, increment the error counter before unlock */ + if (Status != CFE_SUCCESS) + { + CFE_SB_Global.HKTlmMsg.Payload.PipeOptsErrorCounter++; + } + + CFE_SB_UnlockSharedData(__func__, __LINE__); + + /* Send events after unlocking SB */ + if (Status == CFE_SUCCESS) + { + /* get AppID of caller for events */ + CFE_ES_GetAppName(FullName, AppID, sizeof(FullName)); + + CFE_EVS_SendEventWithAppID(CFE_SB_SETPIPEOPTS_EID, CFE_EVS_EventType_DEBUG, CFE_SB_Global.AppId, + "Pipe opts set:id %lu,owner %s, opts=0x%02x", CFE_RESOURCEID_TO_ULONG(PipeId), + FullName, (unsigned int)Opts); + } + else + { + /* get TaskId of caller for events */ + CFE_ES_GetTaskID(&TskId); + + switch (PendingEventID) + { + case CFE_SB_SETPIPEOPTS_ID_ERR_EID: + CFE_EVS_SendEventWithAppID(CFE_SB_SETPIPEOPTS_ID_ERR_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, + "Pipe Opts Error:Bad Argument,PipedId %lu,Requestor %s", + CFE_RESOURCEID_TO_ULONG(PipeId), CFE_SB_GetAppTskName(TskId, FullName)); + break; + case CFE_SB_SETPIPEOPTS_OWNER_ERR_EID: + CFE_EVS_SendEventWithAppID(CFE_SB_SETPIPEOPTS_OWNER_ERR_EID, CFE_EVS_EventType_ERROR, + CFE_SB_Global.AppId, + "Pipe Opts Set Error: Caller(%s) is not the owner of pipe %lu", + CFE_SB_GetAppTskName(TskId, FullName), CFE_RESOURCEID_TO_ULONG(PipeId)); + break; + } + } + + return Status; +} /* end CFE_SB_SetPipeOpts */ + +/* + * Function: CFE_SB_GetPipeOpts - See API and header file for details + */ +int32 CFE_SB_GetPipeOpts(CFE_SB_PipeId_t PipeId, uint8 *OptsPtr) +{ + int32 Status; + CFE_ES_TaskId_t TskId; + char FullName[(OS_MAX_API_NAME * 2)]; + uint16 PendingEventID; + CFE_SB_PipeD_t *PipeDscPtr; + + PendingEventID = 0; + Status = CFE_SUCCESS; + + /* take semaphore to prevent a task switch during this call */ + CFE_SB_LockSharedData(__func__, __LINE__); + + /* check input parameter */ + PipeDscPtr = CFE_SB_LocatePipeDescByID(PipeId); + if (!CFE_SB_PipeDescIsMatch(PipeDscPtr, PipeId)) + { + PendingEventID = CFE_SB_GETPIPEOPTS_ID_ERR_EID; + Status = CFE_SB_BAD_ARGUMENT; + } + else if (OptsPtr == NULL) + { + PendingEventID = CFE_SB_GETPIPEOPTS_PTR_ERR_EID; + Status = CFE_SB_BAD_ARGUMENT; + } + else + { + *OptsPtr = PipeDscPtr->Opts; + } + + /* If anything went wrong, increment the error counter before unlock */ + if (Status != CFE_SUCCESS) + { + CFE_SB_Global.HKTlmMsg.Payload.PipeOptsErrorCounter++; + } + + CFE_SB_UnlockSharedData(__func__, __LINE__); + + /* Send events after unlocking SB */ + if (Status == CFE_SUCCESS) + { + CFE_EVS_SendEventWithAppID(CFE_SB_GETPIPEOPTS_EID, CFE_EVS_EventType_DEBUG, CFE_SB_Global.AppId, + "Pipe opts get:id %lu, opts=0x%02x", CFE_RESOURCEID_TO_ULONG(PipeId), + (unsigned int)*OptsPtr); + } + else + { + /* get TaskId of caller for events */ + CFE_ES_GetTaskID(&TskId); + + switch (PendingEventID) + { + case CFE_SB_GETPIPEOPTS_PTR_ERR_EID: + CFE_EVS_SendEventWithAppID(CFE_SB_GETPIPEOPTS_PTR_ERR_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, + "Pipe Opts Error:Bad Argument,Requestor %s", + CFE_SB_GetAppTskName(TskId, FullName)); + break; + case CFE_SB_GETPIPEOPTS_ID_ERR_EID: + CFE_EVS_SendEventWithAppID(CFE_SB_GETPIPEOPTS_ID_ERR_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, + "Pipe Opts Error:Bad Argument,PipedId %lu,Requestor %s", + CFE_RESOURCEID_TO_ULONG(PipeId), CFE_SB_GetAppTskName(TskId, FullName)); + break; + } + } + + return Status; +} /* end CFE_SB_GetPipeOpts */ + +/* + * Function: CFE_SB_GetPipeName - See API and header file for details + */ +int32 CFE_SB_GetPipeName(char *PipeNameBuf, size_t PipeNameSize, CFE_SB_PipeId_t PipeId) +{ + int32 Status; + CFE_ES_TaskId_t TskId; + char FullName[(OS_MAX_API_NAME * 2)]; + uint16 PendingEventID; + CFE_SB_PipeD_t *PipeDscPtr; + osal_id_t SysQueueId; + + PendingEventID = 0; + Status = CFE_SUCCESS; + SysQueueId = OS_OBJECT_ID_UNDEFINED; + + /* take semaphore to prevent a task switch during this call */ + CFE_SB_LockSharedData(__func__, __LINE__); + + /* check input parameter */ + PipeDscPtr = CFE_SB_LocatePipeDescByID(PipeId); + if (!CFE_SB_PipeDescIsMatch(PipeDscPtr, PipeId)) + { + PendingEventID = CFE_SB_GETPIPENAME_ID_ERR_EID; + Status = CFE_SB_BAD_ARGUMENT; + } + else + { + SysQueueId = PipeDscPtr->SysQueueId; + } + + CFE_SB_UnlockSharedData(__func__, __LINE__); + + if (Status == CFE_SUCCESS) + { + if (PipeNameBuf == NULL || PipeNameSize == 0) + { + PendingEventID = CFE_SB_GETPIPENAME_NULL_PTR_EID; + Status = CFE_SB_BAD_ARGUMENT; + } + else + { + Status = OS_GetResourceName(SysQueueId, PipeNameBuf, PipeNameSize); + + if (Status == OS_SUCCESS) + { + Status = CFE_SUCCESS; + } + else + { + PendingEventID = CFE_SB_GETPIPENAME_ID_ERR_EID; + Status = CFE_SB_BAD_ARGUMENT; + } + } + } + + /* Send Events */ + if (Status == CFE_SUCCESS) + { + CFE_EVS_SendEventWithAppID(CFE_SB_GETPIPENAME_EID, CFE_EVS_EventType_DEBUG, CFE_SB_Global.AppId, + "GetPipeName name=%s id=%lu", PipeNameBuf, CFE_RESOURCEID_TO_ULONG(PipeId)); + } + else + { + CFE_ES_GetTaskID(&TskId); + + switch (PendingEventID) + { + case CFE_SB_GETPIPENAME_NULL_PTR_EID: + CFE_EVS_SendEventWithAppID(CFE_SB_GETPIPENAME_NULL_PTR_EID, CFE_EVS_EventType_ERROR, + CFE_SB_Global.AppId, "Pipe Name Error:NullPtr,Requestor %s", + CFE_SB_GetAppTskName(TskId, FullName)); + break; + case CFE_SB_GETPIPENAME_ID_ERR_EID: + CFE_EVS_SendEventWithAppID(CFE_SB_GETPIPENAME_ID_ERR_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, + "Pipe Id Error:Bad Argument,Id=%lu,Requestor %s", + CFE_RESOURCEID_TO_ULONG(PipeId), CFE_SB_GetAppTskName(TskId, FullName)); + break; + } + + if (PipeNameBuf != NULL && PipeNameSize > 0) + { + memset(PipeNameBuf, 0, PipeNameSize); + } + } + + return Status; + +} /* end CFE_SB_GetPipeName */ + +/* + * Function: CFE_SB_GetPipeIdByName - See API and header file for details + */ +int32 CFE_SB_GetPipeIdByName(CFE_SB_PipeId_t *PipeIdPtr, const char *PipeName) +{ + int32 Status; + CFE_ES_TaskId_t TskId; + uint32 Idx; + char FullName[(OS_MAX_API_NAME * 2)]; + uint16 PendingEventID; + CFE_SB_PipeD_t *PipeDscPtr; + osal_id_t SysQueueId; + + PendingEventID = 0; + SysQueueId = OS_OBJECT_ID_UNDEFINED; + + if (PipeName == NULL || PipeIdPtr == NULL) + { + PendingEventID = CFE_SB_GETPIPEIDBYNAME_NULL_ERR_EID; + Status = CFE_SB_BAD_ARGUMENT; + } + else + { + /* Get QueueID from OSAL */ + Status = OS_QueueGetIdByName(&SysQueueId, PipeName); + if (Status == OS_SUCCESS) + { + Status = CFE_SUCCESS; + } + else + { + PendingEventID = CFE_SB_GETPIPEIDBYNAME_NAME_ERR_EID; + Status = CFE_SB_BAD_ARGUMENT; + } + } + + CFE_SB_LockSharedData(__func__, __LINE__); + + if (Status == CFE_SUCCESS) + { + Idx = CFE_PLATFORM_SB_MAX_PIPES; + PipeDscPtr = CFE_SB_Global.PipeTbl; + while (true) + { + if (Idx == 0) + { + PendingEventID = CFE_SB_GETPIPEIDBYNAME_NAME_ERR_EID; + Status = CFE_SB_BAD_ARGUMENT; + break; + } + + if (OS_ObjectIdEqual(PipeDscPtr->SysQueueId, SysQueueId)) + { + /* grab the ID before we release the lock */ + *PipeIdPtr = CFE_SB_PipeDescGetID(PipeDscPtr); + break; + } /* end if */ + + --Idx; + ++PipeDscPtr; + } + } + + if (Status != CFE_SUCCESS) + { + ++CFE_SB_Global.HKTlmMsg.Payload.GetPipeIdByNameErrorCounter; + } + + CFE_SB_UnlockSharedData(__func__, __LINE__); + + /* Send Events */ + if (Status == CFE_SUCCESS) + { + CFE_EVS_SendEventWithAppID(CFE_SB_GETPIPEIDBYNAME_EID, CFE_EVS_EventType_DEBUG, CFE_SB_Global.AppId, + "PipeIdByName name=%s id=%lu", PipeName, CFE_RESOURCEID_TO_ULONG(*PipeIdPtr)); + } + else + { + /* get TaskId of caller for events */ + CFE_ES_GetTaskID(&TskId); + + switch (PendingEventID) + { + case CFE_SB_GETPIPEIDBYNAME_NULL_ERR_EID: + CFE_EVS_SendEventWithAppID(CFE_SB_GETPIPEIDBYNAME_NULL_ERR_EID, CFE_EVS_EventType_ERROR, + CFE_SB_Global.AppId, "Pipe ID By Name Error:Bad Argument,Requestor %s", + CFE_SB_GetAppTskName(TskId, FullName)); + break; + + case CFE_SB_GETPIPEIDBYNAME_NAME_ERR_EID: + CFE_EVS_SendEventWithAppID(CFE_SB_GETPIPEIDBYNAME_NAME_ERR_EID, CFE_EVS_EventType_ERROR, + CFE_SB_Global.AppId, "Pipe ID By Name Error:Bad Argument,Requestor %s", + CFE_SB_GetAppTskName(TskId, FullName)); + break; + } + } + + return Status; + +} /* end CFE_SB_GetPipeIdByName */ + +/* + * Function: CFE_SB_SubscribeEx - See API and header file for details + */ +int32 CFE_SB_SubscribeEx(CFE_SB_MsgId_t MsgId, CFE_SB_PipeId_t PipeId, CFE_SB_Qos_t Quality, uint16 MsgLim) +{ + return CFE_SB_SubscribeFull(MsgId, PipeId, Quality, MsgLim, (uint8)CFE_SB_MSG_GLOBAL); + +} /* end CFE_SB_SubscribeEx */ + +/* + * Function: CFE_SB_SubscribeLocal - See API and header file for details + */ +int32 CFE_SB_SubscribeLocal(CFE_SB_MsgId_t MsgId, CFE_SB_PipeId_t PipeId, uint16 MsgLim) +{ + return CFE_SB_SubscribeFull(MsgId, PipeId, CFE_SB_DEFAULT_QOS, MsgLim, (uint8)CFE_SB_MSG_LOCAL); + +} /* end CFE_SB_SubscribeLocal */ + +/* + * Function: CFE_SB_Subscribe - See API and header file for details + */ +int32 CFE_SB_Subscribe(CFE_SB_MsgId_t MsgId, CFE_SB_PipeId_t PipeId) +{ + return CFE_SB_SubscribeFull(MsgId, PipeId, CFE_SB_DEFAULT_QOS, (uint16)CFE_PLATFORM_SB_DEFAULT_MSG_LIMIT, + (uint8)CFE_SB_MSG_GLOBAL); + +} /* end CFE_SB_Subscribe */ + +/****************************************************************************** +** Name: CFE_SB_SubscribeFull +** +** Purpose: CFE Internal API used to subscribe to a message. Contains an input +** parameter for all possible subscription choices. This function is +** called by CFE_SB_SubscribeEx, CFE_SB_Subscribe and +** CFE_SB_SubscribeLocal. +** +** Assumptions, External Events, and Notes: +** Has the same typedef as the message Id +** +** Date Written: +** 04/25/2005 +** +** Input Arguments: +** MsgId - Mission unique identifier for the message being requested +** PipeId - The Pipe ID to send the message to +** Quality - Quality of Service (Qos) - priority and reliability +** MsgLim - Max number of messages, with this MsgId, allowed on the +** pipe at any time. +** Scope - Local subscription or broadcasted to peers +** +** Output Arguments: +** None +** +** Return Values: +** Status +** +******************************************************************************/ +int32 CFE_SB_SubscribeFull(CFE_SB_MsgId_t MsgId, CFE_SB_PipeId_t PipeId, CFE_SB_Qos_t Quality, uint16 MsgLim, + uint8 Scope) +{ + CFE_SBR_RouteId_t RouteId; + CFE_SB_PipeD_t * PipeDscPtr; + int32 Status; + CFE_ES_TaskId_t TskId; + CFE_ES_AppId_t AppId; + CFE_SB_DestinationD_t *DestPtr; + uint32 DestCount; + char FullName[(OS_MAX_API_NAME * 2)]; + char PipeName[OS_MAX_API_NAME]; + uint32 Collisions; + uint16 PendingEventID; + + PendingEventID = 0; + Status = CFE_SUCCESS; + DestPtr = NULL; + Collisions = 0; + + /* get the callers Application Id */ + CFE_ES_GetAppID(&AppId); + + /* get TaskId of caller for events */ + CFE_ES_GetTaskID(&TskId); + + /* take semaphore to prevent a task switch during this call */ + CFE_SB_LockSharedData(__func__, __LINE__); + + /* check that the pipe has been created */ + PipeDscPtr = CFE_SB_LocatePipeDescByID(PipeId); + if (!CFE_SB_PipeDescIsMatch(PipeDscPtr, PipeId)) + { + PendingEventID = CFE_SB_SUB_INV_PIPE_EID; + Status = CFE_SB_BAD_ARGUMENT; + } + else if (!CFE_RESOURCEID_TEST_EQUAL(PipeDscPtr->AppId, AppId)) + { + PendingEventID = CFE_SB_SUB_INV_CALLER_EID; + Status = CFE_SB_BAD_ARGUMENT; + } + /* check message id key and scope */ + else if (!CFE_SB_IsValidMsgId(MsgId) || (Scope > 1)) + { + PendingEventID = CFE_SB_SUB_ARG_ERR_EID; + Status = CFE_SB_BAD_ARGUMENT; + } + else + { + /* Get the route, adding one if it does not exist already */ + RouteId = CFE_SBR_GetRouteId(MsgId); + + if (!CFE_SBR_IsValidRouteId(RouteId)) + { + /* Add the route */ + RouteId = CFE_SBR_AddRoute(MsgId, &Collisions); + + /* if all routing table elements are used, send event */ + if (!CFE_SBR_IsValidRouteId(RouteId)) + { + PendingEventID = CFE_SB_MAX_MSGS_MET_EID; + Status = CFE_SB_MAX_MSGS_MET; + } + else + { + /* Increment the MsgIds in use ctr and if it's > the high water mark,*/ + /* adjust the high water mark */ + CFE_SB_Global.StatTlmMsg.Payload.MsgIdsInUse++; + if (CFE_SB_Global.StatTlmMsg.Payload.MsgIdsInUse > CFE_SB_Global.StatTlmMsg.Payload.PeakMsgIdsInUse) + { + CFE_SB_Global.StatTlmMsg.Payload.PeakMsgIdsInUse = CFE_SB_Global.StatTlmMsg.Payload.MsgIdsInUse; + } /* end if */ + } + } + } + + /* If successful up to this point, check if new dest should be added to this route */ + if (Status == CFE_SUCCESS) + { + DestCount = 0; + for (DestPtr = CFE_SBR_GetDestListHeadPtr(RouteId); DestPtr != NULL; DestPtr = DestPtr->Next) + { + ++DestCount; + + /* Check if duplicate (status stays as CFE_SUCCESS) */ + if (CFE_RESOURCEID_TEST_EQUAL(DestPtr->PipeId, PipeId)) + { + PendingEventID = CFE_SB_DUP_SUBSCRIP_EID; + break; + } + + /* Check if limit reached */ + if (DestCount >= CFE_PLATFORM_SB_MAX_DEST_PER_PKT) + { + PendingEventID = CFE_SB_MAX_DESTS_MET_EID; + Status = CFE_SB_MAX_DESTS_MET; + break; + } + } + + /* If no existing dest found, add one now */ + if (DestPtr == NULL) + { + DestPtr = CFE_SB_GetDestinationBlk(); + if (DestPtr == NULL) + { + PendingEventID = CFE_SB_DEST_BLK_ERR_EID; + Status = CFE_SB_BUF_ALOC_ERR; + } + else + { + /* initialize destination block */ + DestPtr->PipeId = PipeId; + DestPtr->MsgId2PipeLim = MsgLim; + DestPtr->Active = CFE_SB_ACTIVE; + DestPtr->BuffCount = 0; + DestPtr->DestCnt = 0; + DestPtr->Scope = Scope; + DestPtr->Prev = NULL; + DestPtr->Next = NULL; + + /* add destination node */ + CFE_SB_AddDestNode(RouteId, DestPtr); + + CFE_SB_Global.StatTlmMsg.Payload.SubscriptionsInUse++; + if (CFE_SB_Global.StatTlmMsg.Payload.SubscriptionsInUse > + CFE_SB_Global.StatTlmMsg.Payload.PeakSubscriptionsInUse) + { + CFE_SB_Global.StatTlmMsg.Payload.PeakSubscriptionsInUse = + CFE_SB_Global.StatTlmMsg.Payload.SubscriptionsInUse; + } + } + } + } + + /* Increment counter before unlock */ + switch (PendingEventID) + { + case CFE_SB_SUB_INV_PIPE_EID: + case CFE_SB_SUB_INV_CALLER_EID: + case CFE_SB_SUB_ARG_ERR_EID: + case CFE_SB_MAX_MSGS_MET_EID: + case CFE_SB_DEST_BLK_ERR_EID: + case CFE_SB_MAX_DESTS_MET_EID: + CFE_SB_Global.HKTlmMsg.Payload.SubscribeErrorCounter++; + break; + case CFE_SB_DUP_SUBSCRIP_EID: + CFE_SB_Global.HKTlmMsg.Payload.DuplicateSubscriptionsCounter++; + break; + } + + CFE_SB_UnlockSharedData(__func__, __LINE__); + + /* Send events now */ + if (PendingEventID != 0) + { + CFE_SB_GetPipeName(PipeName, sizeof(PipeName), PipeId); + + switch (PendingEventID) + { + case CFE_SB_DUP_SUBSCRIP_EID: + CFE_EVS_SendEventWithAppID(CFE_SB_DUP_SUBSCRIP_EID, CFE_EVS_EventType_INFORMATION, CFE_SB_Global.AppId, + "Duplicate Subscription,MsgId 0x%x on %s pipe,app %s", + (unsigned int)CFE_SB_MsgIdToValue(MsgId), PipeName, + CFE_SB_GetAppTskName(TskId, FullName)); + break; + + case CFE_SB_SUB_INV_CALLER_EID: + CFE_EVS_SendEventWithAppID(CFE_SB_SUB_INV_CALLER_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, + "Subscribe Err:Caller(%s) is not the owner of pipe %lu,Msg=0x%x", + CFE_SB_GetAppTskName(TskId, FullName), CFE_RESOURCEID_TO_ULONG(PipeId), + (unsigned int)CFE_SB_MsgIdToValue(MsgId)); + break; + + case CFE_SB_SUB_INV_PIPE_EID: + CFE_EVS_SendEventWithAppID(CFE_SB_SUB_INV_PIPE_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, + "Subscribe Err:Invalid Pipe Id,Msg=0x%x,PipeId=%lu,App %s", + (unsigned int)CFE_SB_MsgIdToValue(MsgId), CFE_RESOURCEID_TO_ULONG(PipeId), + CFE_SB_GetAppTskName(TskId, FullName)); + break; + + case CFE_SB_DEST_BLK_ERR_EID: + CFE_EVS_SendEventWithAppID(CFE_SB_DEST_BLK_ERR_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, + "Subscribe Err:Request for Destination Blk failed for Msg 0x%x", + (unsigned int)CFE_SB_MsgIdToValue(MsgId)); + break; + + case CFE_SB_MAX_DESTS_MET_EID: + CFE_EVS_SendEventWithAppID(CFE_SB_MAX_DESTS_MET_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, + "Subscribe Err:Max Dests(%d)In Use For Msg 0x%x,pipe %s,app %s", + CFE_PLATFORM_SB_MAX_DEST_PER_PKT, (unsigned int)CFE_SB_MsgIdToValue(MsgId), + PipeName, CFE_SB_GetAppTskName(TskId, FullName)); + break; + + case CFE_SB_MAX_MSGS_MET_EID: + CFE_EVS_SendEventWithAppID(CFE_SB_MAX_MSGS_MET_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, + "Subscribe Err:Max Msgs(%d)In Use,MsgId 0x%x,pipe %s,app %s", + CFE_PLATFORM_SB_MAX_MSG_IDS, (unsigned int)CFE_SB_MsgIdToValue(MsgId), + PipeName, CFE_SB_GetAppTskName(TskId, FullName)); + break; + + case CFE_SB_SUB_ARG_ERR_EID: + CFE_EVS_SendEventWithAppID(CFE_SB_SUB_ARG_ERR_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, + "Subscribe Err:Bad Arg,MsgId 0x%x,PipeId %lu,app %s,scope %d", + (unsigned int)CFE_SB_MsgIdToValue(MsgId), CFE_RESOURCEID_TO_ULONG(PipeId), + CFE_SB_GetAppTskName(TskId, FullName), Scope); + break; + } + } + else if (Status == CFE_SUCCESS) + { + /* If no other event pending, send a debug event indicating success */ + CFE_EVS_SendEventWithAppID(CFE_SB_SUBSCRIPTION_RCVD_EID, CFE_EVS_EventType_DEBUG, CFE_SB_Global.AppId, + "Subscription Rcvd:MsgId 0x%x on PipeId %lu,app %s", + (unsigned int)CFE_SB_MsgIdToValue(MsgId), CFE_RESOURCEID_TO_ULONG(PipeId), + CFE_SB_GetAppTskName(TskId, FullName)); + } + + if (Status == CFE_SUCCESS && Scope == CFE_SB_MSG_GLOBAL) + { + CFE_SB_SendSubscriptionReport(MsgId, PipeId, Quality); + } + + if (Collisions != 0) + { + CFE_EVS_SendEventWithAppID(CFE_SB_HASHCOLLISION_EID, CFE_EVS_EventType_DEBUG, CFE_SB_Global.AppId, + "Msg hash collision: MsgId = 0x%x, collisions = %u", + (unsigned int)CFE_SB_MsgIdToValue(MsgId), (unsigned int)Collisions); + } + + return Status; + +} /* end CFE_SB_SubscribeFull */ + +/* + * Function: CFE_SB_Unsubscribe - See API and header file for details + */ +int32 CFE_SB_Unsubscribe(CFE_SB_MsgId_t MsgId, CFE_SB_PipeId_t PipeId) +{ + CFE_ES_AppId_t CallerId; + int32 Status = 0; + + /* get the callers Application Id */ + CFE_ES_GetAppID(&CallerId); + + Status = CFE_SB_UnsubscribeFull(MsgId, PipeId, (uint8)CFE_SB_MSG_GLOBAL, CallerId); + + return Status; + +} /* end CFE_SB_Unsubscribe */ + +/* + * Function: CFE_SB_UnsubscribeLocal - See API and header file for details + */ +int32 CFE_SB_UnsubscribeLocal(CFE_SB_MsgId_t MsgId, CFE_SB_PipeId_t PipeId) +{ + CFE_ES_AppId_t CallerId; + int32 Status = 0; + + /* get the callers Application Id */ + CFE_ES_GetAppID(&CallerId); + + Status = CFE_SB_UnsubscribeFull(MsgId, PipeId, (uint8)CFE_SB_MSG_LOCAL, CallerId); + + return Status; + +} /* end CFE_SB_UnsubscribeLocal */ + +/****************************************************************************** +** Name: CFE_SB_UnsubscribeAppId +** +** Purpose: CFE Internal API intented to be called by CFE_ES when an applications +** SB resources need to be freed. The regular unsibscribe api won't work +** because it does a check to ensure the caller is the owner of the pipe. +** +** Date Written: +** 03/15/2007 +** +** Input Arguments: +** MsgId +** PipeId +** AppId +** +** Output Arguments: +** None +** +** Return Values: +** Status +** +******************************************************************************/ +int32 CFE_SB_UnsubscribeWithAppId(CFE_SB_MsgId_t MsgId, CFE_SB_PipeId_t PipeId, CFE_ES_AppId_t AppId) +{ + int32 Status = 0; + + Status = CFE_SB_UnsubscribeFull(MsgId, PipeId, (uint8)CFE_SB_MSG_LOCAL, AppId); + + return Status; + +} /* end CFE_SB_UnsubscribeWithAppId */ + +/****************************************************************************** +** Name: CFE_SB_UnsubscribeFull +** +** Purpose: CFE Internal API used to unsubscribe to a message. +** +** Assumptions, External Events, and Notes: +** +** +** Notes:This function cannot be called directly,it would not be semaphore protected. +** Also,if more than one subscription is found, this function will remove all +** entries that match. +** +** Date Written: +** 04/25/2005 +** +** Input Arguments: +** MsgId +** PipeId +** Scope +** AppId +** +** Output Arguments: +** None +** +** Return Values: +** Status +** +******************************************************************************/ +int32 CFE_SB_UnsubscribeFull(CFE_SB_MsgId_t MsgId, CFE_SB_PipeId_t PipeId, uint8 Scope, CFE_ES_AppId_t AppId) +{ + int32 Status; + CFE_SBR_RouteId_t RouteId; + CFE_ES_TaskId_t TskId; + CFE_SB_DestinationD_t *DestPtr; + char FullName[(OS_MAX_API_NAME * 2)]; + char PipeName[OS_MAX_API_NAME]; + CFE_SB_PipeD_t * PipeDscPtr; + uint16 PendingEventID; + + PendingEventID = 0; + Status = CFE_SUCCESS; + DestPtr = NULL; + + /* get TaskId of caller for events */ + CFE_ES_GetTaskID(&TskId); + + /* take semaphore to prevent a task switch during this call */ + CFE_SB_LockSharedData(__func__, __LINE__); + + /* check that the pipe has been created */ + PipeDscPtr = CFE_SB_LocatePipeDescByID(PipeId); + if (!CFE_SB_PipeDescIsMatch(PipeDscPtr, PipeId)) + { + PendingEventID = CFE_SB_UNSUB_INV_PIPE_EID; + Status = CFE_SB_BAD_ARGUMENT; + } + /* if given 'AppId' is not the owner of the pipe, send error event and return */ + else if (!CFE_RESOURCEID_TEST_EQUAL(PipeDscPtr->AppId, AppId)) + { + PendingEventID = CFE_SB_UNSUB_INV_CALLER_EID; + Status = CFE_SB_BAD_ARGUMENT; + } + /* check input parameters */ + else if (!CFE_SB_IsValidMsgId(MsgId) || (Scope > 1)) + { + PendingEventID = CFE_SB_UNSUB_ARG_ERR_EID; + Status = CFE_SB_BAD_ARGUMENT; + } + else + { + /* get routing id */ + RouteId = CFE_SBR_GetRouteId(MsgId); + + /* if there have never been subscriptions for this message id... */ + if (!CFE_SBR_IsValidRouteId(RouteId)) + { + PendingEventID = CFE_SB_UNSUB_NO_SUBS_EID; + /* Status stays CFE_SUCCESS here */ + } + else + { + /* Get the destination pointer */ + DestPtr = CFE_SB_GetDestPtr(RouteId, PipeId); + + if (DestPtr != NULL) + { + /* match found, remove destination */ + CFE_SB_RemoveDest(RouteId, DestPtr); + } + else + { + PendingEventID = CFE_SB_UNSUB_NO_SUBS_EID; + } + } + } /* end if */ + + CFE_SB_UnlockSharedData(__func__, __LINE__); + + if (PendingEventID != 0) + { + switch (PendingEventID) + { + case CFE_SB_UNSUB_NO_SUBS_EID: + CFE_SB_GetPipeName(PipeName, sizeof(PipeName), PipeId); + CFE_EVS_SendEventWithAppID(CFE_SB_UNSUB_NO_SUBS_EID, CFE_EVS_EventType_INFORMATION, CFE_SB_Global.AppId, + "Unsubscribe Err:No subs for Msg 0x%x on %s,app %s", + (unsigned int)CFE_SB_MsgIdToValue(MsgId), PipeName, + CFE_SB_GetAppTskName(TskId, FullName)); + break; + + case CFE_SB_UNSUB_INV_PIPE_EID: + CFE_EVS_SendEventWithAppID(CFE_SB_UNSUB_INV_PIPE_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, + "Unsubscribe Err:Invalid Pipe Id Msg=0x%x,Pipe=%lu,app=%s", + (unsigned int)CFE_SB_MsgIdToValue(MsgId), CFE_RESOURCEID_TO_ULONG(PipeId), + CFE_SB_GetAppTskName(TskId, FullName)); + break; + + case CFE_SB_UNSUB_INV_CALLER_EID: + CFE_EVS_SendEventWithAppID(CFE_SB_UNSUB_INV_CALLER_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, + "Unsubscribe Err:Caller(%s) is not the owner of pipe %lu,Msg=0x%x", + CFE_SB_GetAppTskName(TskId, FullName), CFE_RESOURCEID_TO_ULONG(PipeId), + (unsigned int)CFE_SB_MsgIdToValue(MsgId)); + break; + + case CFE_SB_UNSUB_ARG_ERR_EID: + CFE_EVS_SendEventWithAppID(CFE_SB_UNSUB_ARG_ERR_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, + "UnSubscribe Err:Bad Arg,MsgId 0x%x,PipeId %lu,app %s,scope %d", + (unsigned int)CFE_SB_MsgIdToValue(MsgId), CFE_RESOURCEID_TO_ULONG(PipeId), + CFE_SB_GetAppTskName(TskId, FullName), (int)Scope); + break; + } + } + else if (Status == CFE_SUCCESS) + { + /* if no other event pending, send a debug event for successful unsubscribe */ + CFE_EVS_SendEventWithAppID(CFE_SB_SUBSCRIPTION_REMOVED_EID, CFE_EVS_EventType_DEBUG, CFE_SB_Global.AppId, + "Subscription Removed:Msg 0x%x on pipe %lu,app %s", + (unsigned int)CFE_SB_MsgIdToValue(MsgId), CFE_RESOURCEID_TO_ULONG(PipeId), + CFE_SB_GetAppTskName(TskId, FullName)); + } + + return Status; +} /* end CFE_SB_UnsubscribeFull */ + +/* + * Function CFE_SB_TransmitMsg - See API and header file for details + */ +int32 CFE_SB_TransmitMsg(CFE_MSG_Message_t *MsgPtr, bool IncrementSequenceCount) +{ + int32 Status; + CFE_MSG_Size_t Size = 0; + CFE_SB_MsgId_t MsgId = CFE_SB_INVALID_MSG_ID; + CFE_ES_TaskId_t TskId; + char FullName[(OS_MAX_API_NAME * 2)]; + CFE_SB_BufferD_t *BufDscPtr; + CFE_SBR_RouteId_t RouteId; + uint16 PendingEventID; + + PendingEventID = 0; + BufDscPtr = NULL; + RouteId = CFE_SBR_INVALID_ROUTE_ID; + + Status = CFE_SB_TransmitMsgValidate(MsgPtr, &MsgId, &Size, &RouteId); + + CFE_SB_LockSharedData(__func__, __LINE__); + + if (Status == CFE_SUCCESS && CFE_SBR_IsValidRouteId(RouteId)) + { + /* Get buffer - note this pre-initializes the returned buffer with + * a use count of 1, which refers to this task as it fills the buffer. */ + BufDscPtr = CFE_SB_GetBufferFromPool(Size); + if (BufDscPtr == NULL) + { + PendingEventID = CFE_SB_GET_BUF_ERR_EID; + Status = CFE_SB_BUF_ALOC_ERR; + } + } + + /* + * Increment the MsgSendErrorCounter only if there was a real error, + * such as a validation issue or failure to allocate a buffer. + * + * (This should NOT be done if simply no route) + */ + if (Status != CFE_SUCCESS) + { + CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter++; + } + + CFE_SB_UnlockSharedData(__func__, __LINE__); + + /* + * If a buffer was obtained above, then copy the content into it + * and broadcast it to all subscribers in the route. + * + * Note - if there is no route / no subscribers, the "Status" will + * be CFE_SUCCESS because CFE_SB_TransmitMsgValidate() succeeded, + * but there will be no buffer because CFE_SBR_IsValidRouteId() returned + * false. + * + * But if the desciptor 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->AutoSequence = IncrementSequenceCount; + CFE_MSG_GetType(MsgPtr, &BufDscPtr->ContentType); + + /* + * This routine will use best-effort to send to all subscribers, + * increment the buffer use count for every successful delivery, + * and send an event/increment counter for any unsucessful delivery. + */ + CFE_SB_BroadcastBufferToRoute(BufDscPtr, RouteId); + + /* + * The broadcast function consumes the buffer, so it should not be + * accessed in this function anymore + */ + BufDscPtr = NULL; + } + + if (PendingEventID == CFE_SB_GET_BUF_ERR_EID) + { + /* Get task id for events and Sender Info*/ + CFE_ES_GetTaskID(&TskId); + + if (CFE_SB_RequestToSendEvent(TskId, CFE_SB_GET_BUF_ERR_EID_BIT) == CFE_SB_GRANTED) + { + CFE_EVS_SendEventWithAppID(CFE_SB_GET_BUF_ERR_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, + "Send Err:Request for Buffer Failed. MsgId 0x%x,app %s,size %d", + (unsigned int)CFE_SB_MsgIdToValue(MsgId), CFE_SB_GetAppTskName(TskId, FullName), + (int)Size); + + /* clear the bit so the task may send this event again */ + CFE_SB_FinishSendEvent(TskId, CFE_SB_GET_BUF_ERR_EID_BIT); + } + } + + return Status; +} + +/*****************************************************************************/ +/** + * \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) + */ +int32 CFE_SB_TransmitMsgValidate(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; + } /* end if */ + } + + 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: + 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)); + break; + + case CFE_SB_SEND_INV_MSGID_EID: + 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)); + break; + + case CFE_SB_MSG_TOO_BIG_EID: + 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); + 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); + } /* end if */ + break; + } + + } /* end if */ + + return Status; +} + +/*****************************************************************************/ +/** + * \brief Internal routine implements full send logic + * + * \param[in] BufDscPtr Pointer to the buffer description from the memory pool, + * released prior to return + * \param[in] RouteId Route to send to + */ +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 Status; + 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 subsribers this whole logic can be skipped */ + if (CFE_SBR_IsValidRouteId(RouteId)) + { + /* Set the seq count if requested (while locked) before actually sending */ + /* For some reason this is only done for TLM types (historical, TBD) */ + if (BufDscPtr->AutoSequence && BufDscPtr->ContentType == CFE_MSG_Type_Tlm) + { + CFE_SBR_IncrementSequenceCounter(RouteId); + + /* Write the sequence into the message header itself (overwrites whatever was there) */ + CFE_MSG_SetSequenceCount(&BufDscPtr->Content.Msg, CFE_SBR_GetSequenceCounter(RouteId)); + } + + /* 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; + } /* end if */ + + /* 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; + } /* end if */ + + /* + ** Write the buffer descriptor to the queue of the pipe. If the write + ** failed, log info and increment the pipe's error counter. + */ + Status = OS_QueuePut(PipeDscPtr->SysQueueId, &BufDscPtr, sizeof(BufDscPtr), 0); + + if (Status == 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 if (Status == OS_QUEUE_FULL) + { + SBSndErr.EvtBuf[SBSndErr.EvtsToSnd].PipeId = DestPtr->PipeId; + SBSndErr.EvtBuf[SBSndErr.EvtsToSnd].EventId = CFE_SB_Q_FULL_ERR_EID; + SBSndErr.EvtsToSnd++; + CFE_SB_Global.HKTlmMsg.Payload.PipeOverflowErrorCounter++; + PipeDscPtr->SendErrors++; + } + else + { + /* Unexpected error while writing to queue. */ + SBSndErr.EvtBuf[SBSndErr.EvtsToSnd].PipeId = DestPtr->PipeId; + SBSndErr.EvtBuf[SBSndErr.EvtsToSnd].EventId = CFE_SB_Q_WR_ERR_EID; + SBSndErr.EvtBuf[SBSndErr.EvtsToSnd].ErrStat = Status; + SBSndErr.EvtsToSnd++; + CFE_SB_Global.HKTlmMsg.Payload.InternalErrorCounter++; + PipeDscPtr->SendErrors++; + } /*end if */ + + } /* end loop over destinations */ + } + + /* + * If any specific delivery issues occured, 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); + } /* end if */ + } + 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); + } /* end if */ + } + 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 0x%x", + (unsigned int)CFE_SB_MsgIdToValue(BufDscPtr->MsgId), PipeName, + CFE_SB_GetAppTskName(TskId, FullName), + (unsigned int)SBSndErr.EvtBuf[i].ErrStat); + + /* clear the bit so the task may send this event again */ + CFE_SB_FinishSendEvent(TskId, CFE_SB_Q_WR_ERR_EID_BIT); + } /* end if */ + + } /* end if */ + } +} + +/* + * Function: CFE_SB_ReceiveBuffer - See API and header file for details + */ +int32 CFE_SB_ReceiveBuffer(CFE_SB_Buffer_t **BufPtr, CFE_SB_PipeId_t PipeId, int32 TimeOut) +{ + int32 Status; + int32 RcvStatus; + 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; + RcvStatus = OS_SUCCESS; + + /* + * Check input args and see if any are bad, which require + * a "BAD_ARG_EID" to be generated. + * + * Also translate the timeout here. Timeouts greater than 0 + * may be passed to OSAL directly, but the two fixed constants + * CFE_SB_PEND_FOREVER and CFE_SB_POLL are checked explicitly, + * to maintain API independence - even though the values are + * currently defined the same. + */ + + if (BufPtr == NULL) + { + PendingEventID = CFE_SB_RCV_BAD_ARG_EID; + Status = CFE_SB_BAD_ARGUMENT; + } + else if (TimeOut > 0) + { + /* time outs greater than 0 can be passed to OSAL directly */ + SysTimeout = TimeOut; + } + else if (TimeOut == CFE_SB_POLL) + { + SysTimeout = OS_CHECK; + } + else if (TimeOut != CFE_SB_PEND_FOREVER) + { + /* any other timeout value is invalid */ + PendingEventID = CFE_SB_RCV_BAD_ARG_EID; + Status = CFE_SB_BAD_ARGUMENT; + } + + /* If OK, then lock and pull relevent info from Pipe Descriptor */ + if (Status == CFE_SUCCESS) + { + 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__); + } + + /* + * 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) + { + /* Read the buffer descriptor address from the queue. */ + RcvStatus = 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 (RcvStatus == OS_SUCCESS && BufDscPtr != NULL && BufDscSize == sizeof(BufDscPtr)) + { + /* Pass through */ + } + else if (RcvStatus == OS_QUEUE_EMPTY) + { + /* normal if using CFE_SB_POLL */ + Status = CFE_SB_NO_MESSAGE; + } + else if (RcvStatus == 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; + } + } + + /* Now re-lock to store the buffer in the pipe descriptor */ + CFE_SB_LockSharedData(__func__, __LINE__); + + if (Status == CFE_SUCCESS) + { + /* + * 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); + } + + /* Before unlocking, check the PendingEventID and increment relevant error counter */ + if (Status != CFE_SUCCESS) + { + if (PendingEventID == CFE_SB_RCV_BAD_ARG_EID || PendingEventID == CFE_SB_BAD_PIPEID_EID) + { + ++CFE_SB_Global.HKTlmMsg.Payload.MsgReceiveErrorCounter; + } + else + { + /* For any other unexpected error (e.g. CFE_SB_Q_RD_ERR_EID) */ + ++CFE_SB_Global.HKTlmMsg.Payload.InternalErrorCounter; + } + } + + CFE_SB_UnlockSharedData(__func__, __LINE__); + + /* Now actually send the event, after unlocking (do not call EVS with SB locked) */ + if (PendingEventID != 0) + { + /* get task id for events */ + CFE_ES_GetTaskID(&TskId); + + switch (PendingEventID) + { + case CFE_SB_Q_RD_ERR_EID: + CFE_EVS_SendEventWithAppID(CFE_SB_Q_RD_ERR_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, + "Pipe Read Err,pipe %lu,app %s,stat 0x%x", CFE_RESOURCEID_TO_ULONG(PipeId), + CFE_SB_GetAppTskName(TskId, FullName), (unsigned int)RcvStatus); + 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; + } + } + + /* If not successful, set the output pointer to NULL */ + if (Status != CFE_SUCCESS && BufPtr != NULL) + { + *BufPtr = NULL; + } + + return Status; +} + +/* + * Function: CFE_SB_AllocateMessageBuffer - See API and header file for details + */ +CFE_SB_Buffer_t *CFE_SB_AllocateMessageBuffer(size_t MsgSize) +{ + CFE_ES_AppId_t AppId; + CFE_SB_BufferD_t *BufDscPtr; + CFE_SB_Buffer_t * BufPtr; + + AppId = CFE_ES_APPID_UNDEFINED; + BufDscPtr = NULL; + BufPtr = NULL; + + if (MsgSize > CFE_MISSION_SB_MAX_SB_MSG_SIZE) + { + CFE_ES_WriteToSysLog(" CFE_SB:ZeroCopyGetPtr-Failed, MsgSize is too large\n"); + return NULL; + } + + /* get callers AppId */ + if (CFE_ES_GetAppID(&AppId) == CFE_SUCCESS) + { + CFE_SB_LockSharedData(__func__, __LINE__); + + /* + * All this needs to do is get a descriptor from the pool, + * and associate that descriptor with this app ID, so it + * can be freed if this app is deleted before it uses it. + */ + BufDscPtr = CFE_SB_GetBufferFromPool(MsgSize); + + if (BufDscPtr != NULL) + { + /* Track the buffer as a zero-copy assigned to this app ID */ + BufDscPtr->AppId = AppId; + BufPtr = &BufDscPtr->Content; + CFE_SB_TrackingListAdd(&CFE_SB_Global.ZeroCopyList, &BufDscPtr->Link); + } + + CFE_SB_UnlockSharedData(__func__, __LINE__); + } + + if (BufPtr != NULL) + { + /* + * If a buffer was obtained, wipe it now. + * (This ensures the buffer is fully cleared at least once, + * no stale data from a prior use of the same memory) + */ + memset(BufPtr, 0, MsgSize); + } + + return BufPtr; + +} /* CFE_SB_AllocateMessageBuffer */ + +/* + * Helper function to do sanity checks on the Zero Copy Buffer and + * outputs the encapsulating descriptor if successful + */ +int32 CFE_SB_ZeroCopyBufferValidate(CFE_SB_Buffer_t *BufPtr, CFE_SB_BufferD_t **BufDscPtr) +{ + cpuaddr BufDscAddr; + + /* + * Sanity Check that the pointers are not NULL + */ + if (BufPtr == NULL) + { + 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; + + /* + * 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; +} + +/* + * Function: CFE_SB_ReleaseMessageBuffer - See API and header file for details + */ +CFE_Status_t CFE_SB_ReleaseMessageBuffer(CFE_SB_Buffer_t *BufPtr) +{ + CFE_SB_BufferD_t *BufDscPtr; + int32 Status; + + Status = CFE_SB_ZeroCopyBufferValidate(BufPtr, &BufDscPtr); + + CFE_SB_LockSharedData(__func__, __LINE__); + + if (Status == CFE_SUCCESS) + { + /* Clear the ownership app ID and decrement use count (may also free) */ + BufDscPtr->AppId = CFE_ES_APPID_UNDEFINED; + CFE_SB_DecrBufUseCnt(BufDscPtr); + } + + CFE_SB_UnlockSharedData(__func__, __LINE__); + + return Status; + +} /* end CFE_SB_ReleaseMessageBuffer */ + +/* + * Function CFE_SB_TransmitBuffer - See API and header file for details + */ +int32 CFE_SB_TransmitBuffer(CFE_SB_Buffer_t *BufPtr, bool IncrementSequenceCount) +{ + int32 Status; + CFE_SB_BufferD_t *BufDscPtr; + CFE_SBR_RouteId_t RouteId; + + Status = CFE_SB_ZeroCopyBufferValidate(BufPtr, &BufDscPtr); + + if (Status == CFE_SUCCESS) + { + /* Validate the content and get the MsgId, store it in the descriptor */ + Status = CFE_SB_TransmitMsgValidate(&BufPtr->Msg, &BufDscPtr->MsgId, &BufDscPtr->ContentSize, &RouteId); + + /* + * Broadcast the message if validation succeeded. + * + * Note that for the case of no subscribers, the validation returns CFE_SUCCESS + * but the actual route ID may be invalid. This is OK and considered normal- + * the validation will increment the NoSubscribers count, but we should NOT + * increment the MsgSendErrorCounter here - it is not really a sending error to + * have no subscribers. CFE_SB_BroadcastBufferToRoute() will not send to + * anything if the route is not valid (benign). + */ + if (Status == CFE_SUCCESS) + { + BufDscPtr->AutoSequence = IncrementSequenceCount; + CFE_MSG_GetType(&BufPtr->Msg, &BufDscPtr->ContentType); + + /* Now broadcast the message, which consumes the buffer */ + CFE_SB_BroadcastBufferToRoute(BufDscPtr, RouteId); + + /* + * IMPORTANT - the descriptor might be freed at any time after this, + * so the descriptor should not be accessed again after this point. + */ + BufDscPtr = NULL; + } + } + + if (Status != CFE_SUCCESS) + { + /* Increment send error counter for validation failure */ + CFE_SB_LockSharedData(__func__, __LINE__); + CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter++; + CFE_SB_UnlockSharedData(__func__, __LINE__); + } + + return Status; +} diff --git a/modules/sb/fsw/src/cfe_sb_buf.c b/modules/sb/fsw/src/cfe_sb_buf.c new file mode 100644 index 000000000..fc4d595ae --- /dev/null +++ b/modules/sb/fsw/src/cfe_sb_buf.c @@ -0,0 +1,317 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/****************************************************************************** +** File: cfe_sb_buf.c +** +** Purpose: +** This file contains the source code for the SB memory management +** functions. +** +** Author: R.McGraw/SSI +** +******************************************************************************/ + +/* +** Include Files +*/ + +#include "cfe_sb_module_all.h" + +/* + * The actual message content of a SB Buffer Descriptor is the + * offset of the content member. This will be auto-aligned by + * the compiler according to the requirements of the machine. + */ +#define CFE_SB_BUFFERD_CONTENT_OFFSET (offsetof(CFE_SB_BufferD_t, Content)) + +/****************************************************************************** + * + * Helper function to reset/clear the links on a list node (make empty) + */ +void CFE_SB_TrackingListReset(CFE_SB_BufferLink_t *Link) +{ + /* A singleton node/empty list points to itself */ + Link->Prev = Link; + Link->Next = Link; +} + +/****************************************************************************** + * + * Helper function to remove a node from a tracking list + */ +void CFE_SB_TrackingListRemove(CFE_SB_BufferLink_t *Node) +{ + /* Remove from list */ + Node->Prev->Next = Node->Next; + Node->Next->Prev = Node->Prev; + + /* The node is now a singleton */ + CFE_SB_TrackingListReset(Node); +} + +/****************************************************************************** + * + * Helper function to add a node to a tracking list + */ +void CFE_SB_TrackingListAdd(CFE_SB_BufferLink_t *List, CFE_SB_BufferLink_t *Node) +{ + /* Connect this node to the list at "prev" position (tail) */ + Node->Prev = List->Prev; + Node->Next = List; + + /* Connect list nodes to this node */ + Node->Prev->Next = Node; + Node->Next->Prev = Node; +} + +/****************************************************************************** +** Function: CFE_SB_GetBufferFromPool() +** +** Purpose: +** Request a buffer from the SB buffer pool. The SB buffer pool is a +** pre-allocated block of memory of size CFE_PLATFORM_SB_BUF_MEMORY_BYTES. It is used +** by the SB to dynamically allocate memory to hold the message and a buffer +** descriptor associated with the message during the sending of a message. +** +** Note: +** This must only be invoked while holding the SB global lock +** +** Arguments: +** MaxMsgSize : Size of the buffer content area in bytes. +** +** Return: +** Pointer to the buffer descriptor for the new buffer, or NULL if the buffer +** could not be allocated. +*/ + +CFE_SB_BufferD_t *CFE_SB_GetBufferFromPool(size_t MaxMsgSize) +{ + int32 stat1; + size_t AllocSize; + CFE_SB_BufferD_t *bd = NULL; + + /* The allocation needs to include enough space for the descriptor object */ + AllocSize = MaxMsgSize + CFE_SB_BUFFERD_CONTENT_OFFSET; + + /* Allocate a new buffer descriptor from the SB memory pool.*/ + stat1 = CFE_ES_GetPoolBuf((CFE_ES_MemPoolBuf_t *)&bd, CFE_SB_Global.Mem.PoolHdl, AllocSize); + if (stat1 < 0) + { + return NULL; + } + + /* increment the number of buffers in use and adjust the high water mark if needed */ + CFE_SB_Global.StatTlmMsg.Payload.SBBuffersInUse++; + if (CFE_SB_Global.StatTlmMsg.Payload.SBBuffersInUse > CFE_SB_Global.StatTlmMsg.Payload.PeakSBBuffersInUse) + { + CFE_SB_Global.StatTlmMsg.Payload.PeakSBBuffersInUse = CFE_SB_Global.StatTlmMsg.Payload.SBBuffersInUse; + } /* end if */ + + /* Add the size of the actual buffer to the memory-in-use ctr and */ + /* adjust the high water mark if needed */ + CFE_SB_Global.StatTlmMsg.Payload.MemInUse += AllocSize; + if (CFE_SB_Global.StatTlmMsg.Payload.MemInUse > CFE_SB_Global.StatTlmMsg.Payload.PeakMemInUse) + { + CFE_SB_Global.StatTlmMsg.Payload.PeakMemInUse = CFE_SB_Global.StatTlmMsg.Payload.MemInUse; + } /* end if */ + + /* Initialize the buffer descriptor structure. */ + memset(bd, 0, CFE_SB_BUFFERD_CONTENT_OFFSET); + + bd->MsgId = CFE_SB_INVALID_MSG_ID; + bd->UseCount = 1; + bd->AllocatedSize = AllocSize; + + CFE_SB_TrackingListReset(&bd->Link); + + return bd; + +} /* CFE_SB_GetBufferFromPool */ + +/****************************************************************************** +** Function: CFE_SB_ReturnBufferToPool() +** +** Purpose: +** This function will return two blocks of memory back to the memory pool. +** One block is the memory used to store the actual message, the other block +** was used to store the buffer descriptor for the message. +** +** Note: +** This must only be invoked while holding the SB global lock +** +** Arguments: +** bd : Pointer to the buffer descriptor. +** +** Return: +** None +*/ +void CFE_SB_ReturnBufferToPool(CFE_SB_BufferD_t *bd) +{ + /* Remove from any tracking list (no effect if not in a list) */ + CFE_SB_TrackingListRemove(&bd->Link); + + --CFE_SB_Global.StatTlmMsg.Payload.SBBuffersInUse; + CFE_SB_Global.StatTlmMsg.Payload.MemInUse -= bd->AllocatedSize; + + /* finally give the buf descriptor back to the buf descriptor pool */ + CFE_ES_PutPoolBuf(CFE_SB_Global.Mem.PoolHdl, bd); + +} /* end CFE_SB_ReturnBufferToPool */ + +/****************************************************************************** +** Function: CFE_SB_IncrBufUseCnt() +** +** Purpose: +** This function will increment the UseCount of a particular buffer. +** +** Note: +** UseCount is a variable in the CFE_SB_BufferD_t and is used only to +** determine when a buffer may be returned to the memory pool. +** +** This must only be invoked while holding the SB global lock +** +** Arguments: +** bd : Pointer to the buffer descriptor. +** +** Return: +** CFE_SUCCESS for normal operation. +*/ +void CFE_SB_IncrBufUseCnt(CFE_SB_BufferD_t *bd) +{ + /* range check the UseCount variable */ + if (bd->UseCount < 0x7FFF) + { + ++bd->UseCount; + } + +} /* end CFE_SB_DecrBufUseCnt */ + +/****************************************************************************** +** Function: CFE_SB_DecrBufUseCnt() +** +** Purpose: +** This function will decrement the UseCount of a particular buffer. If the +** the UseCount is decremented to zero, it will return the buffer to the +** memory pool. +** +** Note: +** UseCount is a variable in the CFE_SB_BufferD_t and is used only to +** determine when a buffer may be returned to the memory pool. +** +** This must only be invoked while holding the SB global lock +** +** Arguments: +** bd : Pointer to the buffer descriptor. +** +** Return: +** CFE_SUCCESS for normal operation. +*/ +void CFE_SB_DecrBufUseCnt(CFE_SB_BufferD_t *bd) +{ + /* range check the UseCount variable */ + if (bd->UseCount > 0) + { + --bd->UseCount; + + if (bd->UseCount == 0) + { + CFE_SB_ReturnBufferToPool(bd); + } + } + +} /* end CFE_SB_DecrBufUseCnt */ + +/****************************************************************************** +** Function: CFE_SB_GetDestinationBlk() +** +** Purpose: +** This function gets a destination descriptor from the SB memory pool. +** +** Note: +** This must only be invoked while holding the SB global lock +** +** Arguments: +** None +** +** Return: +** Pointer to the destination descriptor +*/ +CFE_SB_DestinationD_t *CFE_SB_GetDestinationBlk(void) +{ + int32 Stat; + CFE_SB_DestinationD_t *Dest = NULL; + + /* Allocate a new destination descriptor from the SB memory pool.*/ + Stat = CFE_ES_GetPoolBuf((CFE_ES_MemPoolBuf_t *)&Dest, CFE_SB_Global.Mem.PoolHdl, sizeof(CFE_SB_DestinationD_t)); + if (Stat < 0) + { + return NULL; + } + + /* Add the size of a destination descriptor to the memory-in-use ctr and */ + /* adjust the high water mark if needed */ + CFE_SB_Global.StatTlmMsg.Payload.MemInUse += Stat; + if (CFE_SB_Global.StatTlmMsg.Payload.MemInUse > CFE_SB_Global.StatTlmMsg.Payload.PeakMemInUse) + { + CFE_SB_Global.StatTlmMsg.Payload.PeakMemInUse = CFE_SB_Global.StatTlmMsg.Payload.MemInUse; + } /* end if */ + + return Dest; + +} /* end CFE_SB_GetDestinationBlk */ + +/****************************************************************************** +** Function: CFE_SB_PutDestinationBlk() +** +** Purpose: +** This function returns a destination descriptor to the SB memory pool. +** +** Note: +** This must only be invoked while holding the SB global lock +** +** Arguments: +** None +** +** Return: +** Pointer to the destination descriptor +*/ +int32 CFE_SB_PutDestinationBlk(CFE_SB_DestinationD_t *Dest) +{ + int32 Stat; + + if (Dest == NULL) + { + return CFE_SB_BAD_ARGUMENT; + } /* end if */ + + /* give the destination block back to the SB memory pool */ + Stat = CFE_ES_PutPoolBuf(CFE_SB_Global.Mem.PoolHdl, Dest); + if (Stat > 0) + { + /* Substract the size of the destination block from the Memory in use ctr */ + CFE_SB_Global.StatTlmMsg.Payload.MemInUse -= Stat; + } /* end if */ + + return CFE_SUCCESS; + +} /* end CFE_SB_PutDestinationBlk */ + +/*****************************************************************************/ diff --git a/modules/sb/fsw/src/cfe_sb_init.c b/modules/sb/fsw/src/cfe_sb_init.c new file mode 100644 index 000000000..5312de7af --- /dev/null +++ b/modules/sb/fsw/src/cfe_sb_init.c @@ -0,0 +1,166 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/****************************************************************************** +** File: cfe_sb_init.c +** +** Purpose: +** This file contains the source code for the SB Initialization. +** +** Author: R.McGraw/SSI +** +******************************************************************************/ + +/* +** Include Files +*/ + +#include "cfe_sb_module_all.h" + +#include + +/* +** External Declarations +*/ + +const size_t CFE_SB_MemPoolDefSize[CFE_PLATFORM_ES_POOL_MAX_BUCKETS] = { + CFE_PLATFORM_SB_MAX_BLOCK_SIZE, CFE_PLATFORM_SB_MEM_BLOCK_SIZE_16, CFE_PLATFORM_SB_MEM_BLOCK_SIZE_15, + CFE_PLATFORM_SB_MEM_BLOCK_SIZE_14, CFE_PLATFORM_SB_MEM_BLOCK_SIZE_13, CFE_PLATFORM_SB_MEM_BLOCK_SIZE_12, + CFE_PLATFORM_SB_MEM_BLOCK_SIZE_11, CFE_PLATFORM_SB_MEM_BLOCK_SIZE_10, CFE_PLATFORM_SB_MEM_BLOCK_SIZE_09, + CFE_PLATFORM_SB_MEM_BLOCK_SIZE_08, CFE_PLATFORM_SB_MEM_BLOCK_SIZE_07, CFE_PLATFORM_SB_MEM_BLOCK_SIZE_06, + CFE_PLATFORM_SB_MEM_BLOCK_SIZE_05, CFE_PLATFORM_SB_MEM_BLOCK_SIZE_04, CFE_PLATFORM_SB_MEM_BLOCK_SIZE_03, + CFE_PLATFORM_SB_MEM_BLOCK_SIZE_02, CFE_PLATFORM_SB_MEM_BLOCK_SIZE_01}; + +/****************************************************************************** +** Function: CFE_SB_EarlyInit() +** +** Purpose: +** Initialize the Software Bus routing tables. +** +** Arguments: +** +** Notes: +** This function MUST be called before any SB API's are called. +** +** Return: +** CFE_SUCCESS +*/ +int32 CFE_SB_EarlyInit(void) +{ + + int32 Stat; + + /* Clear task global */ + memset(&CFE_SB_Global, 0, sizeof(CFE_SB_Global)); + + Stat = OS_MutSemCreate(&CFE_SB_Global.SharedDataMutexId, "CFE_SB_DataMutex", 0); + if (Stat != OS_SUCCESS) + { + CFE_ES_WriteToSysLog("SB shared data mutex creation failed! RC=0x%08x\n", (unsigned int)Stat); + return Stat; + } /* end if */ + + /* Initialize the state of susbcription reporting */ + CFE_SB_Global.SubscriptionReporting = CFE_SB_DISABLE; + + /* Initialize memory partition. */ + Stat = CFE_SB_InitBuffers(); + if (Stat != CFE_SUCCESS) + { + /* error reported in CFE_SB_InitBuffers */ + return Stat; + } /* end if */ + + /* Initialize the pipe table. */ + CFE_SB_InitPipeTbl(); + + /* Initialize the routing module */ + CFE_SBR_Init(); + + /* Initialize the SB Statistics Pkt */ + CFE_MSG_Init(&CFE_SB_Global.StatTlmMsg.Hdr.Msg, CFE_SB_ValueToMsgId(CFE_SB_STATS_TLM_MID), + sizeof(CFE_SB_Global.StatTlmMsg)); + + return Stat; + +} /* end CFE_SB_EarlyInit */ + +/****************************************************************************** +** Function: CFE_SB_InitBuffers() +** +** Purpose: +** Initialize the Software Bus Buffer Pool. +** +** Arguments: +** +** Notes: +** This function MUST be called before any SB API's are called. +** +** Return: +** none +*/ +int32 CFE_SB_InitBuffers(void) +{ + + int32 Stat = 0; + + Stat = CFE_ES_PoolCreateEx(&CFE_SB_Global.Mem.PoolHdl, CFE_SB_Global.Mem.Partition.Data, + CFE_PLATFORM_SB_BUF_MEMORY_BYTES, CFE_PLATFORM_ES_POOL_MAX_BUCKETS, + &CFE_SB_MemPoolDefSize[0], CFE_ES_NO_MUTEX); + + if (Stat != CFE_SUCCESS) + { + CFE_ES_WriteToSysLog("PoolCreate failed for SB Buffers, gave adr 0x%lx,size %d,stat=0x%x\n", + (unsigned long)CFE_SB_Global.Mem.Partition.Data, CFE_PLATFORM_SB_BUF_MEMORY_BYTES, + (unsigned int)Stat); + return Stat; + } + + /* + * Initialize the buffer tracking lists to be empty + */ + CFE_SB_TrackingListReset(&CFE_SB_Global.InTransitList); + CFE_SB_TrackingListReset(&CFE_SB_Global.ZeroCopyList); + + return CFE_SUCCESS; + +} /* end CFE_SB_InitBuffers */ + +/****************************************************************************** +** Function: CFE_SB_InitPipeTbl() +** +** Purpose: +** Initialize the Software Bus Pipe Table. +** +** Arguments: +** +** Notes: +** This function MUST be called before any SB API's are called. +** +** Return: +** none +*/ +void CFE_SB_InitPipeTbl(void) +{ + CFE_SB_Global.LastPipeId = CFE_ResourceId_FromInteger(CFE_SB_PIPEID_BASE); + +} /* end CFE_SB_InitPipeTbl */ + +/*****************************************************************************/ diff --git a/modules/sb/fsw/src/cfe_sb_module_all.h b/modules/sb/fsw/src/cfe_sb_module_all.h new file mode 100644 index 000000000..f54ecb67c --- /dev/null +++ b/modules/sb/fsw/src/cfe_sb_module_all.h @@ -0,0 +1,51 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/** + * @file + * + * Encapsulates all SB module internal header files, as well + * as the public API from all other CFE core modules, OSAL, and PSP. + * + * This simplifies the set of include files that need to be put at the + * start of every source file. + */ + +#ifndef CFE_SB_MODULE_ALL_H +#define CFE_SB_MODULE_ALL_H + +/* +** Includes +*/ +#include "cfe.h" +#include "cfe_platform_cfg.h" +#include "cfe_msgids.h" +#include "cfe_perfids.h" + +#include "cfe_sb_core_internal.h" + +#include "cfe_sb_priv.h" +#include "cfe_sb_events.h" +#include "cfe_sb_destination_typedef.h" +#include "cfe_sb_msg.h" +#include "cfe_sbr.h" +#include "cfe_core_resourceid_basevalues.h" + +#endif /* CFE_SB_MODULE_ALL_H */ diff --git a/modules/sb/fsw/src/cfe_sb_msg_id_util.c b/modules/sb/fsw/src/cfe_sb_msg_id_util.c new file mode 100644 index 000000000..9924a7a04 --- /dev/null +++ b/modules/sb/fsw/src/cfe_sb_msg_id_util.c @@ -0,0 +1,38 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/****************************************************************************** +** File: cfe_sb_msg_id_util.c +** Purpose: message ID utility functions +*/ + +/* +** Include Files +*/ +#include "cfe_sb_module_all.h" + +/* + * Function: CFE_SB_IsValidMsgId - See API and header file for details + */ +bool CFE_SB_IsValidMsgId(CFE_SB_MsgId_t MsgId) +{ + return (!CFE_SB_MsgId_Equal(MsgId, CFE_SB_INVALID_MSG_ID) && + CFE_SB_MsgIdToValue(MsgId) <= CFE_PLATFORM_SB_HIGHEST_VALID_MSGID); +} /* end CFE_SB_IsValidMsgId */ diff --git a/modules/sb/fsw/src/cfe_sb_priv.c b/modules/sb/fsw/src/cfe_sb_priv.c new file mode 100644 index 000000000..a7c929a20 --- /dev/null +++ b/modules/sb/fsw/src/cfe_sb_priv.c @@ -0,0 +1,562 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/****************************************************************************** +** File: cfe_sb_priv.c +** +** Purpose: +** This header file contains prototypes for private functions and type +** definitions for cFE internal use. +** +** Author: R.McGraw/SSI +** +** Notes: + +** The following 4 terms have been or are used in the cFS architecture and implementation +** +** StreamId - First 16 bits of CCSDS Space Packet Protocol (SPP) 133.0-B.1c2 Blue Book +** packet primary header. It contains the 3 bit Version Number, 1 bit Packet Type ID, +** 1 bit Secondary Header flag, and 11 bit Application Process ID +** It was used in earlier cFS implementaions and is defined here for historical reference +** It is NOT exposed to user applications. +** +** MsgId - Unique numeric message identifier within a mission namespace. It is used by cFS +** applications to the identify messages for publishing and subscribing +** It is used by the SB API and encoded in a mission defended way in the header of +** all cFS messages. +** It is exposed to all cFS applications +** +** ApId - CCSDS Application Process Id field in the primary header. +** It has default bit mask of 0x07FF and is part of the cFS message Id +** It should not be confused with the cFE Executive Services (ES) term appId which +** identifies the software application/component +** It is NOT exposed to user applications. +** +** MsgIdkey - This is a unique numeric key within a mission namespace that is used with +** cFS software bus internal structures. +** It is algorithmically created in a mission defined way from the MsgId to support +** efficient lookup and mapping implementations +** It is NOT exposed to user applications. +** +** Some functions have EXTERNAL SYNC REQUIREMENTS +** +** SB functions marked with "Unsync" in their name are designated +** as functions which are _not_ safe to be called concurrently by multiple +** threads, and also do _not_ implement any locking or protection. These +** functions expect the caller to perform all thread synchronization before +** calling it. +** +** The synchronization requirement is across all functions; i.e. it is not safe +** to call B_Unsync() while A_Unsync() is executing or vice-versa. The external +** lock must wait until A_Unsync() finishes before calling B_Unsync(). +** +** The expectation is that the required level of synchronization can be achieved +** using the SB shared data lock. +** +******************************************************************************/ + +/* +** Include Files +*/ + +#include "cfe_sb_module_all.h" + +#include + +/****************************************************************************** +** Function: CFE_SB_CleanUpApp() +** +** Purpose: +** +** Arguments: +** +** Return: +** None +*/ +int32 CFE_SB_CleanUpApp(CFE_ES_AppId_t AppId) +{ + uint32 i; + uint32 DelCount; + CFE_SB_PipeD_t *PipeDscPtr; + CFE_SB_PipeId_t DelList[CFE_PLATFORM_SB_MAX_PIPES]; + + PipeDscPtr = CFE_SB_Global.PipeTbl; + DelCount = 0; + + CFE_SB_LockSharedData(__func__, __LINE__); + + /* loop through the pipe table looking for pipes owned by AppId */ + for (i = 0; i < CFE_PLATFORM_SB_MAX_PIPES; ++i) + { + if (CFE_SB_PipeDescIsUsed(PipeDscPtr) && CFE_RESOURCEID_TEST_EQUAL(PipeDscPtr->AppId, AppId)) + { + DelList[DelCount] = CFE_SB_PipeDescGetID(PipeDscPtr); + ++DelCount; + } + ++PipeDscPtr; + } + + CFE_SB_UnlockSharedData(__func__, __LINE__); + + for (i = 0; i < DelCount; ++i) + { + CFE_SB_DeletePipeWithAppId(DelList[i], AppId); + } + + /* Release any zero copy buffers */ + CFE_SB_ZeroCopyReleaseAppId(AppId); + + return CFE_SUCCESS; + +} /* end CFE_SB_CleanUpApp */ + +/****************************************************************************** +** Function: CFE_SB_LockSharedData() +** +** Purpose: +** SB internal function to handle a semaphore take failure for the Shared +** Data Mutex +** +** Arguments: +** FuncName - the function name containing the code that generated the error. +** LineNumber - the line number in the file of the code that generated the error. +** +** Return: +** None +*/ +void CFE_SB_LockSharedData(const char *FuncName, int32 LineNumber) +{ + + int32 Status; + CFE_ES_AppId_t AppId; + + Status = OS_MutSemTake(CFE_SB_Global.SharedDataMutexId); + if (Status != OS_SUCCESS) + { + + CFE_ES_GetAppID(&AppId); + + CFE_ES_WriteToSysLog("SB SharedData Mutex Take Err Stat=0x%x,App=%lu,Func=%s,Line=%d\n", (unsigned int)Status, + CFE_RESOURCEID_TO_ULONG(AppId), FuncName, (int)LineNumber); + + } /* end if */ + + return; + +} /* end CFE_SB_LockSharedData */ + +/****************************************************************************** +** Function: CFE_SB_UnlockSharedData() +** +** Purpose: +** SB internal function to handle a semaphore give failure for the Shared +** Data Mutex +** +** Arguments: +** FuncName - the function name containing the code that generated the error. +** LineNumber - the line number in the file of the code that generated the error. +** +** Return: +** None +*/ +void CFE_SB_UnlockSharedData(const char *FuncName, int32 LineNumber) +{ + + int32 Status; + CFE_ES_AppId_t AppId; + + Status = OS_MutSemGive(CFE_SB_Global.SharedDataMutexId); + if (Status != OS_SUCCESS) + { + + CFE_ES_GetAppID(&AppId); + + CFE_ES_WriteToSysLog("SB SharedData Mutex Give Err Stat=0x%x,App=%lu,Func=%s,Line=%d\n", (unsigned int)Status, + CFE_RESOURCEID_TO_ULONG(AppId), FuncName, (int)LineNumber); + + } /* end if */ + + return; + +} /* end CFE_SB_UnlockSharedData */ + +/****************************************************************************** + * SB private function to get destination pointer - see description in header + */ +CFE_SB_DestinationD_t *CFE_SB_GetDestPtr(CFE_SBR_RouteId_t RouteId, CFE_SB_PipeId_t PipeId) +{ + CFE_SB_DestinationD_t *destptr; + + destptr = CFE_SBR_GetDestListHeadPtr(RouteId); + + /* Check all destinations */ + while (destptr != NULL) + { + if (CFE_RESOURCEID_TEST_EQUAL(destptr->PipeId, PipeId)) + { + break; + } + destptr = destptr->Next; + } + + return destptr; +} + +/****************************************************************************** +** Function: CFE_SB_ValidateMsgId() +** +** Purpose: +** SB internal function to validate a given MsgId. +** +** Arguments: +** +** Return: +** None +*/ +int32 CFE_SB_ValidateMsgId(CFE_SB_MsgId_t MsgId) +{ + + if (!CFE_SB_IsValidMsgId(MsgId)) + { + return CFE_SB_FAILED; + } + else + { + return CFE_SUCCESS; + } /* end if */ + +} /* end CFE_SB_ValidateMsgId */ + +/*********************************************************************/ +/* + * CFE_SB_LocatePipeDescByID + * + * For complete API information, see prototype in header + */ +CFE_SB_PipeD_t *CFE_SB_LocatePipeDescByID(CFE_SB_PipeId_t PipeId) +{ + CFE_SB_PipeD_t *PipeDscPtr; + uint32 Idx; + + if (CFE_SB_PipeId_ToIndex(PipeId, &Idx) == CFE_SUCCESS) + { + PipeDscPtr = &CFE_SB_Global.PipeTbl[Idx]; + } + else + { + PipeDscPtr = NULL; + } + + return PipeDscPtr; +} + +/*********************************************************************/ +/* + * CFE_SB_CheckPipeDescSlotUsed + * + * Checks if a table slot is used or not (helper for allocating IDs) + */ +bool CFE_SB_CheckPipeDescSlotUsed(CFE_ResourceId_t CheckId) +{ + CFE_SB_PipeD_t *PipeDscPtr; + /* + * Note - The pointer here should never be NULL because the ID should always be + * within the expected range, but if it ever is NULL, this should return true + * such that the caller will _not_ attempt to use the record. + */ + PipeDscPtr = CFE_SB_LocatePipeDescByID(CFE_SB_PIPEID_C(CheckId)); + return (PipeDscPtr == NULL || CFE_SB_PipeDescIsUsed(PipeDscPtr)); +} + +/****************************************************************************** +** Function: CFE_SB_GetAppTskName() +** +** Purpose: +** This function returns a pointer to the app.tsk name string +** +** Arguments: +** TaskId - the task id of the app.task name desired +** FullName - string buffer to store name +** +** Return: +** Pointer to App.Tsk Name +** +** Note: With taskId, Parent App name and Child Task name can be queried from ES +** +*/ +char *CFE_SB_GetAppTskName(CFE_ES_TaskId_t TaskId, char *FullName) +{ + + CFE_ES_TaskInfo_t TaskInfo; + CFE_ES_TaskInfo_t *ptr = &TaskInfo; + char AppName[OS_MAX_API_NAME]; + char TskName[OS_MAX_API_NAME]; + + if (CFE_ES_GetTaskInfo(ptr, TaskId) != CFE_SUCCESS) + { + + /* unlikely, but possible if TaskId is bogus */ + strncpy(FullName, "Unknown", OS_MAX_API_NAME - 1); + FullName[OS_MAX_API_NAME - 1] = '\0'; + } + else if (strncmp((char *)ptr->AppName, (char *)ptr->TaskName, sizeof(ptr->AppName)) == 0) + { + + /* if app name and task name are the same */ + strncpy(FullName, (char *)ptr->AppName, OS_MAX_API_NAME - 1); + FullName[OS_MAX_API_NAME - 1] = '\0'; + } + else + { + + /* AppName and TskName buffers and strncpy are needed to limit string sizes */ + strncpy(AppName, (char *)ptr->AppName, sizeof(AppName) - 1); + AppName[sizeof(AppName) - 1] = '\0'; + strncpy(TskName, (char *)ptr->TaskName, sizeof(TskName) - 1); + TskName[sizeof(TskName) - 1] = '\0'; + + sprintf(FullName, "%s.%s", AppName, TskName); + + } /* end if */ + + return FullName; + +} /* end CFE_SB_GetAppTskName */ + +/****************************************************************************** +** Function: CFE_SB_RequestToSendEvent() +** +** Purpose: +** This function will test the given bit for the given task. If the bit is set +** this function will return CFE_SB_DENIED. If bit is not set, this function set +** the bit and return CFE_SB_GRANTED. This will prevent recursive events from +** occurring. +** +** Arguments: +** +** Return: +** If the bit is set this function will return CFE_SB_DENIED. +** If bit is not set, this function set the bit and return CFE_SB_GRANTED. +*/ +uint32 CFE_SB_RequestToSendEvent(CFE_ES_TaskId_t TaskId, uint32 Bit) +{ + + uint32 Indx; + + if (CFE_ES_TaskID_ToIndex(TaskId, &Indx) != CFE_SUCCESS) + { + return CFE_SB_DENIED; + } + + /* if bit is set... */ + if (CFE_TST(CFE_SB_Global.StopRecurseFlags[Indx], Bit)) + { + + return CFE_SB_DENIED; + } + else + { + + CFE_SET(CFE_SB_Global.StopRecurseFlags[Indx], Bit); + return CFE_SB_GRANTED; + + } /* end if */ + +} /* end CFE_SB_RequestToSendEvent */ + +/****************************************************************************** +** Function: CFE_SB_FinishSendEvent() +** +** Purpose: +** This function will clear the given bit for the given task. Called after +** a successful CFE_SB_RequestToSendEvent() +** +** Arguments: +** +** Return: +** If the bit is set this function will return CFE_SB_DENIED. +** If bit is not set, this function set the bit and return CFE_SB_GRANTED. +*/ +void CFE_SB_FinishSendEvent(CFE_ES_TaskId_t TaskId, uint32 Bit) +{ + + uint32 Indx; + + if (CFE_ES_TaskID_ToIndex(TaskId, &Indx) != CFE_SUCCESS) + { + return; + } + + /* clear the bit so the task may send this event again */ + CFE_CLR(CFE_SB_Global.StopRecurseFlags[Indx], Bit); +} /* end CFE_SB_RequestToSendEvent */ + +/****************************************************************************** + * SB private function to add a destination node - see description in header + */ +int32 CFE_SB_AddDestNode(CFE_SBR_RouteId_t RouteId, CFE_SB_DestinationD_t *NewNode) +{ + + CFE_SB_DestinationD_t *WBS; /* Will Be Second (WBS) node */ + CFE_SB_DestinationD_t *listheadptr; + + listheadptr = CFE_SBR_GetDestListHeadPtr(RouteId); + + /* if first node in list */ + if (listheadptr == NULL) + { + /* initialize the new node */ + NewNode->Next = NULL; + NewNode->Prev = NULL; + } + else + { + WBS = listheadptr; + + /* initialize the new node */ + NewNode->Next = WBS; + NewNode->Prev = NULL; + + /* insert the new node */ + WBS->Prev = NewNode; + } + + /* Update Head */ + CFE_SBR_SetDestListHeadPtr(RouteId, NewNode); + + return CFE_SUCCESS; +} + +/****************************************************************************** + * SB private function to remove a destination - see description in header + */ +void CFE_SB_RemoveDest(CFE_SBR_RouteId_t RouteId, CFE_SB_DestinationD_t *DestPtr) +{ + CFE_SB_RemoveDestNode(RouteId, DestPtr); + CFE_SB_PutDestinationBlk(DestPtr); + CFE_SB_Global.StatTlmMsg.Payload.SubscriptionsInUse--; +} + +/****************************************************************************** + * SB private function to remove a destination node - see description in header + */ +void CFE_SB_RemoveDestNode(CFE_SBR_RouteId_t RouteId, CFE_SB_DestinationD_t *NodeToRemove) +{ + CFE_SB_DestinationD_t *PrevNode; + CFE_SB_DestinationD_t *NextNode; + + if ((NodeToRemove->Prev == NULL) && (NodeToRemove->Next == NULL)) + { + /* Clear destinations if this is the only node in the list */ + CFE_SBR_SetDestListHeadPtr(RouteId, NULL); + } + else if (NodeToRemove->Prev == NULL) + { + /* First in the list, set the next node to list head */ + NextNode = NodeToRemove->Next; + NextNode->Prev = NULL; + CFE_SBR_SetDestListHeadPtr(RouteId, NextNode); + } + else if (NodeToRemove->Next == NULL) + { + + /* Last in the list, remove previous pointer */ + PrevNode = NodeToRemove->Prev; + PrevNode->Next = NULL; + } + else + { + /* Middle of list, remove */ + PrevNode = NodeToRemove->Prev; + NextNode = NodeToRemove->Next; + PrevNode->Next = NextNode; + NextNode->Prev = PrevNode; + } + + /* initialize the node before returning it to the heap */ + NodeToRemove->Next = NULL; + NodeToRemove->Prev = NULL; +} + +/****************************************************************************** +** Name: CFE_SB_ZeroCopyReleaseAppId +** +** Purpose: API used for releasing all pointers to a buffers (for zero copy mode +** only) for a specific Application. This function is used for cleaning +** up when an application crashes. +** +** Assumptions, External Events, and Notes: +** None +** +** Date Written: +** 07/23/2009 +** +** Input Arguments: +** AppId +** +** Output Arguments: +** None +** +** Return Values: +** Status +** +******************************************************************************/ +int32 CFE_SB_ZeroCopyReleaseAppId(CFE_ES_AppId_t AppId) +{ + CFE_SB_BufferLink_t *NextLink; + CFE_SB_BufferD_t * DscPtr; + + /* + * First go through the "ZeroCopy" tracking list and find all nodes + * with a matching AppID. This needs to be done while locked to + * prevent other tasks from changing the list at the same time. + */ + if (CFE_RESOURCEID_TEST_DEFINED(AppId)) + { + CFE_SB_LockSharedData(__func__, __LINE__); + + /* Get start of list */ + NextLink = CFE_SB_TrackingListGetNext(&CFE_SB_Global.ZeroCopyList); + while (!CFE_SB_TrackingListIsEnd(&CFE_SB_Global.ZeroCopyList, NextLink)) + { + /* Get buffer descriptor pointer */ + /* NOTE: casting via void* here rather than CFE_SB_BufferD_t* avoids a false + * alignment warning on platforms with strict alignment requirements */ + DscPtr = (void *)NextLink; + + /* Read the next link now in case this node gets moved */ + NextLink = CFE_SB_TrackingListGetNext(NextLink); + + /* Check if it is a zero-copy buffer owned by this app */ + if (CFE_RESOURCEID_TEST_EQUAL(DscPtr->AppId, AppId)) + { + /* If so, decrement the use count as the app has now gone away */ + CFE_SB_DecrBufUseCnt(DscPtr); + } + } + + CFE_SB_UnlockSharedData(__func__, __LINE__); + } + + return CFE_SUCCESS; + +} /* end CFE_SB_ZeroCopyReleaseAppId */ + +/*****************************************************************************/ diff --git a/modules/sb/fsw/src/cfe_sb_priv.h b/modules/sb/fsw/src/cfe_sb_priv.h new file mode 100644 index 000000000..a607853fb --- /dev/null +++ b/modules/sb/fsw/src/cfe_sb_priv.h @@ -0,0 +1,635 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/** + * @file + * + * Purpose: + * This header file contains prototypes for private functions and type + * definitions for SB internal use. + * + * Author: R.McGraw/SSI + * + */ + +#ifndef CFE_SB_PRIV_H +#define CFE_SB_PRIV_H + +/* +** Includes +*/ +#include "cfe_platform_cfg.h" +#include "common_types.h" +#include "cfe_sb_api_typedefs.h" +#include "cfe_es_api_typedefs.h" +#include "cfe_sbr_api_typedefs.h" +#include "cfe_msg_api_typedefs.h" +#include "cfe_fs_api_typedefs.h" +#include "cfe_resourceid_api_typedefs.h" +#include "cfe_sb_destination_typedef.h" +#include "cfe_sb_msg.h" + +/* +** Macro Definitions +*/ + +#define CFE_SB_UNUSED_QUEUE OS_OBJECT_ID_UNDEFINED +#define CFE_SB_NO_DESTINATION 0xFF +#define CFE_SB_FAILED 1 +#define SB_DONT_CARE 0 + +#define CFE_SB_NO_DUPLICATE 0 +#define CFE_SB_DUPLICATE 1 + +#define CFE_SB_INACTIVE 0 +#define CFE_SB_ACTIVE 1 + +#define CFE_SB_MSG_GLOBAL 0 +#define CFE_SB_MSG_LOCAL 1 + +#define CFE_SB_SEND_ZEROCOPY 0 +#define CFE_SB_SEND_ONECOPY 1 + +#define CFE_SB_NOT_IN_USE 0 +#define CFE_SB_IN_USE 1 + +#define CFE_SB_DISABLE 0 +#define CFE_SB_ENABLE 1 + +#define CFE_SB_DENIED 0 +#define CFE_SB_GRANTED 1 + +#define CFE_SB_DO_NOT_INCREMENT 0 +#define CFE_SB_INCREMENT_TLM 1 + +#define CFE_SB_MAIN_LOOP_ERR_DLY 1000 +#define CFE_SB_CMD_PIPE_DEPTH 32 +#define CFE_SB_CMD_PIPE_NAME "SB_CMD_PIPE" +#define CFE_SB_MAX_CFG_FILE_EVENTS_TO_FILTER 8 + +#define CFE_SB_PIPE_OVERFLOW (-1) +#define CFE_SB_PIPE_WR_ERR (-2) +#define CFE_SB_USECNT_ERR (-3) +#define CFE_SB_FILE_IO_ERR (-5) + +/* bit map for stopping recursive event problem */ +#define CFE_SB_SEND_NO_SUBS_EID_BIT 0 +#define CFE_SB_GET_BUF_ERR_EID_BIT 1 +#define CFE_SB_MSGID_LIM_ERR_EID_BIT 2 +#define CFE_SB_Q_FULL_ERR_EID_BIT 3 +#define CFE_SB_Q_WR_ERR_EID_BIT 4 + +/* +** Type Definitions +*/ + +/** + * \brief Basic linked list structure allowing all buffer descriptors to be tracked. + */ +typedef struct CFE_SB_BufferLink +{ + struct CFE_SB_BufferLink *Next; + struct CFE_SB_BufferLink *Prev; + +} CFE_SB_BufferLink_t; + +/****************************************************************************** +** Typedef: CFE_SB_BufferD_t +** +** Purpose: +** This structure defines a BUFFER DESCRIPTOR used to specify the MsgId +** and address of each packet buffer. +** +** Note: Changing the size of this structure may require the memory pool +** block sizes to change. +*/ +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 is set for buffers which contain actual data 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; + + /** + * Current owner of the buffer, if owned by a single app. + * + * This is used to track "zero copy" buffer allocations - this will be set to + * the AppID that initally allocated it, before it is used to transmit a message. + * + * When the message is in transit, it may be queued to multiple applictions, + * so this is unset. + */ + 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 */ + + bool AutoSequence; /**< If message should get its sequence number assigned from the route */ + + uint16 UseCount; /**< Number of active references to this buffer in the system */ + + CFE_SB_Buffer_t Content; /* Variably sized content field, Keep last */ + +} CFE_SB_BufferD_t; + +/****************************************************************************** +** Typedef: CFE_SB_PipeD_t +** +** Purpose: +** This structure defines a pipe descriptor used to specify the +** characteristics and status of a pipe. +*/ + +typedef struct +{ + CFE_SB_PipeId_t PipeId; + uint8 Opts; + uint8 Spare; + CFE_ES_AppId_t AppId; + osal_id_t SysQueueId; + uint16 SendErrors; + uint16 MaxQueueDepth; + uint16 CurrentQueueDepth; + uint16 PeakQueueDepth; + CFE_SB_BufferD_t *LastBuffer; +} CFE_SB_PipeD_t; + +/****************************************************************************** +** Typedef: CFE_SB_BufParams_t +** +** Purpose: +** This structure defines the variables related to the SB routing buffers. +*/ +typedef struct +{ + + CFE_ES_MemHandle_t PoolHdl; + CFE_ES_STATIC_POOL_TYPE(CFE_PLATFORM_SB_BUF_MEMORY_BYTES) Partition; + +} CFE_SB_MemParams_t; + +/*******************************************************************************/ +/** +** \brief SB route info temporary structure +** +** This tracks the number of desinations along with destination data for 1 route. +** Each route may contain zero or more desinations (variable length). +*/ +typedef struct +{ + uint32 NumDestinations; + CFE_SB_RoutingFileEntry_t DestEntries[CFE_PLATFORM_SB_MAX_DEST_PER_PKT]; /**< Actual data written to file */ +} CFE_SB_BackgroundRouteInfoBuffer_t; + +/** + * \brief Temporary holding buffer for records being written to a file. + * + * This is shared/reused between all file types (msg map, route info, pipe info). + */ +typedef union +{ + CFE_SB_BackgroundRouteInfoBuffer_t RouteInfo; + CFE_SB_PipeInfoEntry_t PipeInfo; + CFE_SB_MsgMapFileEntry_t MsgMapInfo; +} CFE_SB_BackgroundFileBuffer_t; + +/** + * \brief SB Background file write state information + * + * Must be stored in persistent memory (e.g. global). + */ +typedef struct +{ + CFE_FS_FileWriteMetaData_t FileWrite; /**< FS state data - must be first */ + CFE_SB_BackgroundFileBuffer_t Buffer; /**< Temporary holding area for file record */ +} CFE_SB_BackgroundFileStateInfo_t; + +/****************************************************************************** +** Typedef: CFE_SB_Global_t +** +** Purpose: +** This structure contains the SB global variables. +*/ +typedef struct +{ + osal_id_t SharedDataMutexId; + uint32 SubscriptionReporting; + CFE_ES_AppId_t AppId; + uint32 StopRecurseFlags[OS_MAX_TASKS]; + CFE_SB_PipeD_t PipeTbl[CFE_PLATFORM_SB_MAX_PIPES]; + CFE_SB_HousekeepingTlm_t HKTlmMsg; + CFE_SB_StatsTlm_t StatTlmMsg; + CFE_SB_PipeId_t CmdPipe; + CFE_SB_MemParams_t Mem; + CFE_SB_AllSubscriptionsTlm_t PrevSubMsg; + CFE_EVS_BinFilter_t EventFilters[CFE_SB_MAX_CFG_FILE_EVENTS_TO_FILTER]; + CFE_SB_Qos_t Default_Qos; + CFE_ResourceId_t LastPipeId; + + CFE_SB_BackgroundFileStateInfo_t BackgroundFile; + + /* A list of buffers currently in-transit, owned by SB */ + CFE_SB_BufferLink_t InTransitList; + + /* A list of buffers currently issued to apps for zero-copy */ + CFE_SB_BufferLink_t ZeroCopyList; + +} CFE_SB_Global_t; + +/****************************************************************************** +** Typedef: CFE_SB_SendErrEventBuf_t +** +** Purpose: +** This structure is used to store event information during a send. +*/ +typedef struct +{ + uint32 EventId; + int32 ErrStat; + CFE_SB_PipeId_t PipeId; +} CFE_SB_SendErrEventBuf_t; + +/****************************************************************************** +** Typedef: CFE_SB_EventBuf_t +** +** Purpose: +** This structure is used to store event information during a send. +*/ +typedef struct +{ + uint32 EvtsToSnd; + CFE_SB_SendErrEventBuf_t EvtBuf[CFE_PLATFORM_SB_MAX_DEST_PER_PKT]; +} CFE_SB_EventBuf_t; + +/* +** Software Bus Function Prototypes +*/ + +int32 CFE_SB_AppInit(void); +int32 CFE_SB_InitBuffers(void); +void CFE_SB_InitPipeTbl(void); +void CFE_SB_InitIdxStack(void); +void CFE_SB_ResetCounts(void); +void CFE_SB_LockSharedData(const char *FuncName, int32 LineNumber); +void CFE_SB_UnlockSharedData(const char *FuncName, int32 LineNumber); +void CFE_SB_ReleaseBuffer(CFE_SB_BufferD_t *bd, CFE_SB_DestinationD_t *dest); +int32 CFE_SB_WriteQueue(CFE_SB_PipeD_t *pd, uint32 TskId, const CFE_SB_BufferD_t *bd, CFE_SB_MsgId_t MsgId); +void CFE_SB_ProcessCmdPipePkt(CFE_SB_Buffer_t *SBBufPtr); +void CFE_SB_ResetCounters(void); +void CFE_SB_SetMsgSeqCnt(CFE_MSG_Message_t *MsgPtr, uint32 Count); +char *CFE_SB_GetAppTskName(CFE_ES_TaskId_t TaskId, char *FullName); +int32 CFE_SB_DeletePipeWithAppId(CFE_SB_PipeId_t PipeId, CFE_ES_AppId_t AppId); +int32 CFE_SB_DeletePipeFull(CFE_SB_PipeId_t PipeId, CFE_ES_AppId_t AppId); +int32 CFE_SB_SubscribeFull(CFE_SB_MsgId_t MsgId, CFE_SB_PipeId_t PipeId, CFE_SB_Qos_t Quality, uint16 MsgLim, + uint8 Scope); + +int32 CFE_SB_UnsubscribeWithAppId(CFE_SB_MsgId_t MsgId, CFE_SB_PipeId_t PipeId, CFE_ES_AppId_t AppId); + +int32 CFE_SB_UnsubscribeFull(CFE_SB_MsgId_t MsgId, CFE_SB_PipeId_t PipeId, uint8 Scope, CFE_ES_AppId_t AppId); +int32 CFE_SB_TransmitMsgValidate(CFE_MSG_Message_t *MsgPtr, CFE_SB_MsgId_t *MsgIdPtr, CFE_MSG_Size_t *SizePtr, + CFE_SBR_RouteId_t *RouteIdPtr); +int32 CFE_SB_ZeroCopyReleaseAppId(CFE_ES_AppId_t AppId); +void CFE_SB_IncrBufUseCnt(CFE_SB_BufferD_t *bd); +void CFE_SB_DecrBufUseCnt(CFE_SB_BufferD_t *bd); +int32 CFE_SB_ValidateMsgId(CFE_SB_MsgId_t MsgId); +int32 CFE_SB_ValidatePipeId(CFE_SB_PipeId_t PipeId); +void CFE_SB_IncrCmdCtr(int32 status); +void CFE_SB_SetSubscriptionReporting(uint32 state); +int32 CFE_SB_SendSubscriptionReport(CFE_SB_MsgId_t MsgId, CFE_SB_PipeId_t PipeId, CFE_SB_Qos_t Quality); +uint32 CFE_SB_RequestToSendEvent(CFE_ES_TaskId_t TaskId, uint32 Bit); +void CFE_SB_FinishSendEvent(CFE_ES_TaskId_t TaskId, uint32 Bit); +CFE_SB_DestinationD_t *CFE_SB_GetDestinationBlk(void); +int32 CFE_SB_PutDestinationBlk(CFE_SB_DestinationD_t *Dest); + +/** + * \brief For SB buffer tracking, get first/next position in a list + */ +static inline CFE_SB_BufferLink_t *CFE_SB_TrackingListGetNext(CFE_SB_BufferLink_t *Node) +{ + return Node->Next; +} + +/** + * \brief For SB buffer tracking, checks if this current position represents the end of the list + */ +static inline bool CFE_SB_TrackingListIsEnd(CFE_SB_BufferLink_t *List, CFE_SB_BufferLink_t *Node) +{ + /* Normally list nodes should never have NULL, buf if they do, do not follow it */ + return (Node == NULL || Node == List); +} + +/** + * \brief For SB buffer tracking, reset link state to default + * + * This turns the node into a singleton/lone object (not in a list) + * or resets the head link to be empty. + */ +void CFE_SB_TrackingListReset(CFE_SB_BufferLink_t *Link); + +/** + * \brief For SB buffer tracking, removes a node from a tracking list + * + * Extracts a single node from whatever list it is in. After this the + * node becomes a singleton owned by the caller. It may be put into + * another list or freed. + */ +void CFE_SB_TrackingListRemove(CFE_SB_BufferLink_t *Node); + +/** + * \brief For SB buffer tracking, adds a node to a tracking list + * + * Extracts a single node from the list its in. After this the + * node becomes a singleton owned by the caller. It must put it + * in another list or free it. + */ +void CFE_SB_TrackingListAdd(CFE_SB_BufferLink_t *List, CFE_SB_BufferLink_t *Node); + +/** + * \brief Allocates a new buffer descriptor from the SB memory pool. + * + * \param[in] MaxMsgSize Maximum message content size that the buffer must be capable of holding + * \returns Pointer to buffer descriptor, or NULL on failure. + */ +CFE_SB_BufferD_t *CFE_SB_GetBufferFromPool(size_t MaxMsgSize); + +/** + * \brief Returns a buffer to SB memory pool + * + * \param[in] Pointer to descriptor to return + */ +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 + * + * \param[in] BufPtr pointer to the content buffer + * \param[out] BufDscPtr Will be set to actual buffer descriptor + * + * \returns CFE_SUCCESS if validation passed, or error code. + */ +int32 CFE_SB_ZeroCopyBufferValidate(CFE_SB_Buffer_t *BufPtr, CFE_SB_BufferD_t **BufDscPtr); + +/** + * \brief Add a destination node + * + * Private function that will add a destination node to the linked list + * + * \note Assumes destination pointer is valid + * + * \param[in] RouteId The route ID to add destination node to + * \param[in] DestPtr Pointer to the destination to add + */ +int32 CFE_SB_AddDestNode(CFE_SBR_RouteId_t RouteId, CFE_SB_DestinationD_t *NewNode); + +/** + * \brief Remove a destination node + * + * Private function that will remove a destination node from the linked list + * + * \note Assumes destination pointer is valid and in route + * + * \param[in] RouteId The route ID to remove destination node from + * \param[in] DestPtr Pointer to the destination to remove + */ +void CFE_SB_RemoveDestNode(CFE_SBR_RouteId_t RouteId, CFE_SB_DestinationD_t *NodeToRemove); + +/** + * \brief Remove a destination + * + * Private function that will remove a destination by removing the node, + * returning the block, and decrementing counters + * + * \note Assumes destination pointer is valid and in route + * + * \param[in] RouteId The route ID to remove destination from + * \param[in] DestPtr Pointer to the destination to remove + */ +void CFE_SB_RemoveDest(CFE_SBR_RouteId_t RouteId, CFE_SB_DestinationD_t *DestPtr); + +/** + * \brief Get destination pointer for PipeId from RouteId + * + * Private function that will return the destination pointer related to the + * given PipeId and RouteId if it exists + * + * \param[in] RouteId The route ID to search + * \param[in] PipeId The pipe ID to search for + * + * \returns Then destination pointer for a match, NULL otherwise + */ +CFE_SB_DestinationD_t *CFE_SB_GetDestPtr(CFE_SBR_RouteId_t RouteId, CFE_SB_PipeId_t PipeId); + +/*****************************************************************************/ +/** +** \brief Get the size of a message header. +** +** \par Description +** This routine is a best guess of the message header size based off type +** information and the local message implementation. +** If a different header implementation was used to generate the message +** the returned size may not be correct. Critical functionality should +** use the real message structure or otherwise confirm header implementation +** matches expectations prior to using this API. +** +** \par Assumptions, External Events, and Notes: +** - Utilize CFE_MSG_CommandHeader_t and CFE_MSG_TelemetryHeader_t for +** defining message structures. +** +** \param[in] *MsgPtr The message ID to calculate header size for. The size of the message +** header may depend on the MsgId in some implementations. For example, +** if SB messages are implemented as CCSDS packets, the size of the header +** is different for command vs. telemetry packets. +** +** \returns Estimated number of bytes in the message header for the given message +**/ +size_t CFE_SB_MsgHdrSize(const CFE_MSG_Message_t *MsgPtr); + +/* + * Software Bus Message Handler Function prototypes + */ +int32 CFE_SB_NoopCmd(const CFE_SB_NoopCmd_t *data); +int32 CFE_SB_ResetCountersCmd(const CFE_SB_ResetCountersCmd_t *data); +int32 CFE_SB_EnableSubReportingCmd(const CFE_SB_EnableSubReportingCmd_t *data); +int32 CFE_SB_DisableSubReportingCmd(const CFE_SB_DisableSubReportingCmd_t *data); +int32 CFE_SB_SendHKTlmCmd(const CFE_MSG_CommandHeader_t *data); +int32 CFE_SB_EnableRouteCmd(const CFE_SB_EnableRouteCmd_t *data); +int32 CFE_SB_DisableRouteCmd(const CFE_SB_DisableRouteCmd_t *data); +int32 CFE_SB_SendStatsCmd(const CFE_SB_SendSbStatsCmd_t *data); +int32 CFE_SB_WriteRoutingInfoCmd(const CFE_SB_WriteRoutingInfoCmd_t *data); +int32 CFE_SB_WritePipeInfoCmd(const CFE_SB_WritePipeInfoCmd_t *data); +int32 CFE_SB_WriteMapInfoCmd(const CFE_SB_WriteMapInfoCmd_t *data); +int32 CFE_SB_SendPrevSubsCmd(const CFE_SB_SendPrevSubsCmd_t *data); + +/** + * @brief Locate the Pipe table entry correlating with a given Pipe ID. + * + * This only returns a pointer to the table entry and does _not_ + * otherwise check/validate the entry. + * + * @param[in] PipeId the Pipe ID to locate + * @return pointer to Pipe Table entry for the given Pipe ID + */ +extern CFE_SB_PipeD_t *CFE_SB_LocatePipeDescByID(CFE_SB_PipeId_t PipeId); + +/** + * @brief Check if an Pipe descriptor is in use or free/empty + * + * This routine checks if the Pipe table entry is in use or if it is free + * + * As this dereferences fields within the descriptor, global data must be + * locked prior to invoking this function. + * + * @param[in] PipeDscPtr pointer to Pipe table entry + * @returns true if the entry is in use/configured, or false if it is free/empty + */ +static inline bool CFE_SB_PipeDescIsUsed(const CFE_SB_PipeD_t *PipeDscPtr) +{ + return CFE_RESOURCEID_TEST_DEFINED(PipeDscPtr->PipeId); +} + +/** + * @brief Get the ID value from an Pipe table entry + * + * This routine converts the table entry back to an abstract ID. + * + * @param[in] PipeDscPtr pointer to Pipe table entry + * @returns PipeID of entry + */ +static inline CFE_SB_PipeId_t CFE_SB_PipeDescGetID(const CFE_SB_PipeD_t *PipeDscPtr) +{ + return PipeDscPtr->PipeId; +} + +/** + * @brief Marks an Pipe table entry as used (not free) + * + * This sets the internal field(s) within this entry, and marks + * it as being associated with the given Pipe ID. + * + * As this dereferences fields within the descriptor, global data must be + * locked prior to invoking this function. + * + * @param[in] PipeDscPtr pointer to Pipe table entry + * @param[in] PipeID the Pipe ID of this entry + */ +static inline void CFE_SB_PipeDescSetUsed(CFE_SB_PipeD_t *PipeDscPtr, CFE_ResourceId_t PendingID) +{ + PipeDscPtr->PipeId = CFE_SB_PIPEID_C(PendingID); +} + +/** + * @brief Set an Pipe descriptor table entry free (not used) + * + * This clears the internal field(s) within this entry, and allows the + * memory to be re-used in the future. + * + * As this dereferences fields within the descriptor, global data must be + * locked prior to invoking this function. + * + * @param[in] PipeDscPtr pointer to Pipe table entry + */ +static inline void CFE_SB_PipeDescSetFree(CFE_SB_PipeD_t *PipeDscPtr) +{ + PipeDscPtr->PipeId = CFE_SB_INVALID_PIPE; +} + +/** + * @brief Check if an Pipe descriptor is a match for the given PipeID + * + * This routine confirms that the previously-located descriptor is valid + * and matches the expected Pipe ID. + * + * As this dereferences fields within the descriptor, global data must be + * locked prior to invoking this function. + * + * @param[in] PipeDscPtr pointer to Pipe table entry + * @param[in] PipeID expected Pipe ID + * @returns true if the entry matches the given Pipe ID + */ +static inline bool CFE_SB_PipeDescIsMatch(const CFE_SB_PipeD_t *PipeDscPtr, CFE_SB_PipeId_t PipeID) +{ + return (PipeDscPtr != NULL && CFE_RESOURCEID_TEST_EQUAL(PipeDscPtr->PipeId, PipeID)); +} + +/* Availability check functions used in conjunction with CFE_ResourceId_FindNext() */ +bool CFE_SB_CheckPipeDescSlotUsed(CFE_ResourceId_t CheckId); + +/* + * Helper functions for background file write requests (callbacks) + */ +void CFE_SB_CollectMsgMapInfo(CFE_SBR_RouteId_t RouteId, void *ArgPtr); +bool CFE_SB_WriteMsgMapInfoDataGetter(void *Meta, uint32 RecordNum, void **Buffer, size_t *BufSize); +void CFE_SB_CollectRouteInfo(CFE_SBR_RouteId_t RouteId, void *ArgPtr); +bool CFE_SB_WriteRouteInfoDataGetter(void *Meta, uint32 RecordNum, void **Buffer, size_t *BufSize); +bool CFE_SB_WritePipeInfoDataGetter(void *Meta, uint32 RecordNum, void **Buffer, size_t *BufSize); +void CFE_SB_BackgroundFileEventHandler(void *Meta, CFE_FS_FileWriteEvent_t Event, int32 Status, uint32 RecordNum, + size_t BlockSize, size_t Position); + +/* + * External variables private to the software bus module + */ + +extern CFE_SB_Global_t CFE_SB_Global; + +#endif /* CFE_SB_PRIV_H */ diff --git a/modules/sb/fsw/src/cfe_sb_task.c b/modules/sb/fsw/src/cfe_sb_task.c new file mode 100644 index 000000000..d34f186d8 --- /dev/null +++ b/modules/sb/fsw/src/cfe_sb_task.c @@ -0,0 +1,1446 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/****************************************************************************** +** File: cfe_sb_task.c +** +** Purpose: +** This file contains the source code for the SB task. +** +** Author: R.McGraw/SSI +** +******************************************************************************/ + +/* Include Files */ + +#include "cfe_sb_module_all.h" +#include "cfe_version.h" +#include "cfe_es_msg.h" /* needed for local use of CFE_ES_RestartCmd_t */ + +#include + +/* Task Globals */ +CFE_SB_Global_t CFE_SB_Global; + +/* Local structure for file writing callbacks */ +typedef struct +{ + const char *Filename; /* File name for error reporting */ + osal_id_t Fd; /* File id for writing */ + uint32 FileSize; /* File size for reporting */ + uint32 EntryCount; /* Entry count for reporting */ + int32 Status; /* File write status */ +} CFE_SB_FileWriteCallback_t; + +/****************************************************************************** +** Function: CFE_SB_TaskMain() +** +** Purpose: +** Main loop for Software Bus task, used to process SB commands. +** +** Arguments: +** none +** +** Return: +** none +*/ +void CFE_SB_TaskMain(void) +{ + int32 Status; + CFE_SB_Buffer_t *SBBufPtr; + + CFE_ES_PerfLogEntry(CFE_MISSION_SB_MAIN_PERF_ID); + + Status = CFE_SB_AppInit(); + + if (Status != CFE_SUCCESS) + { + CFE_ES_WriteToSysLog("SB:Application Init Failed,RC=0x%08X\n", (unsigned int)Status); + CFE_ES_PerfLogExit(CFE_MISSION_SB_MAIN_PERF_ID); + /* Note: CFE_ES_ExitApp will not return */ + CFE_ES_ExitApp(CFE_ES_RunStatus_CORE_APP_INIT_ERROR); + } /* end if */ + + /* + * Wait for other apps to start. + * It is important that the core apps are present before this starts receiving + * messages from the command pipe, as some of those handlers might depend on + * the other core apps. + */ + CFE_ES_WaitForSystemState(CFE_ES_SystemState_CORE_READY, CFE_PLATFORM_CORE_MAX_STARTUP_MSEC); + + /* Main loop */ + while (Status == CFE_SUCCESS) + { + /* Increment the Main task Execution Counter */ + CFE_ES_IncrementTaskCounter(); + + CFE_ES_PerfLogExit(CFE_MISSION_SB_MAIN_PERF_ID); + + /* Pend on receipt of packet */ + Status = CFE_SB_ReceiveBuffer(&SBBufPtr, CFE_SB_Global.CmdPipe, CFE_SB_PEND_FOREVER); + + CFE_ES_PerfLogEntry(CFE_MISSION_SB_MAIN_PERF_ID); + + if (Status == CFE_SUCCESS) + { + /* Process cmd pipe msg */ + CFE_SB_ProcessCmdPipePkt(SBBufPtr); + } + else + { + CFE_ES_WriteToSysLog("SB:Error reading cmd pipe,RC=0x%08X\n", (unsigned int)Status); + } /* end if */ + + } /* end while */ + + /* while loop exits only if CFE_SB_ReceiveBuffer returns error */ + CFE_ES_ExitApp(CFE_ES_RunStatus_CORE_APP_RUNTIME_ERROR); + +} /* end CFE_SB_TaskMain */ + +/****************************************************************************** +** Function: CFE_SB_AppInit() +** +** Purpose: +** Initialization routine for SB application. This routine is executed when +** the SB application is started by Executive Services. +** +** Arguments: +** none +** +** Return: +** CFE_SUCCESS if no errors, otherwise this function returns the error code +** that was received from the function that detected the error. +** +*/ +int32 CFE_SB_AppInit(void) +{ + + uint32 CfgFileEventsToFilter = 0; + CFE_ES_MemPoolBuf_t TmpPtr; + int32 Status; + + /* Get the assigned Application ID for the SB Task */ + CFE_ES_GetAppID(&CFE_SB_Global.AppId); + + /* Process the platform cfg file events to be filtered */ + if (CFE_PLATFORM_SB_FILTERED_EVENT1 != 0) + { + CFE_SB_Global.EventFilters[CfgFileEventsToFilter].EventID = CFE_PLATFORM_SB_FILTERED_EVENT1; + CFE_SB_Global.EventFilters[CfgFileEventsToFilter].Mask = CFE_PLATFORM_SB_FILTER_MASK1; + CfgFileEventsToFilter++; + } /* end if */ + + if (CFE_PLATFORM_SB_FILTERED_EVENT2 != 0) + { + CFE_SB_Global.EventFilters[CfgFileEventsToFilter].EventID = CFE_PLATFORM_SB_FILTERED_EVENT2; + CFE_SB_Global.EventFilters[CfgFileEventsToFilter].Mask = CFE_PLATFORM_SB_FILTER_MASK2; + CfgFileEventsToFilter++; + } /* end if */ + + if (CFE_PLATFORM_SB_FILTERED_EVENT3 != 0) + { + CFE_SB_Global.EventFilters[CfgFileEventsToFilter].EventID = CFE_PLATFORM_SB_FILTERED_EVENT3; + CFE_SB_Global.EventFilters[CfgFileEventsToFilter].Mask = CFE_PLATFORM_SB_FILTER_MASK3; + CfgFileEventsToFilter++; + } /* end if */ + + if (CFE_PLATFORM_SB_FILTERED_EVENT4 != 0) + { + CFE_SB_Global.EventFilters[CfgFileEventsToFilter].EventID = CFE_PLATFORM_SB_FILTERED_EVENT4; + CFE_SB_Global.EventFilters[CfgFileEventsToFilter].Mask = CFE_PLATFORM_SB_FILTER_MASK4; + CfgFileEventsToFilter++; + } /* end if */ + + if (CFE_PLATFORM_SB_FILTERED_EVENT5 != 0) + { + CFE_SB_Global.EventFilters[CfgFileEventsToFilter].EventID = CFE_PLATFORM_SB_FILTERED_EVENT5; + CFE_SB_Global.EventFilters[CfgFileEventsToFilter].Mask = CFE_PLATFORM_SB_FILTER_MASK5; + CfgFileEventsToFilter++; + } /* end if */ + + if (CFE_PLATFORM_SB_FILTERED_EVENT6 != 0) + { + CFE_SB_Global.EventFilters[CfgFileEventsToFilter].EventID = CFE_PLATFORM_SB_FILTERED_EVENT6; + CFE_SB_Global.EventFilters[CfgFileEventsToFilter].Mask = CFE_PLATFORM_SB_FILTER_MASK6; + CfgFileEventsToFilter++; + } /* end if */ + + if (CFE_PLATFORM_SB_FILTERED_EVENT7 != 0) + { + CFE_SB_Global.EventFilters[CfgFileEventsToFilter].EventID = CFE_PLATFORM_SB_FILTERED_EVENT7; + CFE_SB_Global.EventFilters[CfgFileEventsToFilter].Mask = CFE_PLATFORM_SB_FILTER_MASK7; + CfgFileEventsToFilter++; + } /* end if */ + + if (CFE_PLATFORM_SB_FILTERED_EVENT8 != 0) + { + CFE_SB_Global.EventFilters[CfgFileEventsToFilter].EventID = CFE_PLATFORM_SB_FILTERED_EVENT8; + CFE_SB_Global.EventFilters[CfgFileEventsToFilter].Mask = CFE_PLATFORM_SB_FILTER_MASK8; + CfgFileEventsToFilter++; + } /* end if */ + + /* Be sure the number of events to register for filtering + ** does not exceed CFE_PLATFORM_EVS_MAX_EVENT_FILTERS */ + if (CFE_PLATFORM_EVS_MAX_EVENT_FILTERS < CfgFileEventsToFilter) + { + CfgFileEventsToFilter = CFE_PLATFORM_EVS_MAX_EVENT_FILTERS; + } /* end if */ + + /* Register event filter table... */ + Status = CFE_EVS_Register(CFE_SB_Global.EventFilters, CfgFileEventsToFilter, CFE_EVS_EventFilter_BINARY); + if (Status != CFE_SUCCESS) + { + CFE_ES_WriteToSysLog("SB:Call to CFE_EVS_Register Failed:RC=0x%08X\n", (unsigned int)Status); + return Status; + } /* end if */ + + CFE_ES_WriteToSysLog("SB:Registered %d events for filtering\n", (int)CfgFileEventsToFilter); + + CFE_MSG_Init(&CFE_SB_Global.HKTlmMsg.Hdr.Msg, CFE_SB_ValueToMsgId(CFE_SB_HK_TLM_MID), + sizeof(CFE_SB_Global.HKTlmMsg)); + + CFE_MSG_Init(&CFE_SB_Global.PrevSubMsg.Hdr.Msg, CFE_SB_ValueToMsgId(CFE_SB_ALLSUBS_TLM_MID), + sizeof(CFE_SB_Global.PrevSubMsg)); + + /* Populate the fixed fields in the HK Tlm Msg */ + CFE_SB_Global.HKTlmMsg.Payload.MemPoolHandle = CFE_SB_Global.Mem.PoolHdl; + + /* Populate the fixed fields in the Stat Tlm Msg */ + CFE_SB_Global.StatTlmMsg.Payload.MaxMsgIdsAllowed = CFE_PLATFORM_SB_MAX_MSG_IDS; + CFE_SB_Global.StatTlmMsg.Payload.MaxPipesAllowed = CFE_PLATFORM_SB_MAX_PIPES; + CFE_SB_Global.StatTlmMsg.Payload.MaxMemAllowed = CFE_PLATFORM_SB_BUF_MEMORY_BYTES; + CFE_SB_Global.StatTlmMsg.Payload.MaxPipeDepthAllowed = OS_QUEUE_MAX_DEPTH; + CFE_SB_Global.StatTlmMsg.Payload.MaxSubscriptionsAllowed = + ((CFE_PLATFORM_SB_MAX_MSG_IDS) * (CFE_PLATFORM_SB_MAX_DEST_PER_PKT)); + + Status = CFE_SB_CreatePipe(&CFE_SB_Global.CmdPipe, CFE_SB_CMD_PIPE_DEPTH, CFE_SB_CMD_PIPE_NAME); + if (Status != CFE_SUCCESS) + { + CFE_ES_WriteToSysLog("SB:Call to CFE_SB_CreatePipe Failed:RC=0x%08X\n", (unsigned int)Status); + return Status; + } /* end if */ + + Status = CFE_SB_Subscribe(CFE_SB_ValueToMsgId(CFE_SB_CMD_MID), CFE_SB_Global.CmdPipe); + + if (Status != CFE_SUCCESS) + { + CFE_ES_WriteToSysLog("SB:Subscribe to Cmds Failed:RC=0x%08X\n", (unsigned int)Status); + return Status; + } /* end if */ + + Status = CFE_SB_Subscribe(CFE_SB_ValueToMsgId(CFE_SB_SEND_HK_MID), CFE_SB_Global.CmdPipe); + + if (Status != CFE_SUCCESS) + { + CFE_ES_WriteToSysLog("SB:Subscribe to HK Request Failed:RC=0x%08X\n", (unsigned int)Status); + return Status; + } /* end if */ + + Status = CFE_SB_Subscribe(CFE_SB_ValueToMsgId(CFE_SB_SUB_RPT_CTRL_MID), CFE_SB_Global.CmdPipe); + + if (Status != CFE_SUCCESS) + { + CFE_ES_WriteToSysLog("SB:Subscribe to Subscription Report Request Failed:RC=0x%08X\n", (unsigned int)Status); + return Status; + } /* end if */ + + /* Ensure a ground commanded reset does not get blocked if SB mem pool */ + /* becomes fully configured (DCR6772) */ + Status = CFE_ES_GetPoolBuf(&TmpPtr, CFE_SB_Global.Mem.PoolHdl, sizeof(CFE_ES_RestartCmd_t)); + + if (Status < 0) + { + CFE_ES_WriteToSysLog("SB:Init error, GetPool Failed:RC=0x%08X\n", (unsigned int)Status); + return Status; + } /* end if */ + + /* Return mem block used on previous call,the actual memory is not needed.*/ + /* The SB mem pool is now configured with a block size for the reset cmd. */ + Status = CFE_ES_PutPoolBuf(CFE_SB_Global.Mem.PoolHdl, TmpPtr); + + if (Status < 0) + { + CFE_ES_WriteToSysLog("SB:Init error, PutPool Failed:RC=0x%08X\n", (unsigned int)Status); + return Status; + } /* end if */ + + Status = CFE_EVS_SendEvent(CFE_SB_INIT_EID, CFE_EVS_EventType_INFORMATION, "cFE SB Initialized"); + if (Status != CFE_SUCCESS) + { + CFE_ES_WriteToSysLog("SB:Error sending init event:RC=0x%08X\n", (unsigned int)Status); + return Status; + } /* end if */ + + return CFE_SUCCESS; + +} /* end CFE_SB_AppInit */ + +/****************************************************************************** +** Function: CFE_SB_VerifyCmdLength() +** +** Purpose: +** Function to verify the length of incoming SB command packets +** +** Arguments: +** Message pointer and expected length +** +** Return: +** true if length is acceptable +*/ +bool CFE_SB_VerifyCmdLength(CFE_MSG_Message_t *MsgPtr, size_t ExpectedLength) +{ + bool result = true; + CFE_MSG_Size_t ActualLength = 0; + CFE_MSG_FcnCode_t FcnCode = 0; + CFE_SB_MsgId_t MsgId = CFE_SB_INVALID_MSG_ID; + + CFE_MSG_GetSize(MsgPtr, &ActualLength); + + /* + ** Verify the command packet length + */ + if (ExpectedLength != ActualLength) + { + CFE_MSG_GetMsgId(MsgPtr, &MsgId); + CFE_MSG_GetFcnCode(MsgPtr, &FcnCode); + + CFE_EVS_SendEvent(CFE_SB_LEN_ERR_EID, CFE_EVS_EventType_ERROR, + "Invalid msg length: ID = 0x%X, CC = %u, Len = %u, Expected = %u", + (unsigned int)CFE_SB_MsgIdToValue(MsgId), (unsigned int)FcnCode, (unsigned int)ActualLength, + (unsigned int)ExpectedLength); + result = false; + ++CFE_SB_Global.HKTlmMsg.Payload.CommandErrorCounter; + } + + return (result); + +} /* End of CFE_SB_VerifyCmdLength() */ + +/****************************************************************************** +** Function: CFE_SB_ProcessCmdPipePkt() +** +** Purpose: +** Function to control actions when an SB command is received. +** +** Arguments: +** Software bus buffer pointer +** +** Return: +** none +*/ +void CFE_SB_ProcessCmdPipePkt(CFE_SB_Buffer_t *SBBufPtr) +{ + CFE_SB_MsgId_t MessageID = CFE_SB_INVALID_MSG_ID; + CFE_MSG_FcnCode_t FcnCode = 0; + + CFE_MSG_GetMsgId(&SBBufPtr->Msg, &MessageID); + + switch (CFE_SB_MsgIdToValue(MessageID)) + { + + case CFE_SB_SEND_HK_MID: + /* Note: Command counter not incremented for this command */ + CFE_SB_SendHKTlmCmd((CFE_MSG_CommandHeader_t *)SBBufPtr); + break; + + case CFE_SB_SUB_RPT_CTRL_MID: + /* Note: Command counter not incremented for this command */ + CFE_MSG_GetFcnCode(&SBBufPtr->Msg, &FcnCode); + switch (FcnCode) + { + case CFE_SB_SEND_PREV_SUBS_CC: + if (CFE_SB_VerifyCmdLength(&SBBufPtr->Msg, sizeof(CFE_SB_SendPrevSubsCmd_t))) + { + CFE_SB_SendPrevSubsCmd((CFE_SB_SendPrevSubsCmd_t *)SBBufPtr); + } + break; + + case CFE_SB_ENABLE_SUB_REPORTING_CC: + if (CFE_SB_VerifyCmdLength(&SBBufPtr->Msg, sizeof(CFE_SB_EnableSubReportingCmd_t))) + { + CFE_SB_EnableSubReportingCmd((CFE_SB_EnableSubReportingCmd_t *)SBBufPtr); + } + break; + + case CFE_SB_DISABLE_SUB_REPORTING_CC: + if (CFE_SB_VerifyCmdLength(&SBBufPtr->Msg, sizeof(CFE_SB_DisableSubReportingCmd_t))) + { + CFE_SB_DisableSubReportingCmd((CFE_SB_DisableSubReportingCmd_t *)SBBufPtr); + } + break; + + default: + CFE_EVS_SendEvent(CFE_SB_BAD_CMD_CODE_EID, CFE_EVS_EventType_ERROR, + "Invalid Cmd, Unexpected Command Code %u", (unsigned int)FcnCode); + CFE_SB_Global.HKTlmMsg.Payload.CommandErrorCounter++; + break; + } /* end switch on cmd code */ + break; + + case CFE_SB_CMD_MID: + CFE_MSG_GetFcnCode(&SBBufPtr->Msg, &FcnCode); + switch (FcnCode) + { + case CFE_SB_NOOP_CC: + if (CFE_SB_VerifyCmdLength(&SBBufPtr->Msg, sizeof(CFE_SB_NoopCmd_t))) + { + CFE_SB_NoopCmd((CFE_SB_NoopCmd_t *)SBBufPtr); + } + break; + + case CFE_SB_RESET_COUNTERS_CC: + if (CFE_SB_VerifyCmdLength(&SBBufPtr->Msg, sizeof(CFE_SB_ResetCountersCmd_t))) + { + /* Note: Command counter not incremented for this command */ + CFE_SB_ResetCountersCmd((CFE_SB_ResetCountersCmd_t *)SBBufPtr); + } + break; + + case CFE_SB_SEND_SB_STATS_CC: + if (CFE_SB_VerifyCmdLength(&SBBufPtr->Msg, sizeof(CFE_SB_SendSbStatsCmd_t))) + { + CFE_SB_SendStatsCmd((CFE_SB_SendSbStatsCmd_t *)SBBufPtr); + } + break; + + case CFE_SB_WRITE_ROUTING_INFO_CC: + if (CFE_SB_VerifyCmdLength(&SBBufPtr->Msg, sizeof(CFE_SB_WriteRoutingInfoCmd_t))) + { + CFE_SB_WriteRoutingInfoCmd((CFE_SB_WriteRoutingInfoCmd_t *)SBBufPtr); + } + break; + + case CFE_SB_ENABLE_ROUTE_CC: + if (CFE_SB_VerifyCmdLength(&SBBufPtr->Msg, sizeof(CFE_SB_EnableRouteCmd_t))) + { + CFE_SB_EnableRouteCmd((CFE_SB_EnableRouteCmd_t *)SBBufPtr); + } + break; + + case CFE_SB_DISABLE_ROUTE_CC: + if (CFE_SB_VerifyCmdLength(&SBBufPtr->Msg, sizeof(CFE_SB_DisableRouteCmd_t))) + { + CFE_SB_DisableRouteCmd((CFE_SB_DisableRouteCmd_t *)SBBufPtr); + } + break; + + case CFE_SB_WRITE_PIPE_INFO_CC: + if (CFE_SB_VerifyCmdLength(&SBBufPtr->Msg, sizeof(CFE_SB_WritePipeInfoCmd_t))) + { + CFE_SB_WritePipeInfoCmd((CFE_SB_WritePipeInfoCmd_t *)SBBufPtr); + } + break; + + case CFE_SB_WRITE_MAP_INFO_CC: + if (CFE_SB_VerifyCmdLength(&SBBufPtr->Msg, sizeof(CFE_SB_WriteMapInfoCmd_t))) + { + CFE_SB_WriteMapInfoCmd((CFE_SB_WriteMapInfoCmd_t *)SBBufPtr); + } + break; + + default: + CFE_EVS_SendEvent(CFE_SB_BAD_CMD_CODE_EID, CFE_EVS_EventType_ERROR, + "Invalid Cmd, Unexpected Command Code %u", FcnCode); + CFE_SB_Global.HKTlmMsg.Payload.CommandErrorCounter++; + break; + } /* end switch on cmd code */ + break; + + default: + CFE_EVS_SendEvent(CFE_SB_BAD_MSGID_EID, CFE_EVS_EventType_ERROR, "Invalid Cmd, Unexpected Msg Id: 0x%x", + (unsigned int)CFE_SB_MsgIdToValue(MessageID)); + CFE_SB_Global.HKTlmMsg.Payload.CommandErrorCounter++; + break; + + } /* end switch on MsgId */ + +} /* end CFE_SB_ProcessCmdPipePkt */ + +/****************************************************************************** +** Function: CFE_SB_NoopCmd() +** +** Purpose: +** Handler function the SB command +** +*/ +int32 CFE_SB_NoopCmd(const CFE_SB_NoopCmd_t *data) +{ + CFE_EVS_SendEvent(CFE_SB_CMD0_RCVD_EID, CFE_EVS_EventType_INFORMATION, "No-op Cmd Rcvd. %s", CFE_VERSION_STRING); + CFE_SB_Global.HKTlmMsg.Payload.CommandCounter++; + + return CFE_SUCCESS; +} + +/****************************************************************************** +** Function: CFE_SB_ResetCountersCmd() +** +** Purpose: +** Handler function the SB command +** +*/ +int32 CFE_SB_ResetCountersCmd(const CFE_SB_ResetCountersCmd_t *data) +{ + CFE_EVS_SendEvent(CFE_SB_CMD1_RCVD_EID, CFE_EVS_EventType_DEBUG, "Reset Counters Cmd Rcvd"); + + CFE_SB_ResetCounters(); + + return CFE_SUCCESS; +} + +/****************************************************************************** +** Function: CFE_SB_EnableSubReportingCmd() +** +** Purpose: +** Handler function the SB command +** +*/ +int32 CFE_SB_EnableSubReportingCmd(const CFE_SB_EnableSubReportingCmd_t *data) +{ + CFE_SB_SetSubscriptionReporting(CFE_SB_ENABLE); + return CFE_SUCCESS; +} + +/****************************************************************************** +** Function: CFE_SB_DisableSubReportingCmd() +** +** Purpose: +** Handler function the SB command +** +*/ +int32 CFE_SB_DisableSubReportingCmd(const CFE_SB_DisableSubReportingCmd_t *data) +{ + CFE_SB_SetSubscriptionReporting(CFE_SB_DISABLE); + return CFE_SUCCESS; +} + +/****************************************************************************** +** Function: CFE_SB_SendHKTlmCmd() +** +** Purpose: +** Function to send the SB housekeeping packet. +** +** Arguments: +** none +** +** Notes: +** Command counter not incremented for this command +** +** Return: +** none +*/ +int32 CFE_SB_SendHKTlmCmd(const CFE_MSG_CommandHeader_t *data) +{ + CFE_SB_LockSharedData(__FILE__, __LINE__); + + CFE_SB_Global.HKTlmMsg.Payload.MemInUse = CFE_SB_Global.StatTlmMsg.Payload.MemInUse; + CFE_SB_Global.HKTlmMsg.Payload.UnmarkedMem = + CFE_PLATFORM_SB_BUF_MEMORY_BYTES - CFE_SB_Global.StatTlmMsg.Payload.PeakMemInUse; + + CFE_SB_UnlockSharedData(__FILE__, __LINE__); + + CFE_SB_TimeStampMsg(&CFE_SB_Global.HKTlmMsg.Hdr.Msg); + CFE_SB_TransmitMsg(&CFE_SB_Global.HKTlmMsg.Hdr.Msg, true); + + return CFE_SUCCESS; +} /* end CFE_SB_SendHKTlmCmd */ + +/****************************************************************************** +** Function: CFE_SB_ResetCounters() +** +** Purpose: +** Function to reset the SB housekeeping counters. +** +** Arguments: +** none +** +** Notes: +** Command counter not incremented for this command +** +** Return: +** none +*/ +void CFE_SB_ResetCounters(void) +{ + + CFE_SB_Global.HKTlmMsg.Payload.CommandCounter = 0; + CFE_SB_Global.HKTlmMsg.Payload.CommandErrorCounter = 0; + CFE_SB_Global.HKTlmMsg.Payload.NoSubscribersCounter = 0; + CFE_SB_Global.HKTlmMsg.Payload.DuplicateSubscriptionsCounter = 0; + CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter = 0; + CFE_SB_Global.HKTlmMsg.Payload.MsgReceiveErrorCounter = 0; + CFE_SB_Global.HKTlmMsg.Payload.InternalErrorCounter = 0; + CFE_SB_Global.HKTlmMsg.Payload.CreatePipeErrorCounter = 0; + CFE_SB_Global.HKTlmMsg.Payload.SubscribeErrorCounter = 0; + CFE_SB_Global.HKTlmMsg.Payload.PipeOverflowErrorCounter = 0; + CFE_SB_Global.HKTlmMsg.Payload.MsgLimitErrorCounter = 0; + +} /* end CFE_SB_ResetCounters */ + +/****************************************************************************** +** Function: CFE_SB_EnableRouteCmd() +** +** Purpose: +** SB internal function to enable a specific route. A route is defined as a +** MsgId/PipeId combination. +** +** Arguments: +** MsgPtr : pointer to the message +** +** Return: +** None +*/ +int32 CFE_SB_EnableRouteCmd(const CFE_SB_EnableRouteCmd_t *data) +{ + CFE_SB_MsgId_t MsgId; + CFE_SB_PipeD_t * PipeDscPtr; + CFE_SB_DestinationD_t * DestPtr; + const CFE_SB_RouteCmd_Payload_t *CmdPtr; + uint16 PendingEventID; + + PendingEventID = 0; + CmdPtr = &data->Payload; + + MsgId = CmdPtr->MsgId; + + CFE_SB_LockSharedData(__func__, __LINE__); + + /* check cmd parameters */ + PipeDscPtr = CFE_SB_LocatePipeDescByID(CmdPtr->Pipe); + if (!CFE_SB_IsValidMsgId(MsgId) || !CFE_SB_PipeDescIsMatch(PipeDscPtr, CmdPtr->Pipe)) + { + PendingEventID = CFE_SB_ENBL_RTE3_EID; + CFE_SB_Global.HKTlmMsg.Payload.CommandErrorCounter++; + } + else + { + DestPtr = CFE_SB_GetDestPtr(CFE_SBR_GetRouteId(MsgId), CmdPtr->Pipe); + if (DestPtr == NULL) + { + PendingEventID = CFE_SB_ENBL_RTE1_EID; + CFE_SB_Global.HKTlmMsg.Payload.CommandErrorCounter++; + } + else + { + DestPtr->Active = CFE_SB_ACTIVE; + PendingEventID = CFE_SB_ENBL_RTE2_EID; + CFE_SB_Global.HKTlmMsg.Payload.CommandCounter++; + } + + } /* end if */ + + CFE_SB_UnlockSharedData(__func__, __LINE__); + + switch (PendingEventID) + { + case CFE_SB_ENBL_RTE1_EID: + CFE_EVS_SendEvent(CFE_SB_ENBL_RTE1_EID, CFE_EVS_EventType_ERROR, + "Enbl Route Cmd:Route does not exist.Msg 0x%x,Pipe %lu", + (unsigned int)CFE_SB_MsgIdToValue(MsgId), CFE_RESOURCEID_TO_ULONG(CmdPtr->Pipe)); + break; + case CFE_SB_ENBL_RTE3_EID: + CFE_EVS_SendEvent(CFE_SB_ENBL_RTE3_EID, CFE_EVS_EventType_ERROR, + "Enbl Route Cmd:Invalid Param.Msg 0x%x,Pipe %lu", + (unsigned int)CFE_SB_MsgIdToValue(MsgId), CFE_RESOURCEID_TO_ULONG(CmdPtr->Pipe)); + break; + case CFE_SB_ENBL_RTE2_EID: + CFE_EVS_SendEvent(CFE_SB_ENBL_RTE2_EID, CFE_EVS_EventType_DEBUG, "Enabling Route,Msg 0x%x,Pipe %lu", + (unsigned int)CFE_SB_MsgIdToValue(MsgId), CFE_RESOURCEID_TO_ULONG(CmdPtr->Pipe)); + break; + } + + return CFE_SUCCESS; +} /* end CFE_SB_EnableRouteCmd */ + +/****************************************************************************** +** Function: CFE_SB_DisableRouteCmd() +** +** Purpose: +** SB internal function to disable a specific route. A route is defined as a +** MsgId/PipeId combination. +** +** Arguments: +** MsgPtr : pointer to the message +** +** Return: +** None +*/ +int32 CFE_SB_DisableRouteCmd(const CFE_SB_DisableRouteCmd_t *data) +{ + CFE_SB_MsgId_t MsgId; + CFE_SB_PipeD_t * PipeDscPtr; + CFE_SB_DestinationD_t * DestPtr; + const CFE_SB_RouteCmd_Payload_t *CmdPtr; + uint16 PendingEventID; + + PendingEventID = 0; + CmdPtr = &data->Payload; + + MsgId = CmdPtr->MsgId; + + CFE_SB_LockSharedData(__func__, __LINE__); + + /* check cmd parameters */ + PipeDscPtr = CFE_SB_LocatePipeDescByID(CmdPtr->Pipe); + if (!CFE_SB_IsValidMsgId(MsgId) || !CFE_SB_PipeDescIsMatch(PipeDscPtr, CmdPtr->Pipe)) + { + PendingEventID = CFE_SB_DSBL_RTE3_EID; + CFE_SB_Global.HKTlmMsg.Payload.CommandErrorCounter++; + } + else + { + DestPtr = CFE_SB_GetDestPtr(CFE_SBR_GetRouteId(MsgId), CmdPtr->Pipe); + if (DestPtr == NULL) + { + PendingEventID = CFE_SB_DSBL_RTE1_EID; + CFE_SB_Global.HKTlmMsg.Payload.CommandErrorCounter++; + } + else + { + DestPtr->Active = CFE_SB_INACTIVE; + PendingEventID = CFE_SB_DSBL_RTE2_EID; + CFE_SB_Global.HKTlmMsg.Payload.CommandCounter++; + } + + } /* end if */ + + CFE_SB_UnlockSharedData(__func__, __LINE__); + + switch (PendingEventID) + { + case CFE_SB_DSBL_RTE1_EID: + CFE_EVS_SendEvent(CFE_SB_DSBL_RTE1_EID, CFE_EVS_EventType_ERROR, + "Disable Route Cmd:Route does not exist,Msg 0x%x,Pipe %lu", + (unsigned int)CFE_SB_MsgIdToValue(MsgId), CFE_RESOURCEID_TO_ULONG(CmdPtr->Pipe)); + break; + case CFE_SB_DSBL_RTE3_EID: + CFE_EVS_SendEvent(CFE_SB_DSBL_RTE3_EID, CFE_EVS_EventType_ERROR, + "Disable Route Cmd:Invalid Param.Msg 0x%x,Pipe %lu", + (unsigned int)CFE_SB_MsgIdToValue(MsgId), CFE_RESOURCEID_TO_ULONG(CmdPtr->Pipe)); + break; + case CFE_SB_DSBL_RTE2_EID: + CFE_EVS_SendEvent(CFE_SB_DSBL_RTE2_EID, CFE_EVS_EventType_DEBUG, "Route Disabled,Msg 0x%x,Pipe %lu", + (unsigned int)CFE_SB_MsgIdToValue(MsgId), CFE_RESOURCEID_TO_ULONG(CmdPtr->Pipe)); + break; + } + + return CFE_SUCCESS; +} /* end CFE_SB_DisableRouteCmd */ + +/****************************************************************************** +** Function: CFE_SB_SendStatsCmd() +** +** Purpose: +** SB internal function to send a Software Bus statistics packet +** +** Arguments: +** None +** +** Return: +** None +*/ +int32 CFE_SB_SendStatsCmd(const CFE_SB_SendSbStatsCmd_t *data) +{ + uint32 PipeDscCount; + uint32 PipeStatCount; + CFE_SB_PipeD_t * PipeDscPtr; + CFE_SB_PipeDepthStats_t *PipeStatPtr; + + CFE_SB_LockSharedData(__FILE__, __LINE__); + + /* Collect data on pipes */ + PipeDscCount = CFE_PLATFORM_SB_MAX_PIPES; + PipeStatCount = CFE_MISSION_SB_MAX_PIPES; + PipeDscPtr = CFE_SB_Global.PipeTbl; + PipeStatPtr = CFE_SB_Global.StatTlmMsg.Payload.PipeDepthStats; + + while (PipeDscCount > 0 && PipeStatCount > 0) + { + if (CFE_SB_PipeDescIsUsed(PipeDscPtr)) + { + PipeStatPtr->PipeId = PipeDscPtr->PipeId; + + /* Copy depth info */ + PipeStatPtr->CurrentQueueDepth = PipeDscPtr->CurrentQueueDepth; + PipeStatPtr->PeakQueueDepth = PipeDscPtr->PeakQueueDepth; + PipeStatPtr->MaxQueueDepth = PipeDscPtr->MaxQueueDepth; + + ++PipeStatPtr; + --PipeStatCount; + } + + --PipeDscCount; + ++PipeDscPtr; + } + + CFE_SB_UnlockSharedData(__FILE__, __LINE__); + + while (PipeStatCount > 0) + { + memset(PipeStatPtr, 0, sizeof(*PipeStatPtr)); + + ++PipeStatPtr; + --PipeStatCount; + } + + CFE_SB_TimeStampMsg(&CFE_SB_Global.StatTlmMsg.Hdr.Msg); + CFE_SB_TransmitMsg(&CFE_SB_Global.StatTlmMsg.Hdr.Msg, true); + + CFE_EVS_SendEvent(CFE_SB_SND_STATS_EID, CFE_EVS_EventType_DEBUG, "Software Bus Statistics packet sent"); + + CFE_SB_Global.HKTlmMsg.Payload.CommandCounter++; + + return CFE_SUCCESS; +} /* CFE_SB_SendStatsCmd */ + +/****************************************************************************** + * Local callback helper for writing routing info to a file + */ +void CFE_SB_CollectRouteInfo(CFE_SBR_RouteId_t RouteId, void *ArgPtr) +{ + CFE_SB_DestinationD_t * DestPtr; + CFE_SB_PipeD_t * PipeDscPtr; + CFE_SB_MsgId_t RouteMsgId; + CFE_SB_BackgroundRouteInfoBuffer_t *RouteBufferPtr; + CFE_SB_RoutingFileEntry_t * FileEntryPtr; + CFE_ES_AppId_t DestAppId[CFE_PLATFORM_SB_MAX_DEST_PER_PKT]; + uint32 i; + + /* Cast arguments for local use */ + RouteBufferPtr = (CFE_SB_BackgroundRouteInfoBuffer_t *)ArgPtr; + + /* Extract data from runtime info, write into the temporary buffer */ + /* Data must be locked to snapshot the route info */ + CFE_SB_LockSharedData(__FILE__, __LINE__); + + RouteMsgId = CFE_SBR_GetMsgId(RouteId); + RouteBufferPtr->NumDestinations = 0; + + /* If this is a valid route, get the destinations */ + if (CFE_SB_IsValidMsgId(RouteMsgId)) + { + DestPtr = CFE_SBR_GetDestListHeadPtr(RouteId); + + /* copy relevant data from the destination list into the temp buffer */ + while (DestPtr != NULL && RouteBufferPtr->NumDestinations < CFE_PLATFORM_SB_MAX_DEST_PER_PKT) + { + PipeDscPtr = CFE_SB_LocatePipeDescByID(DestPtr->PipeId); + + /* If invalid id, continue on to next entry */ + if (CFE_SB_PipeDescIsMatch(PipeDscPtr, DestPtr->PipeId)) + { + FileEntryPtr = &RouteBufferPtr->DestEntries[RouteBufferPtr->NumDestinations]; + + /* clear all fields in the temp buffer before re-use */ + memset(FileEntryPtr, 0, sizeof(*FileEntryPtr)); + + FileEntryPtr->PipeId = DestPtr->PipeId; + FileEntryPtr->State = DestPtr->Active; + FileEntryPtr->MsgCnt = DestPtr->DestCnt; + + /* Stash the Pipe Owner AppId - App Name is looked up later (comes from ES) */ + DestAppId[RouteBufferPtr->NumDestinations] = PipeDscPtr->AppId; + + ++RouteBufferPtr->NumDestinations; + } + + DestPtr = DestPtr->Next; + } + } + + CFE_SB_UnlockSharedData(__FILE__, __LINE__); + + /* Go through the temp buffer and fill in the remaining info for each dest */ + FileEntryPtr = RouteBufferPtr->DestEntries; + for (i = 0; i < RouteBufferPtr->NumDestinations; ++i) + { + /* All dest entries refer to the same MsgId (based on the route) */ + FileEntryPtr->MsgId = RouteMsgId; + + /* + * NOTE: as long as CFE_ES_GetAppName() is given a nonzero-length + * output buffer, it guarantees null termination of the output, even + * if the AppID is invalid - in which case it returns an empty string. + */ + CFE_ES_GetAppName(FileEntryPtr->AppName, DestAppId[i], sizeof(FileEntryPtr->AppName)); + CFE_SB_GetPipeName(FileEntryPtr->PipeName, sizeof(FileEntryPtr->PipeName), FileEntryPtr->PipeId); + + ++FileEntryPtr; + } +} + +/****************************************************************************** +** Function: CFE_SB_SendSubscriptionReport() +** +** Purpose: +** SB internal function to generate the "ONESUB_TLM" message after a subscription. +** No-op when subscription reporting is disabled. +** +** Arguments: +** Payload of notification message - MsgId, PipeId, QOS +** +** Return: +** CFE_SUCCESS or error code +*/ +int32 CFE_SB_SendSubscriptionReport(CFE_SB_MsgId_t MsgId, CFE_SB_PipeId_t PipeId, CFE_SB_Qos_t Quality) +{ + CFE_SB_SingleSubscriptionTlm_t SubRptMsg; + int32 Status; + + Status = CFE_SUCCESS; + + if (CFE_SB_Global.SubscriptionReporting == CFE_SB_ENABLE) + { + CFE_MSG_Init(&SubRptMsg.Hdr.Msg, CFE_SB_ValueToMsgId(CFE_SB_ONESUB_TLM_MID), sizeof(SubRptMsg)); + + SubRptMsg.Payload.MsgId = MsgId; + SubRptMsg.Payload.Pipe = PipeId; + SubRptMsg.Payload.Qos = Quality; + SubRptMsg.Payload.SubType = CFE_SB_SUBSCRIPTION; + + Status = CFE_SB_TransmitMsg(&SubRptMsg.Hdr.Msg, true); + CFE_EVS_SendEventWithAppID(CFE_SB_SUBSCRIPTION_RPT_EID, CFE_EVS_EventType_DEBUG, CFE_SB_Global.AppId, + "Sending Subscription Report Msg=0x%x,Pipe=%lu,Stat=0x%x", + (unsigned int)CFE_SB_MsgIdToValue(MsgId), CFE_RESOURCEID_TO_ULONG(PipeId), + (unsigned int)Status); + } + + return Status; +} + +bool CFE_SB_WriteRouteInfoDataGetter(void *Meta, uint32 RecordNum, void **Buffer, size_t *BufSize) +{ + CFE_SB_BackgroundFileStateInfo_t *BgFilePtr; + CFE_SBR_Throttle_t Throttle; + + /* Cast arguments for local use */ + BgFilePtr = (CFE_SB_BackgroundFileStateInfo_t *)Meta; + + Throttle.StartIndex = RecordNum; + Throttle.MaxLoop = 1; + Throttle.NextIndex = 0; + + /* Reset NumDestinations to 0, just in case the CFE_SBR_ForEachRouteId() is a no-op */ + BgFilePtr->Buffer.RouteInfo.NumDestinations = 0; + + /* Collect info on the next route (limited to one per cycle via throttle) */ + CFE_SBR_ForEachRouteId(CFE_SB_CollectRouteInfo, &BgFilePtr->Buffer.RouteInfo, &Throttle); + + /* Pass the output of CFE_SB_CollectRouteInfo() back to be written */ + *Buffer = &BgFilePtr->Buffer.RouteInfo.DestEntries; + *BufSize = sizeof(CFE_SB_RoutingFileEntry_t) * BgFilePtr->Buffer.RouteInfo.NumDestinations; + + /* Check for EOF (last entry) - NextIndex is nonzero if more records left, zero at the end of the route table */ + return (Throttle.NextIndex == 0); +} + +void CFE_SB_BackgroundFileEventHandler(void *Meta, CFE_FS_FileWriteEvent_t Event, int32 Status, uint32 RecordNum, + size_t BlockSize, size_t Position) +{ + CFE_SB_BackgroundFileStateInfo_t *BgFilePtr; + + BgFilePtr = (CFE_SB_BackgroundFileStateInfo_t *)Meta; + + /* + * Note that this runs in the context of ES background task (file writer background job) + * It does NOT run in the context of the CFE_TBL app task. + * + * Events should use CFE_EVS_SendEventWithAppID() rather than CFE_EVS_SendEvent() + * to get proper association with TBL task. + */ + switch (Event) + { + case CFE_FS_FileWriteEvent_COMPLETE: + CFE_EVS_SendEventWithAppID(CFE_SB_SND_RTG_EID, CFE_EVS_EventType_DEBUG, CFE_SB_Global.AppId, + "%s written:Size=%d,Entries=%d", BgFilePtr->FileWrite.FileName, (int)Position, + (int)RecordNum); + break; + + case CFE_FS_FileWriteEvent_HEADER_WRITE_ERROR: + case CFE_FS_FileWriteEvent_RECORD_WRITE_ERROR: + CFE_EVS_SendEventWithAppID(CFE_SB_FILEWRITE_ERR_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, + "File write,byte cnt err,file %s,request=%d,actual=%d", + BgFilePtr->FileWrite.FileName, (int)BlockSize, (int)Status); + break; + + case CFE_FS_FileWriteEvent_CREATE_ERROR: + CFE_EVS_SendEventWithAppID(CFE_SB_SND_RTG_ERR1_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, + "Error creating file %s, stat=0x%x", BgFilePtr->FileWrite.FileName, (int)Status); + break; + + default: + /* unhandled event - ignore */ + break; + } +} + +/****************************************************************************** + * \brief SB internal function to handle processing of 'Write Routing Info' Cmd + * + * \param[in] data Pointer to command structure + * + * \return Execution status, see \ref CFEReturnCodes + */ +int32 CFE_SB_WriteRoutingInfoCmd(const CFE_SB_WriteRoutingInfoCmd_t *data) +{ + const CFE_SB_WriteFileInfoCmd_Payload_t *CmdPtr; + CFE_SB_BackgroundFileStateInfo_t * StatePtr; + int32 Status; + + StatePtr = &CFE_SB_Global.BackgroundFile; + CmdPtr = &data->Payload; + + /* If a routing info dump was already pending, do not overwrite the current request */ + if (!CFE_FS_BackgroundFileDumpIsPending(&StatePtr->FileWrite)) + { + /* Reset the entire state object (just for good measure, ensure no stale data) */ + memset(StatePtr, 0, sizeof(*StatePtr)); + + /* + * Fill out the remainder of meta data. + * This data is currently the same for every request + */ + StatePtr->FileWrite.FileSubType = CFE_FS_SubType_SB_ROUTEDATA; + snprintf(StatePtr->FileWrite.Description, sizeof(StatePtr->FileWrite.Description), "SB Routing Information"); + + StatePtr->FileWrite.GetData = CFE_SB_WriteRouteInfoDataGetter; + StatePtr->FileWrite.OnEvent = CFE_SB_BackgroundFileEventHandler; + + /* + ** Copy the filename into local buffer with default name/path/extension if not specified + */ + Status = CFE_FS_ParseInputFileNameEx(StatePtr->FileWrite.FileName, CmdPtr->Filename, + sizeof(StatePtr->FileWrite.FileName), sizeof(CmdPtr->Filename), + CFE_PLATFORM_SB_DEFAULT_ROUTING_FILENAME, + CFE_FS_GetDefaultMountPoint(CFE_FS_FileCategory_BINARY_DATA_DUMP), + CFE_FS_GetDefaultExtension(CFE_FS_FileCategory_BINARY_DATA_DUMP)); + + if (Status == CFE_SUCCESS) + { + Status = CFE_FS_BackgroundFileDumpRequest(&StatePtr->FileWrite); + } + } + else + { + Status = CFE_STATUS_REQUEST_ALREADY_PENDING; + } + + if (Status != CFE_SUCCESS) + { + /* generate the same event as is generated when unable to create the file (same thing, really) */ + CFE_SB_BackgroundFileEventHandler(StatePtr, CFE_FS_FileWriteEvent_CREATE_ERROR, Status, 0, 0, 0); + } + + CFE_SB_IncrCmdCtr(Status); + + return CFE_SUCCESS; +} /* end CFE_SB_WriteRoutingInfoCmd */ + +bool CFE_SB_WritePipeInfoDataGetter(void *Meta, uint32 RecordNum, void **Buffer, size_t *BufSize) +{ + CFE_SB_BackgroundFileStateInfo_t *BgFilePtr; + CFE_SB_PipeInfoEntry_t * PipeBufferPtr; + CFE_SB_PipeD_t * PipeDscPtr; + osal_id_t SysQueueId = OS_OBJECT_ID_UNDEFINED; + bool PipeIsValid; + + BgFilePtr = (CFE_SB_BackgroundFileStateInfo_t *)Meta; + PipeDscPtr = NULL; + PipeIsValid = false; + + PipeBufferPtr = &BgFilePtr->Buffer.PipeInfo; + + if (RecordNum < CFE_PLATFORM_SB_MAX_PIPES) + { + PipeDscPtr = &CFE_SB_Global.PipeTbl[RecordNum]; + + CFE_SB_LockSharedData(__FILE__, __LINE__); + + PipeIsValid = CFE_SB_PipeDescIsUsed(PipeDscPtr); + + if (PipeIsValid) + { + /* + * Ensure any old data in the struct has been cleared + */ + memset(PipeBufferPtr, 0, sizeof(*PipeBufferPtr)); + + /* + * Take a "snapshot" of the PipeDsc state while locked + */ + PipeBufferPtr->PipeId = CFE_SB_PipeDescGetID(PipeDscPtr); + PipeBufferPtr->AppId = PipeDscPtr->AppId; + PipeBufferPtr->Opts = PipeDscPtr->Opts; + + /* copy stats info */ + PipeBufferPtr->SendErrors = PipeDscPtr->SendErrors; + PipeBufferPtr->MaxQueueDepth = PipeDscPtr->MaxQueueDepth; + PipeBufferPtr->CurrentQueueDepth = PipeDscPtr->CurrentQueueDepth; + PipeBufferPtr->PeakQueueDepth = PipeDscPtr->PeakQueueDepth; + + SysQueueId = PipeDscPtr->SysQueueId; + } + + CFE_SB_UnlockSharedData(__FILE__, __LINE__); + } + + if (PipeIsValid) + { + /* + * Gather data from other subsystems while unlocked. + * This might fail if the pipe is deleted simultaneously while this runs, but in + * the unlikely event that happens, the name data will simply be blank as the ID(s) + * will not validate. + */ + OS_GetResourceName(SysQueueId, PipeBufferPtr->PipeName, sizeof(PipeBufferPtr->PipeName)); + CFE_ES_GetAppName(PipeBufferPtr->AppName, PipeBufferPtr->AppId, sizeof(PipeBufferPtr->AppName)); + + *Buffer = PipeBufferPtr; + *BufSize = sizeof(*PipeBufferPtr); + } + else + { + *Buffer = NULL; + *BufSize = 0; + } + + /* Check for EOF (last entry) */ + return (RecordNum >= (CFE_PLATFORM_SB_MAX_PIPES - 1)); +} + +/****************************************************************************** + * \brief SB internal function to handle processing of 'Write Pipe Info' Cmd + * + * \param[in] data Pointer to command structure + * + * \return Execution status, see \ref CFEReturnCodes + */ +int32 CFE_SB_WritePipeInfoCmd(const CFE_SB_WritePipeInfoCmd_t *data) +{ + const CFE_SB_WriteFileInfoCmd_Payload_t *CmdPtr; + CFE_SB_BackgroundFileStateInfo_t * StatePtr; + int32 Status; + + StatePtr = &CFE_SB_Global.BackgroundFile; + CmdPtr = &data->Payload; + + /* If a pipe info dump was already pending, do not overwrite the current request */ + if (!CFE_FS_BackgroundFileDumpIsPending(&StatePtr->FileWrite)) + { + /* Reset the entire state object (just for good measure, ensure no stale data) */ + memset(StatePtr, 0, sizeof(*StatePtr)); + + /* + * Fill out the remainder of meta data. + * This data is currently the same for every request + */ + StatePtr->FileWrite.FileSubType = CFE_FS_SubType_SB_PIPEDATA; + snprintf(StatePtr->FileWrite.Description, sizeof(StatePtr->FileWrite.Description), "SB Pipe Information"); + + StatePtr->FileWrite.GetData = CFE_SB_WritePipeInfoDataGetter; + StatePtr->FileWrite.OnEvent = CFE_SB_BackgroundFileEventHandler; + + /* + ** Copy the filename into local buffer with default name/path/extension if not specified + */ + Status = CFE_FS_ParseInputFileNameEx(StatePtr->FileWrite.FileName, CmdPtr->Filename, + sizeof(StatePtr->FileWrite.FileName), sizeof(CmdPtr->Filename), + CFE_PLATFORM_SB_DEFAULT_PIPE_FILENAME, + CFE_FS_GetDefaultMountPoint(CFE_FS_FileCategory_BINARY_DATA_DUMP), + CFE_FS_GetDefaultExtension(CFE_FS_FileCategory_BINARY_DATA_DUMP)); + + if (Status == CFE_SUCCESS) + { + Status = CFE_FS_BackgroundFileDumpRequest(&StatePtr->FileWrite); + } + } + else + { + Status = CFE_STATUS_REQUEST_ALREADY_PENDING; + } + + if (Status != CFE_SUCCESS) + { + /* generate the same event as is generated when unable to create the file (same thing, really) */ + CFE_SB_BackgroundFileEventHandler(StatePtr, CFE_FS_FileWriteEvent_CREATE_ERROR, Status, 0, 0, 0); + } + + CFE_SB_IncrCmdCtr(Status); + + return CFE_SUCCESS; +} + +/****************************************************************************** + * Local callback helper for writing map info to a file + */ +void CFE_SB_CollectMsgMapInfo(CFE_SBR_RouteId_t RouteId, void *ArgPtr) +{ + CFE_SB_MsgMapFileEntry_t *BufferPtr; + + /* Cast arguments for local use */ + BufferPtr = (CFE_SB_MsgMapFileEntry_t *)ArgPtr; + + /* Extract data from runtime info, write into the temporary buffer */ + /* Data must be locked to snapshot the route info */ + CFE_SB_LockSharedData(__FILE__, __LINE__); + + BufferPtr->MsgId = CFE_SBR_GetMsgId(RouteId); + BufferPtr->Index = CFE_SBR_RouteIdToValue(RouteId); + + CFE_SB_UnlockSharedData(__FILE__, __LINE__); +} + +bool CFE_SB_WriteMsgMapInfoDataGetter(void *Meta, uint32 RecordNum, void **Buffer, size_t *BufSize) +{ + CFE_SB_BackgroundFileStateInfo_t *BgFilePtr; + CFE_SBR_Throttle_t Throttle; + + /* Cast arguments for local use */ + BgFilePtr = (CFE_SB_BackgroundFileStateInfo_t *)Meta; + + Throttle.StartIndex = RecordNum; + Throttle.MaxLoop = 1; + Throttle.NextIndex = 0; + + /* Set the MsgId intially - will be overwritten with real info in CFE_SB_CollectMsgMapInfo */ + BgFilePtr->Buffer.MsgMapInfo.MsgId = CFE_SB_INVALID_MSG_ID; + + /* Collect info on the next route (limited to one per cycle via throttle) */ + CFE_SBR_ForEachRouteId(CFE_SB_CollectMsgMapInfo, &BgFilePtr->Buffer.MsgMapInfo, &Throttle); + + /* If Map was valid, pass the output of CFE_SB_CollectMsgMapInfo() back to be written */ + if (CFE_SB_IsValidMsgId(BgFilePtr->Buffer.MsgMapInfo.MsgId)) + { + *Buffer = &BgFilePtr->Buffer.MsgMapInfo; + *BufSize = sizeof(CFE_SB_MsgMapFileEntry_t); + } + else + { + *Buffer = NULL; + *BufSize = 0; + } + + /* Check for EOF (last entry) - NextIndex is nonzero if more records left, zero at the end of the route table */ + return (Throttle.NextIndex == 0); +} + +/****************************************************************************** + * \brief SB internal function to handle processing of 'Write Map Info' Cmd + * + * \param[in] data Pointer to command structure + * + * \return Execution status, see \ref CFEReturnCodes + */ +int32 CFE_SB_WriteMapInfoCmd(const CFE_SB_WriteMapInfoCmd_t *data) +{ + const CFE_SB_WriteFileInfoCmd_Payload_t *CmdPtr; + CFE_SB_BackgroundFileStateInfo_t * StatePtr; + int32 Status; + + StatePtr = &CFE_SB_Global.BackgroundFile; + CmdPtr = &data->Payload; + + /* If a pipe info dump was already pending, do not overwrite the current request */ + if (!CFE_FS_BackgroundFileDumpIsPending(&StatePtr->FileWrite)) + { + /* Reset the entire state object (just for good measure, ensure no stale data) */ + memset(StatePtr, 0, sizeof(*StatePtr)); + + /* + * Fill out the remainder of meta data. + * This data is currently the same for every request + */ + StatePtr->FileWrite.FileSubType = CFE_FS_SubType_SB_MAPDATA; + snprintf(StatePtr->FileWrite.Description, sizeof(StatePtr->FileWrite.Description), "SB Map Information"); + + StatePtr->FileWrite.GetData = CFE_SB_WriteMsgMapInfoDataGetter; + StatePtr->FileWrite.OnEvent = CFE_SB_BackgroundFileEventHandler; + + /* + ** Copy the filename into local buffer with default name/path/extension if not specified + */ + Status = CFE_FS_ParseInputFileNameEx(StatePtr->FileWrite.FileName, CmdPtr->Filename, + sizeof(StatePtr->FileWrite.FileName), sizeof(CmdPtr->Filename), + CFE_PLATFORM_SB_DEFAULT_MAP_FILENAME, + CFE_FS_GetDefaultMountPoint(CFE_FS_FileCategory_BINARY_DATA_DUMP), + CFE_FS_GetDefaultExtension(CFE_FS_FileCategory_BINARY_DATA_DUMP)); + + if (Status == CFE_SUCCESS) + { + Status = CFE_FS_BackgroundFileDumpRequest(&StatePtr->FileWrite); + } + } + else + { + Status = CFE_STATUS_REQUEST_ALREADY_PENDING; + } + + if (Status != CFE_SUCCESS) + { + /* generate the same event as is generated when unable to create the file (same thing, really) */ + CFE_SB_BackgroundFileEventHandler(StatePtr, CFE_FS_FileWriteEvent_CREATE_ERROR, Status, 0, 0, 0); + } + + CFE_SB_IncrCmdCtr(Status); + + return CFE_SUCCESS; +} /* end CFE_SB_WriteMapInfoCmd */ + +/****************************************************************************** + * Local callback helper for sending route subscriptions + */ +void CFE_SB_SendRouteSub(CFE_SBR_RouteId_t RouteId, void *ArgPtr) +{ + CFE_SB_DestinationD_t *destptr; + int32 status; + + destptr = CFE_SBR_GetDestListHeadPtr(RouteId); + + /* Loop through destinations */ + while (destptr != NULL) + { + + if (destptr->Scope == CFE_SB_MSG_GLOBAL) + { + + /* ...add entry into pkt */ + CFE_SB_Global.PrevSubMsg.Payload.Entry[CFE_SB_Global.PrevSubMsg.Payload.Entries].MsgId = + CFE_SBR_GetMsgId(RouteId); + CFE_SB_Global.PrevSubMsg.Payload.Entry[CFE_SB_Global.PrevSubMsg.Payload.Entries].Qos.Priority = 0; + CFE_SB_Global.PrevSubMsg.Payload.Entry[CFE_SB_Global.PrevSubMsg.Payload.Entries].Qos.Reliability = 0; + CFE_SB_Global.PrevSubMsg.Payload.Entries++; + + /* send pkt if full */ + if (CFE_SB_Global.PrevSubMsg.Payload.Entries >= CFE_SB_SUB_ENTRIES_PER_PKT) + { + CFE_SB_UnlockSharedData(__func__, __LINE__); + status = CFE_SB_TransmitMsg(&CFE_SB_Global.PrevSubMsg.Hdr.Msg, true); + CFE_EVS_SendEvent(CFE_SB_FULL_SUB_PKT_EID, CFE_EVS_EventType_DEBUG, + "Full Sub Pkt %d Sent,Entries=%d,Stat=0x%x\n", + (int)CFE_SB_Global.PrevSubMsg.Payload.PktSegment, + (int)CFE_SB_Global.PrevSubMsg.Payload.Entries, (unsigned int)status); + CFE_SB_LockSharedData(__func__, __LINE__); + CFE_SB_Global.PrevSubMsg.Payload.Entries = 0; + CFE_SB_Global.PrevSubMsg.Payload.PktSegment++; + } + + /* + * break while loop through destinations, onto next route + * This is done because we want only one network subscription per msgid + * Later when Qos is used, we may want to take just the highest priority + * subscription if there are more than one + */ + break; + } + + /* Advance to next destination */ + destptr = destptr->Next; + } +} + +/****************************************************************************** +** Function: CFE_SB_SendPrevSubsCmd() +** +** Purpose: +** SB function to build and send an SB packet containing a complete list of +** current subscriptions.Intended to be used primarily for the Software Bus +** Networking Application (SBN). +** +** Arguments: +** None +** +** Return: +** None +*/ +int32 CFE_SB_SendPrevSubsCmd(const CFE_SB_SendPrevSubsCmd_t *data) +{ + int32 status; + + /* Take semaphore to ensure data does not change during this function */ + CFE_SB_LockSharedData(__func__, __LINE__); + + /* Initialize entry/segment tracking */ + CFE_SB_Global.PrevSubMsg.Payload.PktSegment = 1; + CFE_SB_Global.PrevSubMsg.Payload.Entries = 0; + + /* Send subcription for each route */ + CFE_SBR_ForEachRouteId(CFE_SB_SendRouteSub, NULL, NULL); + + CFE_SB_UnlockSharedData(__func__, __LINE__); + + /* if pkt has any number of entries, send it as a partial pkt */ + if (CFE_SB_Global.PrevSubMsg.Payload.Entries > 0) + { + status = CFE_SB_TransmitMsg(&CFE_SB_Global.PrevSubMsg.Hdr.Msg, true); + CFE_EVS_SendEvent(CFE_SB_PART_SUB_PKT_EID, CFE_EVS_EventType_DEBUG, + "Partial Sub Pkt %d Sent,Entries=%d,Stat=0x%x", + (int)CFE_SB_Global.PrevSubMsg.Payload.PktSegment, + (int)CFE_SB_Global.PrevSubMsg.Payload.Entries, (unsigned int)status); + } + + return CFE_SUCCESS; +} /* end CFE_SB_SendPrevSubsCmd */ + +/****************************************************************************** +** Function: CFE_SB_IncrCmdCtr() +** +** Purpose: +** SB internal function to increment the proper cmd counter based on the +** status input. This small utility was written to eliminate duplicate code. +** +** Arguments: +** status - typically CFE_SUCCESS or an SB error code +** +** Return: +** None +*/ +void CFE_SB_IncrCmdCtr(int32 status) +{ + + if (status == CFE_SUCCESS) + { + CFE_SB_Global.HKTlmMsg.Payload.CommandCounter++; + } + else + { + CFE_SB_Global.HKTlmMsg.Payload.CommandErrorCounter++; + } /* end if */ + +} /* end CFE_SB_IncrCmdCtr */ + +/****************************************************************************** +** Function: CFE_SB_SetSubscriptionReporting() +** +** Purpose: +** SB internal function to enable and disable subscription reporting. +** +** Arguments: +** +** +** Return: +** None +*/ +void CFE_SB_SetSubscriptionReporting(uint32 state) +{ + + CFE_SB_Global.SubscriptionReporting = state; + +} /* end CFE_SB_SetSubscriptionReporting */ diff --git a/modules/sb/fsw/src/cfe_sb_util.c b/modules/sb/fsw/src/cfe_sb_util.c new file mode 100644 index 000000000..bbfa95a7b --- /dev/null +++ b/modules/sb/fsw/src/cfe_sb_util.c @@ -0,0 +1,253 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/****************************************************************************** +** File: cfe_sb_util.c +** +** Purpose: +** This file contains 'access' macros and functions for reading and +** writing message header fields. +** +** Author: R.McGraw/SSI +** +******************************************************************************/ + +/* +** Include Files +*/ + +#include "cfe_sb_module_all.h" + +#include + +/****************************************************************************** +** Function: CFE_SB_MsgHdrSize() +** +** Purpose: +** Get the size of a message header. +** +** Arguments: +** *MsgPtr - Pointer to a SB message +** +** Return: +** Size of Message Header. +*/ +size_t CFE_SB_MsgHdrSize(const CFE_MSG_Message_t *MsgPtr) +{ + size_t size = 0; + bool hassechdr = false; + CFE_MSG_Type_t type = CFE_MSG_Type_Invalid; + + if (MsgPtr == NULL) + { + return CFE_SB_BAD_ARGUMENT; + } + + CFE_MSG_GetHasSecondaryHeader(MsgPtr, &hassechdr); + CFE_MSG_GetType(MsgPtr, &type); + + /* if secondary hdr is not present... */ + /* Since all cFE messages must have a secondary hdr this check is not needed */ + if (!hassechdr) + { + size = sizeof(CCSDS_SpacePacket_t); + } + else if (type == CFE_MSG_Type_Cmd) + { + size = sizeof(CFE_MSG_CommandHeader_t); + } + else if (type == CFE_MSG_Type_Tlm) + { + size = sizeof(CFE_MSG_TelemetryHeader_t); + } + + return size; + +} /* end CFE_SB_MsgHdrSize */ + +/* + * Function: CFE_SB_GetUserData - See API and header file for details + */ +void *CFE_SB_GetUserData(CFE_MSG_Message_t *MsgPtr) +{ + uint8 *BytePtr; + size_t HdrSize; + + if (MsgPtr == NULL) + { + CFE_ES_WriteToSysLog("CFE_SB:GetUserData-Failed invalid arguments\n"); + return 0; + } + + BytePtr = (uint8 *)MsgPtr; + HdrSize = CFE_SB_MsgHdrSize(MsgPtr); + + return (BytePtr + HdrSize); +} /* end CFE_SB_GetUserData */ + +/* + * Function: CFE_SB_GetUserDataLength - See API and header file for details + */ +size_t CFE_SB_GetUserDataLength(const CFE_MSG_Message_t *MsgPtr) +{ + CFE_MSG_Size_t TotalMsgSize = 0; + size_t HdrSize; + + if (MsgPtr == NULL) + { + return CFE_SB_BAD_ARGUMENT; + } + + CFE_MSG_GetSize(MsgPtr, &TotalMsgSize); + HdrSize = CFE_SB_MsgHdrSize(MsgPtr); + + return TotalMsgSize - HdrSize; +} /* end CFE_SB_GetUserDataLength */ + +/* + * Function: CFE_SB_SetUserDataLength - See API and header file for details + */ +void CFE_SB_SetUserDataLength(CFE_MSG_Message_t *MsgPtr, size_t DataLength) +{ + CFE_MSG_Size_t TotalMsgSize; + size_t HdrSize; + + if (MsgPtr == NULL) + { + CFE_ES_WriteToSysLog("CFE_SB:SetUserDataLength-Failed invalid arguments\n"); + } + else + { + HdrSize = CFE_SB_MsgHdrSize(MsgPtr); + TotalMsgSize = HdrSize + DataLength; + + if (TotalMsgSize <= CFE_MISSION_SB_MAX_SB_MSG_SIZE) + { + CFE_MSG_SetSize(MsgPtr, TotalMsgSize); + } + else + { + CFE_ES_WriteToSysLog("CFE_SB:SetUserDataLength-Failed TotalMsgSize too large\n"); + } + } +} /* end CFE_SB_SetUserDataLength */ + +/* + * Function: CFE_SB_TimeStampMsg - See API and header file for details + */ +void CFE_SB_TimeStampMsg(CFE_MSG_Message_t *MsgPtr) +{ + CFE_MSG_SetMsgTime(MsgPtr, CFE_TIME_GetTime()); + +} /* end CFE_SB_TimeStampMsg */ + +/* + * Function: CFE_SB_MessageStringGet - See API and header file for details + */ +int32 CFE_SB_MessageStringGet(char *DestStringPtr, const char *SourceStringPtr, const char *DefaultString, + size_t DestMaxSize, size_t SourceMaxSize) +{ + int32 Result; + + /* + * Error in caller if DestMaxSize == 0. + * Cannot terminate the string, since there is no place for the NUL + * In this case, do nothing + */ + if (DestMaxSize == 0 || DestStringPtr == NULL) + { + Result = CFE_SB_BAD_ARGUMENT; + } + else + { + Result = 0; + + /* + * Check if should use the default, which is if + * the source string has zero length (first char is NUL). + */ + if (DefaultString != NULL && (SourceMaxSize == 0 || *SourceStringPtr == 0)) + { + SourceStringPtr = DefaultString; + SourceMaxSize = DestMaxSize; + } + + /* Reserve 1 character for the required NUL */ + --DestMaxSize; + + while (SourceMaxSize > 0 && *SourceStringPtr != 0 && DestMaxSize > 0) + { + *DestStringPtr = *SourceStringPtr; + ++DestStringPtr; + ++SourceStringPtr; + --SourceMaxSize; + --DestMaxSize; + + ++Result; + } + + /* Put the NUL in the last character */ + *DestStringPtr = 0; + } + + return Result; +} + +/* + * Function: CFE_SB_MessageStringSet - See API and header file for details + */ +int32 CFE_SB_MessageStringSet(char *DestStringPtr, const char *SourceStringPtr, size_t DestMaxSize, + size_t SourceMaxSize) +{ + int32 Result; + + if (SourceStringPtr == NULL || DestStringPtr == NULL) + { + Result = CFE_SB_BAD_ARGUMENT; + } + else + { + Result = 0; + + while (SourceMaxSize > 0 && *SourceStringPtr != 0 && DestMaxSize > 0) + { + *DestStringPtr = *SourceStringPtr; + ++DestStringPtr; + ++SourceStringPtr; + ++Result; + --DestMaxSize; + --SourceMaxSize; + } + + /* + * Pad the remaining space with NUL chars, + * but this should NOT be included in the final size + */ + while (DestMaxSize > 0) + { + /* Put the NUL in the last character */ + *DestStringPtr = 0; + ++DestStringPtr; + --DestMaxSize; + } + } + + return Result; +} diff --git a/modules/sb/fsw/src/cfe_sb_verify.h b/modules/sb/fsw/src/cfe_sb_verify.h new file mode 100644 index 000000000..fd1221e3a --- /dev/null +++ b/modules/sb/fsw/src/cfe_sb_verify.h @@ -0,0 +1,167 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/** + * @file + * + * Purpose: + * This header file performs compile time checking for SB configuration + * parameters. + * + * Author: R.McGraw/SSI + * + */ + +#ifndef CFE_SB_VERIFY_H +#define CFE_SB_VERIFY_H + +#include + +#if CFE_PLATFORM_SB_MAX_MSG_IDS < 1 +#error CFE_PLATFORM_SB_MAX_MSG_IDS cannot be less than 1! +#endif + +#if CFE_PLATFORM_SB_MAX_PIPES < 1 +#error CFE_PLATFORM_SB_MAX_PIPES cannot be less than 1! +#endif + +#if CFE_PLATFORM_SB_MAX_PIPES > OS_MAX_QUEUES +#error CFE_PLATFORM_SB_MAX_PIPES cannot be greater than OS_MAX_QUEUES! +#endif + +#if CFE_PLATFORM_SB_MAX_DEST_PER_PKT < 1 +#error CFE_PLATFORM_SB_MAX_DEST_PER_PKT cannot be less than 1! +#endif + +#if CFE_PLATFORM_SB_HIGHEST_VALID_MSGID < 1 +#error CFE_PLATFORM_SB_HIGHEST_VALID_MSGID cannot be less than 1! +#endif + +#if CFE_PLATFORM_SB_HIGHEST_VALID_MSGID > 0xFFFFFFFE +#error CFE_PLATFORM_SB_HIGHEST_VALID_MSGID cannot be > 0xFFFFFFFE +#endif + +#if CFE_PLATFORM_SB_BUF_MEMORY_BYTES < 512 +#error CFE_PLATFORM_SB_BUF_MEMORY_BYTES cannot be less than 512 bytes! +#endif + +#if CFE_PLATFORM_SB_BUF_MEMORY_BYTES > UINT32_MAX +#error CFE_PLATFORM_SB_BUF_MEMORY_BYTES cannot be greater than UINT32_MAX (4 Gigabytes)! +#endif + +/* + * Legacy time formats no longer supported in core cFE, this will pass + * if default is selected or if both defines are removed + */ +#if (CFE_MISSION_SB_PACKET_TIME_FORMAT != CFE_MISSION_SB_TIME_32_16_SUBS) +#error Legacy CFE_MISSION_SB_PACKET_TIME_FORMAT implementations no longer supported in core +#endif + +#if CFE_MISSION_SB_MAX_SB_MSG_SIZE < 6 +#error CFE_MISSION_SB_MAX_SB_MSG_SIZE cannot be less than 6 (CCSDS Primary Hdr Size)! +#endif + +/* +** SB Memory Pool Block Sizes +*/ +#if CFE_PLATFORM_SB_MAX_BLOCK_SIZE < CFE_MISSION_SB_MAX_SB_MSG_SIZE +#error CFE_PLATFORM_SB_MAX_BLOCK_SIZE must be > or = to CFE_MISSION_SB_MAX_SB_MSG_SIZE! +#endif + +#if CFE_PLATFORM_SB_MEM_BLOCK_SIZE_01 > CFE_PLATFORM_SB_MEM_BLOCK_SIZE_02 +#error CFE_PLATFORM_SB_MEM_BLOCK_SIZE_01 must be less than CFE_PLATFORM_SB_MEM_BLOCK_SIZE_02 +#endif + +#if CFE_PLATFORM_SB_MEM_BLOCK_SIZE_02 > CFE_PLATFORM_SB_MEM_BLOCK_SIZE_03 +#error CFE_PLATFORM_SB_MEM_BLOCK_SIZE_02 must be less than CFE_PLATFORM_SB_MEM_BLOCK_SIZE_03 +#endif + +#if CFE_PLATFORM_SB_MEM_BLOCK_SIZE_03 > CFE_PLATFORM_SB_MEM_BLOCK_SIZE_04 +#error CFE_PLATFORM_SB_MEM_BLOCK_SIZE_03 must be less than CFE_PLATFORM_SB_MEM_BLOCK_SIZE_04 +#endif + +#if CFE_PLATFORM_SB_MEM_BLOCK_SIZE_04 > CFE_PLATFORM_SB_MEM_BLOCK_SIZE_05 +#error CFE_PLATFORM_SB_MEM_BLOCK_SIZE_04 must be less than CFE_PLATFORM_SB_MEM_BLOCK_SIZE_05 +#endif + +#if CFE_PLATFORM_SB_MEM_BLOCK_SIZE_05 > CFE_PLATFORM_SB_MEM_BLOCK_SIZE_06 +#error CFE_PLATFORM_SB_MEM_BLOCK_SIZE_05 must be less than CFE_PLATFORM_SB_MEM_BLOCK_SIZE_06 +#endif + +#if CFE_PLATFORM_SB_MEM_BLOCK_SIZE_06 > CFE_PLATFORM_SB_MEM_BLOCK_SIZE_07 +#error CFE_PLATFORM_SB_MEM_BLOCK_SIZE_06 must be less than CFE_PLATFORM_SB_MEM_BLOCK_SIZE_07 +#endif + +#if CFE_PLATFORM_SB_MEM_BLOCK_SIZE_07 > CFE_PLATFORM_SB_MEM_BLOCK_SIZE_08 +#error CFE_PLATFORM_SB_MEM_BLOCK_SIZE_07 must be less than CFE_PLATFORM_SB_MEM_BLOCK_SIZE_08 +#endif + +#if CFE_PLATFORM_SB_MEM_BLOCK_SIZE_08 > CFE_PLATFORM_SB_MEM_BLOCK_SIZE_09 +#error CFE_PLATFORM_SB_MEM_BLOCK_SIZE_08 must be less than CFE_PLATFORM_SB_MEM_BLOCK_SIZE_09 +#endif + +#if CFE_PLATFORM_SB_MEM_BLOCK_SIZE_09 > CFE_PLATFORM_SB_MEM_BLOCK_SIZE_10 +#error CFE_PLATFORM_SB_MEM_BLOCK_SIZE_09 must be less than CFE_PLATFORM_SB_MEM_BLOCK_SIZE_10 +#endif + +#if CFE_PLATFORM_SB_MEM_BLOCK_SIZE_10 > CFE_PLATFORM_SB_MEM_BLOCK_SIZE_11 +#error CFE_PLATFORM_SB_MEM_BLOCK_SIZE_10 must be less than CFE_PLATFORM_SB_MEM_BLOCK_SIZE_11 +#endif + +#if CFE_PLATFORM_SB_MEM_BLOCK_SIZE_11 > CFE_PLATFORM_SB_MEM_BLOCK_SIZE_12 +#error CFE_PLATFORM_SB_MEM_BLOCK_SIZE_11 must be less than CFE_PLATFORM_SB_MEM_BLOCK_SIZE_12 +#endif + +#if CFE_PLATFORM_SB_MEM_BLOCK_SIZE_12 > CFE_PLATFORM_SB_MEM_BLOCK_SIZE_13 +#error CFE_PLATFORM_SB_MEM_BLOCK_SIZE_12 must be less than CFE_PLATFORM_SB_MEM_BLOCK_SIZE_13 +#endif + +#if CFE_PLATFORM_SB_MEM_BLOCK_SIZE_13 > CFE_PLATFORM_SB_MEM_BLOCK_SIZE_14 +#error CFE_PLATFORM_SB_MEM_BLOCK_SIZE_13 must be less than CFE_PLATFORM_SB_MEM_BLOCK_SIZE_14 +#endif + +#if CFE_PLATFORM_SB_MEM_BLOCK_SIZE_14 > CFE_PLATFORM_SB_MEM_BLOCK_SIZE_15 +#error CFE_PLATFORM_SB_MEM_BLOCK_SIZE_14 must be less than CFE_PLATFORM_SB_MEM_BLOCK_SIZE_15 +#endif + +#if CFE_PLATFORM_SB_MEM_BLOCK_SIZE_15 > CFE_PLATFORM_SB_MEM_BLOCK_SIZE_16 +#error CFE_PLATFORM_SB_MEM_BLOCK_SIZE_15 must be less than CFE_PLATFORM_SB_MEM_BLOCK_SIZE_16 +#endif + +#if CFE_PLATFORM_SB_MEM_BLOCK_SIZE_16 >= CFE_PLATFORM_SB_MAX_BLOCK_SIZE +#error CFE_PLATFORM_SB_MEM_BLOCK_SIZE_16 must be less than CFE_PLATFORM_SB_MAX_BLOCK_SIZE +#endif + +#if CFE_PLATFORM_SB_DEFAULT_MSG_LIMIT < 4 +#error CFE_PLATFORM_SB_DEFAULT_MSG_LIMIT cannot be less than 4! +#endif + +#if CFE_PLATFORM_SB_DEFAULT_MSG_LIMIT > 65535 +#error CFE_PLATFORM_SB_DEFAULT_MSG_LIMIT cannot be greater than 65535! +#endif + +/* +** Validate task stack size... +*/ +#if CFE_PLATFORM_SB_START_TASK_STACK_SIZE < 2048 +#error CFE_PLATFORM_SB_START_TASK_STACK_SIZE must be greater than or equal to 2048 +#endif + +#endif /* CFE_SB_VERIFY_H */ diff --git a/modules/sbr/CMakeLists.txt b/modules/sbr/CMakeLists.txt new file mode 100644 index 000000000..a8a564a7a --- /dev/null +++ b/modules/sbr/CMakeLists.txt @@ -0,0 +1,47 @@ +################################################################## +# +# cFE software bus routing module CMake build recipe +# +# This CMakeLists.txt adds source files for the +# SBR module included in the cFE distribution. Selected +# files are built into a static library that in turn +# is linked into the final executable. +# +# Note this is different than applications which are dynamically +# linked to support runtime loading. The core applications all +# use static linkage. +# +################################################################## + +project(CFE_SBR C) + +if (NOT MISSION_MSGMAP_IMPLEMENTATION) + set(MISSION_MSGMAP_IMPLEMENTATION "DIRECT") +endif (NOT MISSION_MSGMAP_IMPLEMENTATION) + +if (MISSION_MSGMAP_IMPLEMENTATION STREQUAL "DIRECT") + message(STATUS "Using direct map software bus routing implementation") + set(${DEP}_SRC + ${CMAKE_CURRENT_SOURCE_DIR}/fsw/src/cfe_sbr_map_direct.c + ${CMAKE_CURRENT_SOURCE_DIR}/fsw/src/cfe_sbr_route_unsorted.c) +elseif (MISSION_MSGMAP_IMPLEMENTATION STREQUAL "HASH") + message(STATUS "Using hashed map software bus routing implementation") + set(${DEP}_SRC + ${CMAKE_CURRENT_SOURCE_DIR}/fsw/src/cfe_sbr_map_hash.c + ${CMAKE_CURRENT_SOURCE_DIR}/fsw/src/cfe_sbr_route_unsorted.c) +else() + message(ERROR "Invalid software bush routing implementation selected:" MISSION_MSGMAP_IMPLEMENTATION) +endif() + +# Module library +add_library(${DEP} STATIC ${${DEP}_SRC}) + +# Add private include +target_include_directories(${DEP} PRIVATE private_inc) +target_link_libraries(sbr PRIVATE core_private) + +# Add unit test coverage subdirectory +if(ENABLE_UNIT_TESTS) + add_subdirectory(ut-coverage) +endif(ENABLE_UNIT_TESTS) + diff --git a/modules/sbr/fsw/src/cfe_sbr_map_direct.c b/modules/sbr/fsw/src/cfe_sbr_map_direct.c new file mode 100644 index 000000000..8aab2d0fa --- /dev/null +++ b/modules/sbr/fsw/src/cfe_sbr_map_direct.c @@ -0,0 +1,95 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/****************************************************************************** + * Direct routing map implementation + * + * Notes: + * These functions manipulate/access global variables and need + * to be protected by the SB Shared data lock. + * + */ + +/* + * Include Files + */ + +#include "common_types.h" +#include "cfe_sbr.h" +#include "cfe_sbr_priv.h" +#include + +#include "cfe_sb.h" + +/* + * Macro Definitions + */ + +/** + * \brief Message map size + * + * For direct mapping, map size is maximum valid MsgId value + 1 (since MsgId 0 is valid) + */ +#define CFE_SBR_MSG_MAP_SIZE (CFE_PLATFORM_SB_HIGHEST_VALID_MSGID + 1) + +/****************************************************************************** + * Shared data + */ + +/** \brief Message map shared data */ +CFE_SBR_RouteId_t CFE_SBR_MSGMAP[CFE_SBR_MSG_MAP_SIZE]; + +/****************************************************************************** + * Interface function - see header for description + */ +void CFE_SBR_Init_Map(void) +{ + /* Clear the shared data */ + memset(&CFE_SBR_MSGMAP, 0, sizeof(CFE_SBR_MSGMAP)); +} + +/****************************************************************************** + * Interface function - see header for description + */ +uint32 CFE_SBR_SetRouteId(CFE_SB_MsgId_t MsgId, CFE_SBR_RouteId_t RouteId) +{ + if (CFE_SB_IsValidMsgId(MsgId)) + { + CFE_SBR_MSGMAP[CFE_SB_MsgIdToValue(MsgId)] = RouteId; + } + + /* Direct lookup never collides, always return 0 */ + return 0; +} + +/****************************************************************************** + * Interface function - see API for description + */ +CFE_SBR_RouteId_t CFE_SBR_GetRouteId(CFE_SB_MsgId_t MsgId) +{ + CFE_SBR_RouteId_t routeid = CFE_SBR_INVALID_ROUTE_ID; + + if (CFE_SB_IsValidMsgId(MsgId)) + { + routeid = CFE_SBR_MSGMAP[CFE_SB_MsgIdToValue(MsgId)]; + } + + return routeid; +} diff --git a/modules/sbr/fsw/src/cfe_sbr_map_hash.c b/modules/sbr/fsw/src/cfe_sbr_map_hash.c new file mode 100644 index 000000000..44a85dc8e --- /dev/null +++ b/modules/sbr/fsw/src/cfe_sbr_map_hash.c @@ -0,0 +1,165 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/****************************************************************************** + * Hash routing map implementation + * + * Notes: + * These functions manipulate/access global variables and need + * to be protected by the SB Shared data lock. + * + */ + +/* + * Include Files + */ + +#include "common_types.h" +#include "cfe_sbr.h" +#include "cfe_sbr_priv.h" +#include "cfe_sb.h" + +#include +#include + +/* + * Macro Definitions + */ + +/** + * \brief Message map size + * + * For hash mapping, map size is a multiple of maximum number of routes. + * The multiple impacts the number of collisions when the routes fill up. + * 4 was initially chosen to provide for plenty of holes in the map, while + * still remaining much smaller than the routing table. Note the + * multiple must be a factor of 2 to use the efficient shift logic, and + * can't be bigger than what can be indexed by CFE_SB_MsgId_Atom_t + */ +#define CFE_SBR_MSG_MAP_SIZE (4 * CFE_PLATFORM_SB_MAX_MSG_IDS) + +/* Verify power of two */ +#if ((CFE_SBR_MSG_MAP_SIZE & (CFE_SBR_MSG_MAP_SIZE - 1)) != 0) +#error CFE_SBR_MSG_MAP_SIZE must be a power of 2 for hash algorithm to work +#endif + +/** \brief Hash algorithm magic number + * + * Ref: + * https://stackoverflow.com/questions/664014/what-integer-hash-function-are-good-that-accepts-an-integer-hash-key/12996028#12996028 + */ +#define CFE_SBR_HASH_MAGIC (0x45d9f3b) + +/****************************************************************************** + * Shared data + */ + +/** \brief Message map shared data */ +CFE_SBR_RouteId_t CFE_SBR_MSGMAP[CFE_SBR_MSG_MAP_SIZE]; + +/****************************************************************************** + * Internal helper function to hash the message id + * + * Note: algorithm designed for a 32 bit int, changing the size of + * CFE_SB_MsgId_Atom_t may require an update to this impelementation + */ +CFE_SB_MsgId_Atom_t CFE_SBR_MsgIdHash(CFE_SB_MsgId_t MsgId) +{ + CFE_SB_MsgId_Atom_t hash; + + hash = CFE_SB_MsgIdToValue(MsgId); + + hash = ((hash >> 16) ^ hash) * CFE_SBR_HASH_MAGIC; + hash = ((hash >> 16) ^ hash) * CFE_SBR_HASH_MAGIC; + hash = (hash >> 16) ^ hash; + + /* Reduce to fit in map */ + hash &= CFE_SBR_MSG_MAP_SIZE - 1; + + return hash; +} + +/****************************************************************************** + * Interface function - see header for description + */ +void CFE_SBR_Init_Map(void) +{ + /* Clear the shared data */ + memset(&CFE_SBR_MSGMAP, 0, sizeof(CFE_SBR_MSGMAP)); +} + +/****************************************************************************** + * Interface function - see header for description + */ +uint32 CFE_SBR_SetRouteId(CFE_SB_MsgId_t MsgId, CFE_SBR_RouteId_t RouteId) +{ + CFE_SB_MsgId_Atom_t hash; + uint32 collisions = 0; + + if (CFE_SB_IsValidMsgId(MsgId)) + { + hash = CFE_SBR_MsgIdHash(MsgId); + + /* + * Increment from original hash to find the next open slot. + * Since map is larger than possible routes this will + * never deadlock + */ + while (CFE_SBR_IsValidRouteId(CFE_SBR_MSGMAP[hash])) + { + /* Increment or loop to start of array */ + hash = (hash + 1) & (CFE_SBR_MSG_MAP_SIZE - 1); + collisions++; + } + + CFE_SBR_MSGMAP[hash] = RouteId; + } + + return collisions; +} + +/****************************************************************************** + * Interface function - see API for description + */ +CFE_SBR_RouteId_t CFE_SBR_GetRouteId(CFE_SB_MsgId_t MsgId) +{ + CFE_SB_MsgId_Atom_t hash; + CFE_SBR_RouteId_t routeid = CFE_SBR_INVALID_ROUTE_ID; + + if (CFE_SB_IsValidMsgId(MsgId)) + { + hash = CFE_SBR_MsgIdHash(MsgId); + routeid = CFE_SBR_MSGMAP[hash]; + + /* + * Increment from original hash to find matching route. + * Since map is larger than possible routes this will + * never deadlock + */ + while (CFE_SBR_IsValidRouteId(routeid) && !CFE_SB_MsgId_Equal(CFE_SBR_GetMsgId(routeid), MsgId)) + { + /* Increment or loop to start of array */ + hash = (hash + 1) & (CFE_SBR_MSG_MAP_SIZE - 1); + routeid = CFE_SBR_MSGMAP[hash]; + } + } + + return routeid; +} diff --git a/modules/sbr/fsw/src/cfe_sbr_priv.h b/modules/sbr/fsw/src/cfe_sbr_priv.h new file mode 100644 index 000000000..6f490fd28 --- /dev/null +++ b/modules/sbr/fsw/src/cfe_sbr_priv.h @@ -0,0 +1,61 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/** + * @file + * + * Prototypes for private functions and type definitions for SB + * routing internal use. + */ + +#ifndef CFE_SBR_PRIV_H +#define CFE_SBR_PRIV_H + +/* + * Includes + */ +#include "cfe_sbr.h" + +/****************************************************************************** + * Function prototypes + */ + +/** + * \brief Routing map initialization + */ +void CFE_SBR_Init_Map(void); + +/** + * \brief Associates the given route ID with the given message ID + * + * Used for implementations that use a mapping table (typically hash or direct) + * and need this information to later get the route id from the message id. + * + * \note Typically not needed for a search implementation. Assumes + * message ID is valid + * + * \param[in] MsgId Message id to associate with route id + * \param[in] RouteId Route id to associate with message id + * + * \returns Number of collisions + */ +uint32 CFE_SBR_SetRouteId(CFE_SB_MsgId_t MsgId, CFE_SBR_RouteId_t RouteId); + +#endif /* CFE_SBR_PRIV_H */ diff --git a/modules/sbr/fsw/src/cfe_sbr_route_unsorted.c b/modules/sbr/fsw/src/cfe_sbr_route_unsorted.c new file mode 100644 index 000000000..40404fc53 --- /dev/null +++ b/modules/sbr/fsw/src/cfe_sbr_route_unsorted.c @@ -0,0 +1,210 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/****************************************************************************** + * Purpose: + * Unsorted routing implemenation + * Used with route map implementations where order of routes doesn't matter + * + * Notes: + * These functions manipulate/access global variables and need + * to be protected by the SB Shared data lock. + */ + +/* + * Include Files + */ + +#include "common_types.h" +#include "cfe_sbr.h" +#include "cfe_sbr_priv.h" +#include + +#include "cfe_sb.h" + +/****************************************************************************** + * Type Definitions + */ + +/** \brief Routing table entry */ +typedef struct +{ + CFE_SB_DestinationD_t * ListHeadPtr; /**< \brief Destination list head */ + CFE_SB_MsgId_t MsgId; /**< \brief Message ID associated with route */ + CFE_MSG_SequenceCount_t SeqCnt; /**< \brief Message sequence counter */ +} CFE_SBR_RouteEntry_t; + +/** \brief Module data */ +typedef struct +{ + CFE_SBR_RouteEntry_t RoutingTbl[CFE_PLATFORM_SB_MAX_MSG_IDS]; /**< \brief Routing table */ + CFE_SB_RouteId_Atom_t RouteIdxTop; /**< \brief First unused entry in RoutingTbl */ +} cfe_sbr_route_data_t; + +/****************************************************************************** + * Shared data + */ + +/** \brief Routing module shared data */ +cfe_sbr_route_data_t CFE_SBR_RDATA; + +/****************************************************************************** + * Interface function - see API for description + */ +void CFE_SBR_Init(void) +{ + CFE_SB_RouteId_Atom_t routeidx; + + /* Clear the shared data */ + memset(&CFE_SBR_RDATA, 0, sizeof(CFE_SBR_RDATA)); + + /* Only non-zero value for shared data initialization is the invalid MsgId */ + for (routeidx = 0; routeidx < CFE_PLATFORM_SB_MAX_MSG_IDS; routeidx++) + { + CFE_SBR_RDATA.RoutingTbl[routeidx].MsgId = CFE_SB_INVALID_MSG_ID; + } + + /* Initialize map */ + CFE_SBR_Init_Map(); +} + +/****************************************************************************** + * Interface function - see API for description + */ +CFE_SBR_RouteId_t CFE_SBR_AddRoute(CFE_SB_MsgId_t MsgId, uint32 *CollisionsPtr) +{ + CFE_SBR_RouteId_t routeid = CFE_SBR_INVALID_ROUTE_ID; + uint32 collisions = 0; + + if (CFE_SB_IsValidMsgId(MsgId) && (CFE_SBR_RDATA.RouteIdxTop < CFE_PLATFORM_SB_MAX_MSG_IDS)) + { + routeid = CFE_SBR_ValueToRouteId(CFE_SBR_RDATA.RouteIdxTop); + collisions = CFE_SBR_SetRouteId(MsgId, routeid); + + CFE_SBR_RDATA.RoutingTbl[CFE_SBR_RDATA.RouteIdxTop].MsgId = MsgId; + CFE_SBR_RDATA.RouteIdxTop++; + } + + if (CollisionsPtr != NULL) + { + *CollisionsPtr = collisions; + } + + return routeid; +} + +/****************************************************************************** + * Interface function - see API for description + */ +CFE_SB_MsgId_t CFE_SBR_GetMsgId(CFE_SBR_RouteId_t RouteId) +{ + CFE_SB_MsgId_t msgid = CFE_SB_INVALID_MSG_ID; + + if (CFE_SBR_IsValidRouteId(RouteId)) + { + msgid = CFE_SBR_RDATA.RoutingTbl[CFE_SBR_RouteIdToValue(RouteId)].MsgId; + } + + return msgid; +} + +/****************************************************************************** + * Interface function - see API for description + */ +CFE_SB_DestinationD_t *CFE_SBR_GetDestListHeadPtr(CFE_SBR_RouteId_t RouteId) +{ + + CFE_SB_DestinationD_t *destptr = NULL; + + if (CFE_SBR_IsValidRouteId(RouteId)) + { + destptr = CFE_SBR_RDATA.RoutingTbl[CFE_SBR_RouteIdToValue(RouteId)].ListHeadPtr; + } + + return destptr; +} + +/****************************************************************************** + * Interface function - see API for description + */ +void CFE_SBR_SetDestListHeadPtr(CFE_SBR_RouteId_t RouteId, CFE_SB_DestinationD_t *DestPtr) +{ + + if (CFE_SBR_IsValidRouteId(RouteId)) + { + CFE_SBR_RDATA.RoutingTbl[CFE_SBR_RouteIdToValue(RouteId)].ListHeadPtr = DestPtr; + } +} + +/****************************************************************************** + * Interface function - see API for description + */ +void CFE_SBR_IncrementSequenceCounter(CFE_SBR_RouteId_t RouteId) +{ + if (CFE_SBR_IsValidRouteId(RouteId)) + { + CFE_SBR_RDATA.RoutingTbl[CFE_SBR_RouteIdToValue(RouteId)].SeqCnt++; + } +} + +/****************************************************************************** + * Interface function - see API for description + */ +CFE_MSG_SequenceCount_t CFE_SBR_GetSequenceCounter(CFE_SBR_RouteId_t RouteId) +{ + uint32 seqcnt = 0; + + if (CFE_SBR_IsValidRouteId(RouteId)) + { + seqcnt = CFE_SBR_RDATA.RoutingTbl[CFE_SBR_RouteIdToValue(RouteId)].SeqCnt; + } + + return seqcnt; +} + +/****************************************************************************** + * Interface function - see API for description + */ +void CFE_SBR_ForEachRouteId(CFE_SBR_CallbackPtr_t CallbackPtr, void *ArgPtr, CFE_SBR_Throttle_t *ThrottlePtr) +{ + CFE_SB_RouteId_Atom_t routeidx; + CFE_SB_RouteId_Atom_t startidx = 0; + CFE_SB_RouteId_Atom_t endidx = CFE_SBR_RDATA.RouteIdxTop; + + /* Update throttle settings if needed */ + if (ThrottlePtr != NULL) + { + startidx = ThrottlePtr->StartIndex; + + /* Return next index of zero if full range is processed */ + ThrottlePtr->NextIndex = 0; + + if ((startidx + ThrottlePtr->MaxLoop) < endidx) + { + endidx = startidx + ThrottlePtr->MaxLoop; + ThrottlePtr->NextIndex = endidx; + } + } + + for (routeidx = startidx; routeidx < endidx; routeidx++) + { + (*CallbackPtr)(CFE_SBR_ValueToRouteId(routeidx), ArgPtr); + } +}