From c0d3be764d66f2270d4fec1f691a3bb54d59a0f0 Mon Sep 17 00:00:00 2001 From: Soren Ptak Date: Tue, 1 Aug 2023 02:18:15 -0700 Subject: [PATCH] Fixing various typos and the like --- .github/workflows/test.yml | 36 +- formatting/goodFiles/source/core_mqtt.c | 3388 ++++++++++++++ formatting/goodFiles/source/tasks.c | 5532 +++++++++++++++++++++++ 3 files changed, 8939 insertions(+), 17 deletions(-) create mode 100644 formatting/goodFiles/source/core_mqtt.c create mode 100644 formatting/goodFiles/source/tasks.c diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b8250801..877303d4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -115,7 +115,8 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, windows-latest] + #os: [ubuntu-latest, windows-latest] + os: [ubuntu-latest] inputs: [ { @@ -198,12 +199,12 @@ jobs: steps: - uses: actions/checkout@v3 - - env: - stepName: Install Windows Build tools - name: ${{ env.stepName }} - if: runner.os == 'Windows' - id: install-windows-build-tools - uses: microsoft/setup-msbuild@v1.1 +# - env: +# stepName: Install Windows Build tools +# name: ${{ env.stepName }} +# if: runner.os == 'Windows' +# id: install-windows-build-tools + - env: stepName: Install Ubuntu Build Tools @@ -285,7 +286,8 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, windows-latest] + #os: [ubuntu-latest, windows-latest] + os: [ubuntu-latest] inputs: [ { @@ -362,15 +364,15 @@ jobs: retry-attempts: 2, }, ] - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v3 - - env: - stepName: Install Windows Build tools - name: ${{ env.stepName }} - if: runner.os == 'Windows' - id: install-windows-build-tools - uses: microsoft/setup-msbuild@v1.1 +# runs-on: ${{ matrix.os }} +# steps: +# - uses: actions/checkout@v3 +# - env: +# stepName: Install Windows Build tools +# name: ${{ env.stepName }} +# if: runner.os == 'Windows' +# id: install-windows-build-tools +# uses: setup-msbuild@v1.1 - env: stepName: Install Ubuntu Build Tools diff --git a/formatting/goodFiles/source/core_mqtt.c b/formatting/goodFiles/source/core_mqtt.c new file mode 100644 index 00000000..1efb3520 --- /dev/null +++ b/formatting/goodFiles/source/core_mqtt.c @@ -0,0 +1,3388 @@ +/* + * coreMQTT v2.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file core_mqtt.c + * @brief Implements the user-facing functions in core_mqtt.h. + */ +#include +#include + +#include "core_mqtt.h" +#include "core_mqtt_state.h" + +/* Include config defaults header to get default values of configs. */ +#include "core_mqtt_config_defaults.h" + +#include "core_mqtt_default_logging.h" + +#ifndef MQTT_PRE_SEND_HOOK + +/** + * @brief Hook called before a 'send' operation is executed. + */ + #define MQTT_PRE_SEND_HOOK( pContext ) +#endif /* !MQTT_PRE_SEND_HOOK */ + +#ifndef MQTT_POST_SEND_HOOK + +/** + * @brief Hook called after the 'send' operation is complete. + */ + #define MQTT_POST_SEND_HOOK( pContext ) +#endif /* !MQTT_POST_SEND_HOOK */ + +#ifndef MQTT_PRE_STATE_UPDATE_HOOK + +/** + * @brief Hook called just before an update to the MQTT state is made. + */ + #define MQTT_PRE_STATE_UPDATE_HOOK( pContext ) +#endif /* !MQTT_PRE_STATE_UPDATE_HOOK */ + +#ifndef MQTT_POST_STATE_UPDATE_HOOK + +/** + * @brief Hook called just after an update to the MQTT state has + * been made. + */ + #define MQTT_POST_STATE_UPDATE_HOOK( pContext ) +#endif /* !MQTT_POST_STATE_UPDATE_HOOK */ + +/** + * @brief Bytes required to encode any string length in an MQTT packet header. + * Length is always encoded in two bytes according to the MQTT specification. + */ +#define CORE_MQTT_SERIALIZED_LENGTH_FIELD_BYTES ( 2U ) + +/** + * @brief Number of vectors required to encode one topic filter in a subscribe + * request. Three vectors are required as there are three fields in the + * subscribe request namely: + * 1. Topic filter length; 2. Topic filter; and 3. QoS in this order. + */ +#define CORE_MQTT_SUBSCRIBE_PER_TOPIC_VECTOR_LENGTH ( 3U ) + +/** + * @brief Number of vectors required to encode one topic filter in an + * unsubscribe request. Two vectors are required as there are two fields in the + * unsubscribe request namely: + * 1. Topic filter length; and 2. Topic filter in this order. + */ +#define CORE_MQTT_UNSUBSCRIBE_PER_TOPIC_VECTOR_LENGTH ( 2U ) + +/*-----------------------------------------------------------*/ + +/** + * @brief Sends provided buffer to network using transport send. + * + * @brief param[in] pContext Initialized MQTT context. + * @brief param[in] pBufferToSend Buffer to be sent to network. + * @brief param[in] bytesToSend Number of bytes to be sent. + * + * @note This operation may call the transport send function + * repeatedly to send bytes over the network until either: + * 1. The requested number of bytes @a bytesToSend have been sent. + * OR + * 2. MQTT_SEND_TIMEOUT_MS milliseconds have gone by since entering this + * function. + * OR + * 3. There is an error in sending data over the network. + * + * @return Total number of bytes sent, or negative value on network error. + */ +static int32_t sendBuffer( MQTTContext_t * pContext, + const uint8_t * pBufferToSend, + size_t bytesToSend ); + +/** + * @brief Sends MQTT connect without copying the users data into any buffer. + * + * @brief param[in] pContext Initialized MQTT context. + * @brief param[in] pConnectInfo MQTT CONNECT packet information. + * @brief param[in] pWillInfo Last Will and Testament. Pass NULL if Last Will and + * Testament is not used. + * @brief param[in] remainingLength the length of the connect packet. + * + * @note This operation may call the transport send function + * repeatedly to send bytes over the network until either: + * 1. The requested number of bytes @a remainingLength have been sent. + * OR + * 2. MQTT_SEND_TIMEOUT_MS milliseconds have gone by since entering this + * function. + * OR + * 3. There is an error in sending data over the network. + * + * @return #MQTTSendFailed or #MQTTSuccess. + */ +static MQTTStatus_t sendConnectWithoutCopy( MQTTContext_t * pContext, + const MQTTConnectInfo_t * pConnectInfo, + const MQTTPublishInfo_t * pWillInfo, + size_t remainingLength ); + +/** + * @brief Sends the vector array passed through the parameters over the network. + * + * @note The preference is given to 'writev' function if it is present in the + * transport interface. Otherwise, a send call is made repeatedly to achieve the + * result. + * + * @param[in] pContext Initialized MQTT context. + * @param[in] pIoVec The vector array to be sent. + * @param[in] ioVecCount The number of elements in the array. + * + * @note This operation may call the transport send or writev functions + * repeatedly to send bytes over the network until either: + * 1. The requested number of bytes have been sent. + * OR + * 2. MQTT_SEND_TIMEOUT_MS milliseconds have gone by since entering this + * function. + * OR + * 3. There is an error in sending data over the network. + * + * @return The total number of bytes sent or the error code as received from the + * transport interface. + */ +static int32_t sendMessageVector( MQTTContext_t * pContext, + TransportOutVector_t * pIoVec, + size_t ioVecCount ); + +/** + * @brief Add a string and its length after serializing it in a manner outlined by + * the MQTT specification. + * + * @param[in] serializedLength Array of two bytes to which the vector will point. + * The array must remain in scope until the message has been sent. + * @param[in] string The string to be serialized. + * @param[in] length The length of the string to be serialized. + * @param[in] iterator The iterator pointing to the first element in the + * transport interface IO array. + * @param[out] updatedLength This parameter will be added to with the number of + * bytes added to the vector. + * + * @return The number of vectors added. + */ +static size_t addEncodedStringToVector( uint8_t serializedLength[ CORE_MQTT_SERIALIZED_LENGTH_FIELD_BYTES ], + const char * const string, + uint16_t length, + TransportOutVector_t * iterator, + size_t * updatedLength ); + +/** + * @brief Send MQTT SUBSCRIBE message without copying the user data into a buffer and + * directly sending it. + * + * @param[in] pContext Initialized MQTT context. + * @param[in] pSubscriptionList List of MQTT subscription info. + * @param[in] subscriptionCount The count of elements in the list. + * @param[in] packetId The packet ID of the subscribe packet + * @param[in] remainingLength The remaining length of the subscribe packet. + * + * @return #MQTTSuccess or #MQTTSendFailed. + */ +static MQTTStatus_t sendSubscribeWithoutCopy( MQTTContext_t * pContext, + const MQTTSubscribeInfo_t * pSubscriptionList, + size_t subscriptionCount, + uint16_t packetId, + size_t remainingLength ); + +/** + * @brief Send MQTT UNSUBSCRIBE message without copying the user data into a buffer and + * directly sending it. + * + * @param[in] pContext Initialized MQTT context. + * @param[in] pSubscriptionList MQTT subscription info. + * @param[in] subscriptionCount The count of elements in the list. + * @param[in] packetId The packet ID of the unsubscribe packet. + * @param[in] remainingLength The remaining length of the unsubscribe packet. + * + * @return #MQTTSuccess or #MQTTSendFailed. + */ +static MQTTStatus_t sendUnsubscribeWithoutCopy( MQTTContext_t * pContext, + const MQTTSubscribeInfo_t * pSubscriptionList, + size_t subscriptionCount, + uint16_t packetId, + size_t remainingLength ); + +/** + * @brief Calculate the interval between two millisecond timestamps, including + * when the later value has overflowed. + * + * @note In C, the operands are promoted to signed integers in subtraction. + * Using this function avoids the need to cast the result of subtractions back + * to uint32_t. + * + * @param[in] later The later time stamp, in milliseconds. + * @param[in] start The earlier time stamp, in milliseconds. + * + * @return later - start. + */ +static uint32_t calculateElapsedTime( uint32_t later, + uint32_t start ); + +/** + * @brief Convert a byte indicating a publish ack type to an #MQTTPubAckType_t. + * + * @param[in] packetType First byte of fixed header. + * + * @return Type of ack. + */ +static MQTTPubAckType_t getAckFromPacketType( uint8_t packetType ); + +/** + * @brief Receive bytes into the network buffer. + * + * @param[in] pContext Initialized MQTT Context. + * @param[in] bytesToRecv Number of bytes to receive. + * + * @note This operation calls the transport receive function + * repeatedly to read bytes from the network until either: + * 1. The requested number of bytes @a bytesToRecv are read. + * OR + * 2. No data is received from the network for MQTT_RECV_POLLING_TIMEOUT_MS duration. + * + * OR + * 3. There is an error in reading from the network. + * + * + * @return Number of bytes received, or negative number on network error. + */ +static int32_t recvExact( const MQTTContext_t * pContext, + size_t bytesToRecv ); + +/** + * @brief Discard a packet from the transport interface. + * + * @param[in] pContext MQTT Connection context. + * @param[in] remainingLength Remaining length of the packet to dump. + * @param[in] timeoutMs Time remaining to discard the packet. + * + * @return #MQTTRecvFailed or #MQTTNoDataAvailable. + */ +static MQTTStatus_t discardPacket( const MQTTContext_t * pContext, + size_t remainingLength, + uint32_t timeoutMs ); + +/** + * @brief Discard a packet from the MQTT buffer and the transport interface. + * + * @param[in] pContext MQTT Connection context. + * @param[in] pPacketInfo Information struct of the packet to be discarded. + * + * @return #MQTTRecvFailed or #MQTTNoDataAvailable. + */ +static MQTTStatus_t discardStoredPacket( MQTTContext_t * pContext, + const MQTTPacketInfo_t * pPacketInfo ); + +/** + * @brief Receive a packet from the transport interface. + * + * @param[in] pContext MQTT Connection context. + * @param[in] incomingPacket packet struct with remaining length. + * @param[in] remainingTimeMs Time remaining to receive the packet. + * + * @return #MQTTSuccess or #MQTTRecvFailed. + */ +static MQTTStatus_t receivePacket( const MQTTContext_t * pContext, + MQTTPacketInfo_t incomingPacket, + uint32_t remainingTimeMs ); + +/** + * @brief Get the correct ack type to send. + * + * @param[in] state Current state of publish. + * + * @return Packet Type byte of PUBACK, PUBREC, PUBREL, or PUBCOMP if one of + * those should be sent, else 0. + */ +static uint8_t getAckTypeToSend( MQTTPublishState_t state ); + +/** + * @brief Send acks for received QoS 1/2 publishes. + * + * @param[in] pContext MQTT Connection context. + * @param[in] packetId packet ID of original PUBLISH. + * @param[in] publishState Current publish state in record. + * + * @return #MQTTSuccess, #MQTTIllegalState or #MQTTSendFailed. + */ +static MQTTStatus_t sendPublishAcks( MQTTContext_t * pContext, + uint16_t packetId, + MQTTPublishState_t publishState ); + +/** + * @brief Send a keep alive PINGREQ if the keep alive interval has elapsed. + * + * @param[in] pContext Initialized MQTT Context. + * + * @return #MQTTKeepAliveTimeout if a PINGRESP is not received in time, + * #MQTTSendFailed if the PINGREQ cannot be sent, or #MQTTSuccess. + */ +static MQTTStatus_t handleKeepAlive( MQTTContext_t * pContext ); + +/** + * @brief Handle received MQTT PUBLISH packet. + * + * @param[in] pContext MQTT Connection context. + * @param[in] pIncomingPacket Incoming packet. + * + * @return MQTTSuccess, MQTTIllegalState or deserialization error. + */ +static MQTTStatus_t handleIncomingPublish( MQTTContext_t * pContext, + MQTTPacketInfo_t * pIncomingPacket ); + +/** + * @brief Handle received MQTT publish acks. + * + * @param[in] pContext MQTT Connection context. + * @param[in] pIncomingPacket Incoming packet. + * + * @return MQTTSuccess, MQTTIllegalState, or deserialization error. + */ +static MQTTStatus_t handlePublishAcks( MQTTContext_t * pContext, + MQTTPacketInfo_t * pIncomingPacket ); + +/** + * @brief Handle received MQTT ack. + * + * @param[in] pContext MQTT Connection context. + * @param[in] pIncomingPacket Incoming packet. + * @param[in] manageKeepAlive Flag indicating if PINGRESPs should not be given + * to the application + * + * @return MQTTSuccess, MQTTIllegalState, or deserialization error. + */ +static MQTTStatus_t handleIncomingAck( MQTTContext_t * pContext, + MQTTPacketInfo_t * pIncomingPacket, + bool manageKeepAlive ); + +/** + * @brief Run a single iteration of the receive loop. + * + * @param[in] pContext MQTT Connection context. + * @param[in] manageKeepAlive Flag indicating if keep alive should be handled. + * + * @return #MQTTRecvFailed if a network error occurs during reception; + * #MQTTSendFailed if a network error occurs while sending an ACK or PINGREQ; + * #MQTTBadResponse if an invalid packet is received; + * #MQTTKeepAliveTimeout if the server has not sent a PINGRESP before + * #MQTT_PINGRESP_TIMEOUT_MS milliseconds; + * #MQTTIllegalState if an incoming QoS 1/2 publish or ack causes an + * invalid transition for the internal state machine; + * #MQTTSuccess on success. + */ +static MQTTStatus_t receiveSingleIteration( MQTTContext_t * pContext, + bool manageKeepAlive ); + +/** + * @brief Validates parameters of #MQTT_Subscribe or #MQTT_Unsubscribe. + * + * @param[in] pContext Initialized MQTT context. + * @param[in] pSubscriptionList List of MQTT subscription info. + * @param[in] subscriptionCount The number of elements in pSubscriptionList. + * @param[in] packetId Packet identifier. + * + * @return #MQTTBadParameter if invalid parameters are passed; + * #MQTTSuccess otherwise. + */ +static MQTTStatus_t validateSubscribeUnsubscribeParams( const MQTTContext_t * pContext, + const MQTTSubscribeInfo_t * pSubscriptionList, + size_t subscriptionCount, + uint16_t packetId ); + +/** + * @brief Receives a CONNACK MQTT packet. + * + * @param[in] pContext Initialized MQTT context. + * @param[in] timeoutMs Timeout for waiting for CONNACK packet. + * @param[in] cleanSession Clean session flag set by application. + * @param[out] pIncomingPacket List of MQTT subscription info. + * @param[out] pSessionPresent Whether a previous session was present. + * Only relevant if not establishing a clean session. + * + * @return #MQTTBadResponse if a bad response is received; + * #MQTTNoDataAvailable if no data available for transport recv; + * ##MQTTRecvFailed if transport recv failed; + * #MQTTSuccess otherwise. + */ +static MQTTStatus_t receiveConnack( const MQTTContext_t * pContext, + uint32_t timeoutMs, + bool cleanSession, + MQTTPacketInfo_t * pIncomingPacket, + bool * pSessionPresent ); + +/** + * @brief Resends pending acks for a re-established MQTT session, or + * clears existing state records for a clean session. + * + * @param[in] pContext Initialized MQTT context. + * @param[in] sessionPresent Session present flag received from the MQTT broker. + * + * @return #MQTTSendFailed if transport send during resend failed; + * #MQTTSuccess otherwise. + */ +static MQTTStatus_t handleSessionResumption( MQTTContext_t * pContext, + bool sessionPresent ); + + +/** + * @brief Send the publish packet without copying the topic string and payload in + * the buffer. + * + * @brief param[in] pContext Initialized MQTT context. + * @brief param[in] pPublishInfo MQTT PUBLISH packet parameters. + * @brief param[in] pMqttHeader the serialized MQTT header with the header byte; + * the encoded length of the packet; and the encoded length of the topic string. + * @brief param[in] headerSize Size of the serialized PUBLISH header. + * @brief param[in] packetId Packet Id of the publish packet. + * + * @return #MQTTSendFailed if transport send during resend failed; + * #MQTTSuccess otherwise. + */ +static MQTTStatus_t sendPublishWithoutCopy( MQTTContext_t * pContext, + const MQTTPublishInfo_t * pPublishInfo, + const uint8_t * pMqttHeader, + size_t headerSize, + uint16_t packetId ); + +/** + * @brief Function to validate #MQTT_Publish parameters. + * + * @brief param[in] pContext Initialized MQTT context. + * @brief param[in] pPublishInfo MQTT PUBLISH packet parameters. + * @brief param[in] packetId Packet Id for the MQTT PUBLISH packet. + * + * @return #MQTTBadParameter if invalid parameters are passed; + * #MQTTSuccess otherwise. + */ +static MQTTStatus_t validatePublishParams( const MQTTContext_t * pContext, + const MQTTPublishInfo_t * pPublishInfo, + uint16_t packetId ); + +/** + * @brief Performs matching for special cases when a topic filter ends + * with a wildcard character. + * + * When the topic name has been consumed but there are remaining characters to + * to match in topic filter, this function handles the following 2 cases: + * - When the topic filter ends with "/+" or "/#" characters, but the topic + * name only ends with '/'. + * - When the topic filter ends with "/#" characters, but the topic name + * ends at the parent level. + * + * @note This function ASSUMES that the topic name been consumed in linear + * matching with the topic filer, but the topic filter has remaining characters + * to be matched. + * + * @param[in] pTopicFilter The topic filter containing the wildcard. + * @param[in] topicFilterLength Length of the topic filter being examined. + * @param[in] filterIndex Index of the topic filter being examined. + * + * @return Returns whether the topic filter and the topic name match. + */ +static bool matchEndWildcardsSpecialCases( const char * pTopicFilter, + uint16_t topicFilterLength, + uint16_t filterIndex ); + +/** + * @brief Attempt to match topic name with a topic filter starting with a wildcard. + * + * If the topic filter starts with a '+' (single-level) wildcard, the function + * advances the @a pNameIndex by a level in the topic name. + * If the topic filter starts with a '#' (multi-level) wildcard, the function + * concludes that both the topic name and topic filter match. + * + * @param[in] pTopicName The topic name to match. + * @param[in] topicNameLength Length of the topic name. + * @param[in] pTopicFilter The topic filter to match. + * @param[in] topicFilterLength Length of the topic filter. + * @param[in,out] pNameIndex Current index in the topic name being examined. It is + * advanced by one level for `+` wildcards. + * @param[in, out] pFilterIndex Current index in the topic filter being examined. + * It is advanced to position of '/' level separator for '+' wildcard. + * @param[out] pMatch Whether the topic filter and topic name match. + * + * @return `true` if the caller of this function should exit; `false` if the + * caller should continue parsing the topics. + */ +static bool matchWildcards( const char * pTopicName, + uint16_t topicNameLength, + const char * pTopicFilter, + uint16_t topicFilterLength, + uint16_t * pNameIndex, + uint16_t * pFilterIndex, + bool * pMatch ); + +/** + * @brief Match a topic name and topic filter allowing the use of wildcards. + * + * @param[in] pTopicName The topic name to check. + * @param[in] topicNameLength Length of the topic name. + * @param[in] pTopicFilter The topic filter to check. + * @param[in] topicFilterLength Length of topic filter. + * + * @return `true` if the topic name and topic filter match; `false` otherwise. + */ +static bool matchTopicFilter( const char * pTopicName, + uint16_t topicNameLength, + const char * pTopicFilter, + uint16_t topicFilterLength ); + +/*-----------------------------------------------------------*/ + +static bool matchEndWildcardsSpecialCases( const char * pTopicFilter, + uint16_t topicFilterLength, + uint16_t filterIndex ) +{ + bool matchFound = false; + + assert( pTopicFilter != NULL ); + assert( topicFilterLength != 0U ); + + /* Check if the topic filter has 2 remaining characters and it ends in + * "/#". This check handles the case to match filter "sport/#" with topic + * "sport". The reason is that the '#' wildcard represents the parent and + * any number of child levels in the topic name.*/ + if( ( topicFilterLength >= 3U ) && + ( filterIndex == ( topicFilterLength - 3U ) ) && + ( pTopicFilter[ filterIndex + 1U ] == '/' ) && + ( pTopicFilter[ filterIndex + 2U ] == '#' ) ) + + { + matchFound = true; + } + + /* Check if the next character is "#" or "+" and the topic filter ends in + * "/#" or "/+". This check handles the cases to match: + * + * - Topic filter "sport/+" with topic "sport/". + * - Topic filter "sport/#" with topic "sport/". + */ + if( ( filterIndex == ( topicFilterLength - 2U ) ) && + ( pTopicFilter[ filterIndex ] == '/' ) ) + { + /* Check that the last character is a wildcard. */ + matchFound = ( pTopicFilter[ filterIndex + 1U ] == '+' ) || + ( pTopicFilter[ filterIndex + 1U ] == '#' ); + } + + return matchFound; +} + +/*-----------------------------------------------------------*/ + +static bool matchWildcards( const char * pTopicName, + uint16_t topicNameLength, + const char * pTopicFilter, + uint16_t topicFilterLength, + uint16_t * pNameIndex, + uint16_t * pFilterIndex, + bool * pMatch ) +{ + bool shouldStopMatching = false; + bool locationIsValidForWildcard; + + assert( pTopicName != NULL ); + assert( topicNameLength != 0U ); + assert( pTopicFilter != NULL ); + assert( topicFilterLength != 0U ); + assert( pNameIndex != NULL ); + assert( pFilterIndex != NULL ); + assert( pMatch != NULL ); + + /* Wild card in a topic filter is only valid either at the starting position + * or when it is preceded by a '/'.*/ + locationIsValidForWildcard = ( *pFilterIndex == 0u ) || + ( pTopicFilter[ *pFilterIndex - 1U ] == '/' ); + + if( ( pTopicFilter[ *pFilterIndex ] == '+' ) && ( locationIsValidForWildcard == true ) ) + { + bool nextLevelExistsInTopicName = false; + bool nextLevelExistsinTopicFilter = false; + + /* Move topic name index to the end of the current level. The end of the + * current level is identified by the last character before the next level + * separator '/'. */ + while( *pNameIndex < topicNameLength ) + { + /* Exit the loop if we hit the level separator. */ + if( pTopicName[ *pNameIndex ] == '/' ) + { + nextLevelExistsInTopicName = true; + break; + } + + ( *pNameIndex )++; + } + + /* Determine if the topic filter contains a child level after the current level + * represented by the '+' wildcard. */ + if( ( *pFilterIndex < ( topicFilterLength - 1U ) ) && + ( pTopicFilter[ *pFilterIndex + 1U ] == '/' ) ) + { + nextLevelExistsinTopicFilter = true; + } + + /* If the topic name contains a child level but the topic filter ends at + * the current level, then there does not exist a match. */ + if( ( nextLevelExistsInTopicName == true ) && + ( nextLevelExistsinTopicFilter == false ) ) + { + *pMatch = false; + shouldStopMatching = true; + } + + /* If the topic name and topic filter have child levels, then advance the + * filter index to the level separator in the topic filter, so that match + * can be performed in the next level. + * Note: The name index already points to the level separator in the topic + * name. */ + else if( nextLevelExistsInTopicName == true ) + { + ( *pFilterIndex )++; + } + else + { + /* If we have reached here, the the loop terminated on the + * ( *pNameIndex < topicNameLength) condition, which means that have + * reached past the end of the topic name, and thus, we decrement the + * index to the last character in the topic name.*/ + ( *pNameIndex )--; + } + } + + /* '#' matches everything remaining in the topic name. It must be the + * last character in a topic filter. */ + else if( ( pTopicFilter[ *pFilterIndex ] == '#' ) && + ( *pFilterIndex == ( topicFilterLength - 1U ) ) && + ( locationIsValidForWildcard == true ) ) + { + /* Subsequent characters don't need to be checked for the + * multi-level wildcard. */ + *pMatch = true; + shouldStopMatching = true; + } + else + { + /* Any character mismatch other than '+' or '#' means the topic + * name does not match the topic filter. */ + *pMatch = false; + shouldStopMatching = true; + } + + return shouldStopMatching; +} + +/*-----------------------------------------------------------*/ + +static bool matchTopicFilter( const char * pTopicName, + uint16_t topicNameLength, + const char * pTopicFilter, + uint16_t topicFilterLength ) +{ + bool matchFound = false, shouldStopMatching = false; + uint16_t nameIndex = 0, filterIndex = 0; + + assert( pTopicName != NULL ); + assert( topicNameLength != 0 ); + assert( pTopicFilter != NULL ); + assert( topicFilterLength != 0 ); + + while( ( nameIndex < topicNameLength ) && ( filterIndex < topicFilterLength ) ) + { + /* Check if the character in the topic name matches the corresponding + * character in the topic filter string. */ + if( pTopicName[ nameIndex ] == pTopicFilter[ filterIndex ] ) + { + /* If the topic name has been consumed but the topic filter has not + * been consumed, match for special cases when the topic filter ends + * with wildcard character. */ + if( nameIndex == ( topicNameLength - 1U ) ) + { + matchFound = matchEndWildcardsSpecialCases( pTopicFilter, + topicFilterLength, + filterIndex ); + } + } + else + { + /* Check for matching wildcards. */ + shouldStopMatching = matchWildcards( pTopicName, + topicNameLength, + pTopicFilter, + topicFilterLength, + &nameIndex, + &filterIndex, + &matchFound ); + } + + if( ( matchFound == true ) || ( shouldStopMatching == true ) ) + { + break; + } + + /* Increment indexes. */ + nameIndex++; + filterIndex++; + } + + if( matchFound == false ) + { + /* If the end of both strings has been reached, they match. This represents the + * case when the topic filter contains the '+' wildcard at a non-starting position. + * For example, when matching either of "sport/+/player" OR "sport/hockey/+" topic + * filters with "sport/hockey/player" topic name. */ + matchFound = ( nameIndex == topicNameLength ) && + ( filterIndex == topicFilterLength ); + } + + return matchFound; +} + +/*-----------------------------------------------------------*/ + +static int32_t sendMessageVector( MQTTContext_t * pContext, + TransportOutVector_t * pIoVec, + size_t ioVecCount ) +{ + int32_t sendResult; + uint32_t startTime; + TransportOutVector_t * pIoVectIterator; + size_t vectorsToBeSent = ioVecCount; + size_t bytesToSend = 0U; + int32_t bytesSentOrError = 0; + + assert( pContext != NULL ); + assert( pIoVec != NULL ); + assert( pContext->getTime != NULL ); + /* Send must always be defined */ + assert( pContext->transportInterface.send != NULL ); + + /* Count the total number of bytes to be sent as outlined in the vector. */ + for( pIoVectIterator = pIoVec; pIoVectIterator <= &( pIoVec[ ioVecCount - 1U ] ); pIoVectIterator++ ) + { + bytesToSend += pIoVectIterator->iov_len; + } + + /* Reset the iterator to point to the first entry in the array. */ + pIoVectIterator = pIoVec; + + /* Note the start time. */ + startTime = pContext->getTime(); + + while( ( bytesSentOrError < ( int32_t ) bytesToSend ) && ( bytesSentOrError >= 0 ) ) + { + if( pContext->transportInterface.writev != NULL ) + { + sendResult = pContext->transportInterface.writev( pContext->transportInterface.pNetworkContext, + pIoVectIterator, + vectorsToBeSent ); + } + else + { + sendResult = pContext->transportInterface.send( pContext->transportInterface.pNetworkContext, + pIoVectIterator->iov_base, + pIoVectIterator->iov_len ); + } + + if( sendResult > 0 ) + { + /* It is a bug in the application's transport send implementation if + * more bytes than expected are sent. */ + assert( sendResult <= ( ( int32_t ) bytesToSend - bytesSentOrError ) ); + + bytesSentOrError += sendResult; + + /* Set last transmission time. */ + pContext->lastPacketTxTime = pContext->getTime(); + + LogDebug( ( "sendMessageVector: Bytes Sent=%ld, Bytes Remaining=%lu", + ( long int ) sendResult, + ( unsigned long ) ( bytesToSend - ( size_t ) bytesSentOrError ) ) ); + } + else if( sendResult < 0 ) + { + bytesSentOrError = sendResult; + LogError( ( "sendMessageVector: Unable to send packet: Network Error." ) ); + } + else + { + /* MISRA Empty body */ + } + + /* Check for timeout. */ + if( calculateElapsedTime( pContext->getTime(), startTime ) > MQTT_SEND_TIMEOUT_MS ) + { + LogError( ( "sendMessageVector: Unable to send packet: Timed out." ) ); + break; + } + + /* Update the send pointer to the correct vector and offset. */ + while( ( pIoVectIterator <= &( pIoVec[ ioVecCount - 1U ] ) ) && + ( sendResult >= ( int32_t ) pIoVectIterator->iov_len ) ) + { + sendResult -= ( int32_t ) pIoVectIterator->iov_len; + pIoVectIterator++; + /* Update the number of vector which are yet to be sent. */ + vectorsToBeSent--; + } + + /* Some of the bytes from this vector were sent as well, update the length + * and the pointer to data in this vector. */ + if( ( sendResult > 0 ) && + ( pIoVectIterator <= &( pIoVec[ ioVecCount - 1U ] ) ) ) + { + pIoVectIterator->iov_base = ( const void * ) &( ( ( const uint8_t * ) pIoVectIterator->iov_base )[ sendResult ] ); + pIoVectIterator->iov_len -= ( size_t ) sendResult; + } + } + + return bytesSentOrError; +} + +static int32_t sendBuffer( MQTTContext_t * pContext, + const uint8_t * pBufferToSend, + size_t bytesToSend ) +{ + int32_t sendResult; + uint32_t timeoutMs; + int32_t bytesSentOrError = 0; + const uint8_t * pIndex = pBufferToSend; + + assert( pContext != NULL ); + assert( pContext->getTime != NULL ); + assert( pContext->transportInterface.send != NULL ); + assert( pIndex != NULL ); + + /* Set the timeout. */ + timeoutMs = pContext->getTime() + MQTT_SEND_TIMEOUT_MS; + + while( ( bytesSentOrError < ( int32_t ) bytesToSend ) && ( bytesSentOrError >= 0 ) ) + { + sendResult = pContext->transportInterface.send( pContext->transportInterface.pNetworkContext, + pIndex, + bytesToSend - ( size_t ) bytesSentOrError ); + + if( sendResult > 0 ) + { + /* It is a bug in the application's transport send implementation if + * more bytes than expected are sent. */ + assert( sendResult <= ( ( int32_t ) bytesToSend - bytesSentOrError ) ); + + bytesSentOrError += sendResult; + pIndex = &pIndex[ sendResult ]; + + /* Set last transmission time. */ + pContext->lastPacketTxTime = pContext->getTime(); + + LogDebug( ( "sendBuffer: Bytes Sent=%ld, Bytes Remaining=%lu", + ( long int ) sendResult, + ( unsigned long ) ( bytesToSend - ( size_t ) bytesSentOrError ) ) ); + } + else if( sendResult < 0 ) + { + bytesSentOrError = sendResult; + LogError( ( "sendBuffer: Unable to send packet: Network Error." ) ); + } + else + { + /* MISRA Empty body */ + } + + /* Check for timeout. */ + if( pContext->getTime() >= timeoutMs ) + { + LogError( ( "sendBuffer: Unable to send packet: Timed out." ) ); + break; + } + } + + return bytesSentOrError; +} + +/*-----------------------------------------------------------*/ + +static uint32_t calculateElapsedTime( uint32_t later, + uint32_t start ) +{ + return later - start; +} + +/*-----------------------------------------------------------*/ + +static MQTTPubAckType_t getAckFromPacketType( uint8_t packetType ) +{ + MQTTPubAckType_t ackType = MQTTPuback; + + switch( packetType ) + { + case MQTT_PACKET_TYPE_PUBACK: + ackType = MQTTPuback; + break; + + case MQTT_PACKET_TYPE_PUBREC: + ackType = MQTTPubrec; + break; + + case MQTT_PACKET_TYPE_PUBREL: + ackType = MQTTPubrel; + break; + + case MQTT_PACKET_TYPE_PUBCOMP: + default: + + /* This function is only called after checking the type is one of + * the above four values, so packet type must be PUBCOMP here. */ + assert( packetType == MQTT_PACKET_TYPE_PUBCOMP ); + ackType = MQTTPubcomp; + break; + } + + return ackType; +} + +/*-----------------------------------------------------------*/ + +static int32_t recvExact( const MQTTContext_t * pContext, + size_t bytesToRecv ) +{ + uint8_t * pIndex = NULL; + size_t bytesRemaining = bytesToRecv; + int32_t totalBytesRecvd = 0, bytesRecvd; + uint32_t lastDataRecvTimeMs = 0U, timeSinceLastRecvMs = 0U; + TransportRecv_t recvFunc = NULL; + MQTTGetCurrentTimeFunc_t getTimeStampMs = NULL; + bool receiveError = false; + + assert( pContext != NULL ); + assert( bytesToRecv <= pContext->networkBuffer.size ); + assert( pContext->getTime != NULL ); + assert( pContext->transportInterface.recv != NULL ); + assert( pContext->networkBuffer.pBuffer != NULL ); + + pIndex = pContext->networkBuffer.pBuffer; + recvFunc = pContext->transportInterface.recv; + getTimeStampMs = pContext->getTime; + + /* Part of the MQTT packet has been read before calling this function. */ + lastDataRecvTimeMs = getTimeStampMs(); + + while( ( bytesRemaining > 0U ) && ( receiveError == false ) ) + { + bytesRecvd = recvFunc( pContext->transportInterface.pNetworkContext, + pIndex, + bytesRemaining ); + + if( bytesRecvd < 0 ) + { + LogError( ( "Network error while receiving packet: ReturnCode=%ld.", + ( long int ) bytesRecvd ) ); + totalBytesRecvd = bytesRecvd; + receiveError = true; + } + else if( bytesRecvd > 0 ) + { + /* Reset the starting time as we have received some data from the network. */ + lastDataRecvTimeMs = getTimeStampMs(); + + /* It is a bug in the application's transport receive implementation + * if more bytes than expected are received. To avoid a possible + * overflow in converting bytesRemaining from unsigned to signed, + * this assert must exist after the check for bytesRecvd being + * negative. */ + assert( ( size_t ) bytesRecvd <= bytesRemaining ); + + bytesRemaining -= ( size_t ) bytesRecvd; + totalBytesRecvd += ( int32_t ) bytesRecvd; + /* Increment the index. */ + pIndex = &pIndex[ bytesRecvd ]; + LogDebug( ( "BytesReceived=%ld, BytesRemaining=%lu, TotalBytesReceived=%ld.", + ( long int ) bytesRecvd, + ( unsigned long ) bytesRemaining, + ( long int ) totalBytesRecvd ) ); + } + else + { + /* No bytes were read from the network. */ + timeSinceLastRecvMs = calculateElapsedTime( getTimeStampMs(), lastDataRecvTimeMs ); + + /* Check for timeout if we have been waiting to receive any byte on the network. */ + if( timeSinceLastRecvMs >= MQTT_RECV_POLLING_TIMEOUT_MS ) + { + LogError( ( "Unable to receive packet: Timed out in transport recv." ) ); + receiveError = true; + } + } + } + + return totalBytesRecvd; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t discardPacket( const MQTTContext_t * pContext, + size_t remainingLength, + uint32_t timeoutMs ) +{ + MQTTStatus_t status = MQTTRecvFailed; + int32_t bytesReceived = 0; + size_t bytesToReceive = 0U; + uint32_t totalBytesReceived = 0U; + uint32_t entryTimeMs = 0U; + uint32_t elapsedTimeMs = 0U; + MQTTGetCurrentTimeFunc_t getTimeStampMs = NULL; + bool receiveError = false; + + assert( pContext != NULL ); + assert( pContext->getTime != NULL ); + + bytesToReceive = pContext->networkBuffer.size; + getTimeStampMs = pContext->getTime; + + entryTimeMs = getTimeStampMs(); + + while( ( totalBytesReceived < remainingLength ) && ( receiveError == false ) ) + { + if( ( remainingLength - totalBytesReceived ) < bytesToReceive ) + { + bytesToReceive = remainingLength - totalBytesReceived; + } + + bytesReceived = recvExact( pContext, bytesToReceive ); + + if( bytesReceived != ( int32_t ) bytesToReceive ) + { + LogError( ( "Receive error while discarding packet." + "ReceivedBytes=%ld, ExpectedBytes=%lu.", + ( long int ) bytesReceived, + ( unsigned long ) bytesToReceive ) ); + receiveError = true; + } + else + { + totalBytesReceived += ( uint32_t ) bytesReceived; + + elapsedTimeMs = calculateElapsedTime( getTimeStampMs(), entryTimeMs ); + + /* Check for timeout. */ + if( elapsedTimeMs >= timeoutMs ) + { + LogError( ( "Time expired while discarding packet." ) ); + receiveError = true; + } + } + } + + if( totalBytesReceived == remainingLength ) + { + LogError( ( "Dumped packet. DumpedBytes=%lu.", + ( unsigned long ) totalBytesReceived ) ); + /* Packet dumped, so no data is available. */ + status = MQTTNoDataAvailable; + } + + return status; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t discardStoredPacket( MQTTContext_t * pContext, + const MQTTPacketInfo_t * pPacketInfo ) +{ + MQTTStatus_t status = MQTTRecvFailed; + int32_t bytesReceived = 0; + size_t bytesToReceive = 0U; + uint32_t totalBytesReceived = 0U; + bool receiveError = false; + size_t mqttPacketSize = 0; + size_t remainingLength; + + assert( pContext != NULL ); + assert( pPacketInfo != NULL ); + + mqttPacketSize = pPacketInfo->remainingLength + pPacketInfo->headerLength; + + /* Assert that the packet being discarded is bigger than the + * receive buffer. */ + assert( mqttPacketSize > pContext->networkBuffer.size ); + + /* Discard these many bytes at a time. */ + bytesToReceive = pContext->networkBuffer.size; + + /* Number of bytes depicted by 'index' have already been received. */ + remainingLength = mqttPacketSize - pContext->index; + + while( ( totalBytesReceived < remainingLength ) && ( receiveError == false ) ) + { + if( ( remainingLength - totalBytesReceived ) < bytesToReceive ) + { + bytesToReceive = remainingLength - totalBytesReceived; + } + + bytesReceived = recvExact( pContext, bytesToReceive ); + + if( bytesReceived != ( int32_t ) bytesToReceive ) + { + LogError( ( "Receive error while discarding packet." + "ReceivedBytes=%ld, ExpectedBytes=%lu.", + ( long int ) bytesReceived, + ( unsigned long ) bytesToReceive ) ); + receiveError = true; + } + else + { + totalBytesReceived += ( uint32_t ) bytesReceived; + } + } + + if( totalBytesReceived == remainingLength ) + { + LogError( ( "Dumped packet. DumpedBytes=%lu.", + ( unsigned long ) totalBytesReceived ) ); + /* Packet dumped, so no data is available. */ + status = MQTTNoDataAvailable; + } + + /* Clear the buffer */ + ( void ) memset( pContext->networkBuffer.pBuffer, + 0, + pContext->networkBuffer.size ); + + /* Reset the index. */ + pContext->index = 0; + + return status; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t receivePacket( const MQTTContext_t * pContext, + MQTTPacketInfo_t incomingPacket, + uint32_t remainingTimeMs ) +{ + MQTTStatus_t status = MQTTSuccess; + int32_t bytesReceived = 0; + size_t bytesToReceive = 0U; + + assert( pContext != NULL ); + assert( pContext->networkBuffer.pBuffer != NULL ); + + if( incomingPacket.remainingLength > pContext->networkBuffer.size ) + { + LogError( ( "Incoming packet will be dumped: " + "Packet length exceeds network buffer size." + "PacketSize=%lu, NetworkBufferSize=%lu.", + ( unsigned long ) incomingPacket.remainingLength, + ( unsigned long ) pContext->networkBuffer.size ) ); + status = discardPacket( pContext, + incomingPacket.remainingLength, + remainingTimeMs ); + } + else + { + bytesToReceive = incomingPacket.remainingLength; + bytesReceived = recvExact( pContext, bytesToReceive ); + + if( bytesReceived == ( int32_t ) bytesToReceive ) + { + /* Receive successful, bytesReceived == bytesToReceive. */ + LogDebug( ( "Packet received. ReceivedBytes=%ld.", + ( long int ) bytesReceived ) ); + } + else + { + LogError( ( "Packet reception failed. ReceivedBytes=%ld, " + "ExpectedBytes=%lu.", + ( long int ) bytesReceived, + ( unsigned long ) bytesToReceive ) ); + status = MQTTRecvFailed; + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +static uint8_t getAckTypeToSend( MQTTPublishState_t state ) +{ + uint8_t packetTypeByte = 0U; + + switch( state ) + { + case MQTTPubAckSend: + packetTypeByte = MQTT_PACKET_TYPE_PUBACK; + break; + + case MQTTPubRecSend: + packetTypeByte = MQTT_PACKET_TYPE_PUBREC; + break; + + case MQTTPubRelSend: + packetTypeByte = MQTT_PACKET_TYPE_PUBREL; + break; + + case MQTTPubCompSend: + packetTypeByte = MQTT_PACKET_TYPE_PUBCOMP; + break; + + case MQTTPubAckPending: + case MQTTPubCompPending: + case MQTTPubRecPending: + case MQTTPubRelPending: + case MQTTPublishDone: + case MQTTPublishSend: + case MQTTStateNull: + default: + /* Take no action for states that do not require sending an ack. */ + break; + } + + return packetTypeByte; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t sendPublishAcks( MQTTContext_t * pContext, + uint16_t packetId, + MQTTPublishState_t publishState ) +{ + MQTTStatus_t status = MQTTSuccess; + MQTTPublishState_t newState = MQTTStateNull; + int32_t sendResult = 0; + uint8_t packetTypeByte = 0U; + MQTTPubAckType_t packetType; + MQTTFixedBuffer_t localBuffer; + uint8_t pubAckPacket[ MQTT_PUBLISH_ACK_PACKET_SIZE ]; + + localBuffer.pBuffer = pubAckPacket; + localBuffer.size = MQTT_PUBLISH_ACK_PACKET_SIZE; + + assert( pContext != NULL ); + + packetTypeByte = getAckTypeToSend( publishState ); + + if( packetTypeByte != 0U ) + { + packetType = getAckFromPacketType( packetTypeByte ); + + status = MQTT_SerializeAck( &localBuffer, + packetTypeByte, + packetId ); + + if( status == MQTTSuccess ) + { + MQTT_PRE_SEND_HOOK( pContext ); + + /* Here, we are not using the vector approach for efficiency. There is just one buffer + * to be sent which can be achieved with a normal send call. */ + sendResult = sendBuffer( pContext, + localBuffer.pBuffer, + MQTT_PUBLISH_ACK_PACKET_SIZE ); + + MQTT_POST_SEND_HOOK( pContext ); + } + + if( sendResult == ( int32_t ) MQTT_PUBLISH_ACK_PACKET_SIZE ) + { + pContext->controlPacketSent = true; + + MQTT_PRE_STATE_UPDATE_HOOK( pContext ); + + status = MQTT_UpdateStateAck( pContext, + packetId, + packetType, + MQTT_SEND, + &newState ); + + MQTT_POST_STATE_UPDATE_HOOK( pContext ); + + if( status != MQTTSuccess ) + { + LogError( ( "Failed to update state of publish %hu.", + ( unsigned short ) packetId ) ); + } + } + else + { + LogError( ( "Failed to send ACK packet: PacketType=%02x, SentBytes=%ld, " + "PacketSize=%lu.", + ( unsigned int ) packetTypeByte, ( long int ) sendResult, + MQTT_PUBLISH_ACK_PACKET_SIZE ) ); + status = MQTTSendFailed; + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t handleKeepAlive( MQTTContext_t * pContext ) +{ + MQTTStatus_t status = MQTTSuccess; + uint32_t now = 0U; + uint32_t packetTxTimeoutMs = 0U; + + assert( pContext != NULL ); + assert( pContext->getTime != NULL ); + + now = pContext->getTime(); + + packetTxTimeoutMs = 1000U * ( uint32_t ) pContext->keepAliveIntervalSec; + + if( PACKET_TX_TIMEOUT_MS < packetTxTimeoutMs ) + { + packetTxTimeoutMs = PACKET_TX_TIMEOUT_MS; + } + + /* If keep alive interval is 0, it is disabled. */ + if( pContext->waitingForPingResp == true ) + { + /* Has time expired? */ + if( calculateElapsedTime( now, pContext->pingReqSendTimeMs ) > + MQTT_PINGRESP_TIMEOUT_MS ) + { + status = MQTTKeepAliveTimeout; + } + } + else + { + if( ( packetTxTimeoutMs != 0U ) && ( calculateElapsedTime( now, pContext->lastPacketTxTime ) >= packetTxTimeoutMs ) ) + { + status = MQTT_Ping( pContext ); + } + else + { + const uint32_t timeElapsed = calculateElapsedTime( now, pContext->lastPacketRxTime ); + + if( ( timeElapsed != 0U ) && ( timeElapsed >= PACKET_RX_TIMEOUT_MS ) ) + { + status = MQTT_Ping( pContext ); + } + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t handleIncomingPublish( MQTTContext_t * pContext, + MQTTPacketInfo_t * pIncomingPacket ) +{ + MQTTStatus_t status = MQTTBadParameter; + MQTTPublishState_t publishRecordState = MQTTStateNull; + uint16_t packetIdentifier = 0U; + MQTTPublishInfo_t publishInfo; + MQTTDeserializedInfo_t deserializedInfo; + bool duplicatePublish = false; + + assert( pContext != NULL ); + assert( pIncomingPacket != NULL ); + assert( pContext->appCallback != NULL ); + + status = MQTT_DeserializePublish( pIncomingPacket, &packetIdentifier, &publishInfo ); + LogInfo( ( "De-serialized incoming PUBLISH packet: DeserializerResult=%s.", + MQTT_Status_strerror( status ) ) ); + + if( ( status == MQTTSuccess ) && + ( pContext->incomingPublishRecords == NULL ) && + ( publishInfo.qos > MQTTQoS0 ) ) + { + LogError( ( "Incoming publish has QoS > MQTTQoS0 but incoming " + "publish records have not been initialized. Dropping the " + "incoming publish. Please call MQTT_InitStatefulQoS to enable " + "use of QoS1 and QoS2 publishes." ) ); + status = MQTTRecvFailed; + } + + if( status == MQTTSuccess ) + { + MQTT_PRE_STATE_UPDATE_HOOK( pContext ); + + status = MQTT_UpdateStatePublish( pContext, + packetIdentifier, + MQTT_RECEIVE, + publishInfo.qos, + &publishRecordState ); + + MQTT_POST_STATE_UPDATE_HOOK( pContext ); + + if( status == MQTTSuccess ) + { + LogInfo( ( "State record updated. New state=%s.", + MQTT_State_strerror( publishRecordState ) ) ); + } + + /* Different cases in which an incoming publish with duplicate flag is + * handled are as listed below. + * 1. No collision - This is the first instance of the incoming publish + * packet received or an earlier received packet state is lost. This + * will be handled as a new incoming publish for both QoS1 and QoS2 + * publishes. + * 2. Collision - The incoming packet was received before and a state + * record is present in the state engine. For QoS1 and QoS2 publishes + * this case can happen at 2 different cases and handling is + * different. + * a. QoS1 - If a PUBACK is not successfully sent for the incoming + * publish due to a connection issue, it can result in broker + * sending out a duplicate publish with dup flag set, when a + * session is reestablished. It can result in a collision in + * state engine. This will be handled by processing the incoming + * publish as a new publish ignoring the + * #MQTTStateCollision status from the state engine. The publish + * data is not passed to the application. + * b. QoS2 - If a PUBREC is not successfully sent for the incoming + * publish or the PUBREC sent is not successfully received by the + * broker due to a connection issue, it can result in broker + * sending out a duplicate publish with dup flag set, when a + * session is reestablished. It can result in a collision in + * state engine. This will be handled by ignoring the + * #MQTTStateCollision status from the state engine. The publish + * data is not passed to the application. */ + else if( status == MQTTStateCollision ) + { + status = MQTTSuccess; + duplicatePublish = true; + + /* Calculate the state for the ack packet that needs to be sent out + * for the duplicate incoming publish. */ + publishRecordState = MQTT_CalculateStatePublish( MQTT_RECEIVE, + publishInfo.qos ); + + LogDebug( ( "Incoming publish packet with packet id %hu already exists.", + ( unsigned short ) packetIdentifier ) ); + + if( publishInfo.dup == false ) + { + LogError( ( "DUP flag is 0 for duplicate packet (MQTT-3.3.1.-1)." ) ); + } + } + else + { + LogError( ( "Error in updating publish state for incoming publish with packet id %hu." + " Error is %s", + ( unsigned short ) packetIdentifier, + MQTT_Status_strerror( status ) ) ); + } + } + + if( status == MQTTSuccess ) + { + /* Set fields of deserialized struct. */ + deserializedInfo.packetIdentifier = packetIdentifier; + deserializedInfo.pPublishInfo = &publishInfo; + deserializedInfo.deserializationResult = status; + + /* Invoke application callback to hand the buffer over to application + * before sending acks. + * Application callback will be invoked for all publishes, except for + * duplicate incoming publishes. */ + if( duplicatePublish == false ) + { + pContext->appCallback( pContext, + pIncomingPacket, + &deserializedInfo ); + } + + /* Send PUBACK or PUBREC if necessary. */ + status = sendPublishAcks( pContext, + packetIdentifier, + publishRecordState ); + } + + return status; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t handlePublishAcks( MQTTContext_t * pContext, + MQTTPacketInfo_t * pIncomingPacket ) +{ + MQTTStatus_t status = MQTTBadResponse; + MQTTPublishState_t publishRecordState = MQTTStateNull; + uint16_t packetIdentifier; + MQTTPubAckType_t ackType; + MQTTEventCallback_t appCallback; + MQTTDeserializedInfo_t deserializedInfo; + + assert( pContext != NULL ); + assert( pIncomingPacket != NULL ); + assert( pContext->appCallback != NULL ); + + appCallback = pContext->appCallback; + + ackType = getAckFromPacketType( pIncomingPacket->type ); + status = MQTT_DeserializeAck( pIncomingPacket, &packetIdentifier, NULL ); + LogInfo( ( "Ack packet deserialized with result: %s.", + MQTT_Status_strerror( status ) ) ); + + if( status == MQTTSuccess ) + { + MQTT_PRE_STATE_UPDATE_HOOK( pContext ); + + status = MQTT_UpdateStateAck( pContext, + packetIdentifier, + ackType, + MQTT_RECEIVE, + &publishRecordState ); + + MQTT_POST_STATE_UPDATE_HOOK( pContext ); + + if( status == MQTTSuccess ) + { + LogInfo( ( "State record updated. New state=%s.", + MQTT_State_strerror( publishRecordState ) ) ); + } + else + { + LogError( ( "Updating the state engine for packet id %hu" + " failed with error %s.", + ( unsigned short ) packetIdentifier, + MQTT_Status_strerror( status ) ) ); + } + } + + if( status == MQTTSuccess ) + { + /* Set fields of deserialized struct. */ + deserializedInfo.packetIdentifier = packetIdentifier; + deserializedInfo.deserializationResult = status; + deserializedInfo.pPublishInfo = NULL; + + /* Invoke application callback to hand the buffer over to application + * before sending acks. */ + appCallback( pContext, pIncomingPacket, &deserializedInfo ); + + /* Send PUBREL or PUBCOMP if necessary. */ + status = sendPublishAcks( pContext, + packetIdentifier, + publishRecordState ); + } + + return status; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t handleIncomingAck( MQTTContext_t * pContext, + MQTTPacketInfo_t * pIncomingPacket, + bool manageKeepAlive ) +{ + MQTTStatus_t status = MQTTBadResponse; + uint16_t packetIdentifier = MQTT_PACKET_ID_INVALID; + MQTTDeserializedInfo_t deserializedInfo; + + /* We should always invoke the app callback unless we receive a PINGRESP + * and are managing keep alive, or if we receive an unknown packet. We + * initialize this to false since the callback must be invoked before + * sending any PUBREL or PUBCOMP. However, for other cases, we invoke it + * at the end to reduce the complexity of this function. */ + bool invokeAppCallback = false; + MQTTEventCallback_t appCallback = NULL; + + assert( pContext != NULL ); + assert( pIncomingPacket != NULL ); + assert( pContext->appCallback != NULL ); + + appCallback = pContext->appCallback; + + LogDebug( ( "Received packet of type %02x.", + ( unsigned int ) pIncomingPacket->type ) ); + + switch( pIncomingPacket->type ) + { + case MQTT_PACKET_TYPE_PUBACK: + case MQTT_PACKET_TYPE_PUBREC: + case MQTT_PACKET_TYPE_PUBREL: + case MQTT_PACKET_TYPE_PUBCOMP: + + /* Handle all the publish acks. The app callback is invoked here. */ + status = handlePublishAcks( pContext, pIncomingPacket ); + + break; + + case MQTT_PACKET_TYPE_PINGRESP: + status = MQTT_DeserializeAck( pIncomingPacket, &packetIdentifier, NULL ); + invokeAppCallback = ( status == MQTTSuccess ) && !manageKeepAlive; + + if( ( status == MQTTSuccess ) && ( manageKeepAlive == true ) ) + { + pContext->waitingForPingResp = false; + } + + break; + + case MQTT_PACKET_TYPE_SUBACK: + case MQTT_PACKET_TYPE_UNSUBACK: + /* Deserialize and give these to the app provided callback. */ + status = MQTT_DeserializeAck( pIncomingPacket, &packetIdentifier, NULL ); + invokeAppCallback = ( status == MQTTSuccess ) || ( status == MQTTServerRefused ); + break; + + default: + /* Bad response from the server. */ + LogError( ( "Unexpected packet type from server: PacketType=%02x.", + ( unsigned int ) pIncomingPacket->type ) ); + status = MQTTBadResponse; + break; + } + + if( invokeAppCallback == true ) + { + /* Set fields of deserialized struct. */ + deserializedInfo.packetIdentifier = packetIdentifier; + deserializedInfo.deserializationResult = status; + deserializedInfo.pPublishInfo = NULL; + appCallback( pContext, pIncomingPacket, &deserializedInfo ); + /* In case a SUBACK indicated refusal, reset the status to continue the loop. */ + status = MQTTSuccess; + } + + return status; +} +/*-----------------------------------------------------------*/ + +static MQTTStatus_t receiveSingleIteration( MQTTContext_t * pContext, + bool manageKeepAlive ) +{ + MQTTStatus_t status = MQTTSuccess; + MQTTPacketInfo_t incomingPacket = { 0 }; + int32_t recvBytes; + size_t totalMQTTPacketLength = 0; + + assert( pContext != NULL ); + assert( pContext->networkBuffer.pBuffer != NULL ); + + /* Read as many bytes as possible into the network buffer. */ + recvBytes = pContext->transportInterface.recv( pContext->transportInterface.pNetworkContext, + &( pContext->networkBuffer.pBuffer[ pContext->index ] ), + pContext->networkBuffer.size - pContext->index ); + + if( recvBytes < 0 ) + { + /* The receive function has failed. Bubble up the error up to the user. */ + status = MQTTRecvFailed; + } + else if( ( recvBytes == 0 ) && ( pContext->index == 0U ) ) + { + /* No more bytes available since the last read and neither is anything in + * the buffer. */ + status = MQTTNoDataAvailable; + } + + /* Either something was received, or there is still data to be processed in the + * buffer, or both. */ + else + { + /* Update the number of bytes in the MQTT fixed buffer. */ + pContext->index += ( size_t ) recvBytes; + + status = MQTT_ProcessIncomingPacketTypeAndLength( pContext->networkBuffer.pBuffer, + &( pContext->index ), + &incomingPacket ); + + totalMQTTPacketLength = incomingPacket.remainingLength + incomingPacket.headerLength; + } + + /* No data was received, check for keep alive timeout. */ + if( recvBytes == 0 ) + { + if( manageKeepAlive == true ) + { + /* Keep the copy of the status to be reset later. */ + MQTTStatus_t statusCopy = status; + + /* Assign status so an error can be bubbled up to application, + * but reset it on success. */ + status = handleKeepAlive( pContext ); + + if( status == MQTTSuccess ) + { + /* Reset the status. */ + status = statusCopy; + } + else + { + LogError( ( "Handling of keep alive failed. Status=%s", + MQTT_Status_strerror( status ) ) ); + } + } + } + + /* Check whether there is data available before processing the packet further. */ + if( ( status == MQTTNeedMoreBytes ) || ( status == MQTTNoDataAvailable ) ) + { + /* Do nothing as there is nothing to be processed right now. The proper + * error code will be bubbled up to the user. */ + } + /* Any other error code. */ + else if( status != MQTTSuccess ) + { + LogError( ( "Call to receiveSingleIteration failed. Status=%s", + MQTT_Status_strerror( status ) ) ); + } + /* If the MQTT Packet size is bigger than the buffer itself. */ + else if( totalMQTTPacketLength > pContext->networkBuffer.size ) + { + /* Discard the packet from the receive buffer and drain the pending + * data from the socket buffer. */ + status = discardStoredPacket( pContext, + &incomingPacket ); + } + /* If the total packet is of more length than the bytes we have available. */ + else if( totalMQTTPacketLength > pContext->index ) + { + status = MQTTNeedMoreBytes; + } + else + { + /* MISRA else. */ + } + + /* Handle received packet. If incomplete data was read then this will not execute. */ + if( status == MQTTSuccess ) + { + incomingPacket.pRemainingData = &pContext->networkBuffer.pBuffer[ incomingPacket.headerLength ]; + + /* PUBLISH packets allow flags in the lower four bits. For other + * packet types, they are reserved. */ + if( ( incomingPacket.type & 0xF0U ) == MQTT_PACKET_TYPE_PUBLISH ) + { + status = handleIncomingPublish( pContext, &incomingPacket ); + } + else + { + status = handleIncomingAck( pContext, &incomingPacket, manageKeepAlive ); + } + + /* Update the index to reflect the remaining bytes in the buffer. */ + pContext->index -= totalMQTTPacketLength; + + /* Move the remaining bytes to the front of the buffer. */ + ( void ) memmove( pContext->networkBuffer.pBuffer, + &( pContext->networkBuffer.pBuffer[ totalMQTTPacketLength ] ), + pContext->index ); + + if( status == MQTTSuccess ) + { + pContext->lastPacketRxTime = pContext->getTime(); + } + } + + if( status == MQTTNoDataAvailable ) + { + /* No data available is not an error. Reset to MQTTSuccess so the + * return code will indicate success. */ + status = MQTTSuccess; + } + + return status; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t validateSubscribeUnsubscribeParams( const MQTTContext_t * pContext, + const MQTTSubscribeInfo_t * pSubscriptionList, + size_t subscriptionCount, + uint16_t packetId ) +{ + MQTTStatus_t status = MQTTSuccess; + size_t iterator; + + /* Validate all the parameters. */ + if( ( pContext == NULL ) || ( pSubscriptionList == NULL ) ) + { + LogError( ( "Argument cannot be NULL: pContext=%p, " + "pSubscriptionList=%p.", + ( void * ) pContext, + ( void * ) pSubscriptionList ) ); + status = MQTTBadParameter; + } + else if( subscriptionCount == 0UL ) + { + LogError( ( "Subscription count is 0." ) ); + status = MQTTBadParameter; + } + else if( packetId == 0U ) + { + LogError( ( "Packet Id for subscription packet is 0." ) ); + status = MQTTBadParameter; + } + else + { + if( pContext->incomingPublishRecords == NULL ) + { + for( iterator = 0; iterator < subscriptionCount; iterator++ ) + { + if( pSubscriptionList->qos > MQTTQoS0 ) + { + LogError( ( "The incoming publish record list is not " + "initialised for QoS1/QoS2 records. Please call " + " MQTT_InitStatefulQoS to enable use of QoS1 and " + " QoS2 packets." ) ); + status = MQTTBadParameter; + break; + } + } + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +static size_t addEncodedStringToVector( uint8_t serializedLength[ CORE_MQTT_SERIALIZED_LENGTH_FIELD_BYTES ], + const char * const string, + uint16_t length, + TransportOutVector_t * iterator, + size_t * updatedLength ) +{ + size_t packetLength = 0U; + TransportOutVector_t * pLocalIterator = iterator; + size_t vectorsAdded = 0U; + + /* When length is non-zero, the string must be non-NULL. */ + assert( ( length != 0U ) ? ( string != NULL ) : true ); + + serializedLength[ 0 ] = ( ( uint8_t ) ( ( length ) >> 8 ) ); + serializedLength[ 1 ] = ( ( uint8_t ) ( ( length ) & 0x00ffU ) ); + + /* Add the serialized length of the string first. */ + pLocalIterator[ 0 ].iov_base = serializedLength; + pLocalIterator[ 0 ].iov_len = CORE_MQTT_SERIALIZED_LENGTH_FIELD_BYTES; + vectorsAdded++; + packetLength = CORE_MQTT_SERIALIZED_LENGTH_FIELD_BYTES; + + /* Sometimes the string can be NULL that is, of 0 length. In that case, + * only the length field should be encoded in the vector. */ + if( ( string != NULL ) && ( length != 0U ) ) + { + /* Then add the pointer to the string itself. */ + pLocalIterator[ 1 ].iov_base = string; + pLocalIterator[ 1 ].iov_len = length; + vectorsAdded++; + packetLength += length; + } + + ( *updatedLength ) = ( *updatedLength ) + packetLength; + + return vectorsAdded; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t sendSubscribeWithoutCopy( MQTTContext_t * pContext, + const MQTTSubscribeInfo_t * pSubscriptionList, + size_t subscriptionCount, + uint16_t packetId, + size_t remainingLength ) +{ + MQTTStatus_t status = MQTTSuccess; + uint8_t * pIndex; + TransportOutVector_t pIoVector[ MQTT_SUB_UNSUB_MAX_VECTORS ]; + TransportOutVector_t * pIterator; + uint8_t serializedTopicFieldLength[ MQTT_SUB_UNSUB_MAX_VECTORS ][ CORE_MQTT_SERIALIZED_LENGTH_FIELD_BYTES ]; + size_t totalPacketLength = 0U; + size_t ioVectorLength = 0U; + size_t subscriptionsSent = 0U; + size_t vectorsAdded; + size_t topicFieldLengthIndex; + + /* Maximum number of bytes required by the 'fixed' part of the SUBSCRIBE + * packet header according to the MQTT specification. + * MQTT Control Byte 0 + 1 = 1 + * Remaining length (max) + 4 = 5 + * Packet ID + 2 = 7 */ + uint8_t subscribeheader[ 7U ]; + + /* The vector array should be at least three element long as the topic + * string needs these many vector elements to be stored. */ + assert( MQTT_SUB_UNSUB_MAX_VECTORS >= CORE_MQTT_SUBSCRIBE_PER_TOPIC_VECTOR_LENGTH ); + + pIndex = subscribeheader; + pIterator = pIoVector; + + pIndex = MQTT_SerializeSubscribeHeader( remainingLength, + pIndex, + packetId ); + + /* The header is to be sent first. */ + pIterator->iov_base = subscribeheader; + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + pIterator->iov_len = ( size_t ) ( pIndex - subscribeheader ); + totalPacketLength += pIterator->iov_len; + pIterator++; + ioVectorLength++; + + while( ( status == MQTTSuccess ) && ( subscriptionsSent < subscriptionCount ) ) + { + /* Reset the index for next iteration. */ + topicFieldLengthIndex = 0; + + /* Check whether the subscription topic (with QoS) will fit in the + * given vector. */ + while( ( ioVectorLength <= ( MQTT_SUB_UNSUB_MAX_VECTORS - CORE_MQTT_SUBSCRIBE_PER_TOPIC_VECTOR_LENGTH ) ) && + ( subscriptionsSent < subscriptionCount ) ) + { + /* The topic filter and the filter length gets sent next. */ + vectorsAdded = addEncodedStringToVector( serializedTopicFieldLength[ topicFieldLengthIndex ], + pSubscriptionList[ subscriptionsSent ].pTopicFilter, + pSubscriptionList[ subscriptionsSent ].topicFilterLength, + pIterator, + &totalPacketLength ); + + /* Update the pointer after the above operation. */ + pIterator = &pIterator[ vectorsAdded ]; + + /* Lastly, the QoS gets sent. */ + pIterator->iov_base = &( pSubscriptionList[ subscriptionsSent ].qos ); + pIterator->iov_len = 1U; + totalPacketLength += pIterator->iov_len; + + /* Increment the pointer. */ + pIterator++; + + /* Two slots get used by the topic string length and topic string. + * One slot gets used by the quality of service. */ + ioVectorLength += vectorsAdded + 1U; + + subscriptionsSent++; + + /* The index needs to be updated for next iteration. */ + topicFieldLengthIndex++; + } + + if( sendMessageVector( pContext, + pIoVector, + ioVectorLength ) != ( int32_t ) totalPacketLength ) + { + status = MQTTSendFailed; + } + + /* Update the iterator for the next potential loop iteration. */ + pIterator = pIoVector; + /* Reset the vector length for the next potential loop iteration. */ + ioVectorLength = 0U; + /* Reset the packet length for the next potential loop iteration. */ + totalPacketLength = 0U; + } + + return status; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t sendUnsubscribeWithoutCopy( MQTTContext_t * pContext, + const MQTTSubscribeInfo_t * pSubscriptionList, + size_t subscriptionCount, + uint16_t packetId, + size_t remainingLength ) +{ + MQTTStatus_t status = MQTTSuccess; + uint8_t * pIndex; + TransportOutVector_t pIoVector[ MQTT_SUB_UNSUB_MAX_VECTORS ]; + TransportOutVector_t * pIterator; + uint8_t serializedTopicFieldLength[ MQTT_SUB_UNSUB_MAX_VECTORS ][ CORE_MQTT_SERIALIZED_LENGTH_FIELD_BYTES ]; + size_t totalPacketLength = 0U; + size_t unsubscriptionsSent = 0U; + size_t ioVectorLength = 0U; + size_t vectorsAdded; + size_t topicFieldLengthIndex; + + /* Maximum number of bytes required by the 'fixed' part of the UNSUBSCRIBE + * packet header according to the MQTT specification. + * MQTT Control Byte 0 + 1 = 1 + * Remaining length (max) + 4 = 5 + * Packet ID + 2 = 7 */ + uint8_t unsubscribeheader[ 7U ]; + + /* The vector array should be at least three element long as the topic + * string needs these many vector elements to be stored. */ + assert( MQTT_SUB_UNSUB_MAX_VECTORS >= CORE_MQTT_UNSUBSCRIBE_PER_TOPIC_VECTOR_LENGTH ); + + pIndex = unsubscribeheader; + pIterator = pIoVector; + + pIndex = MQTT_SerializeUnsubscribeHeader( remainingLength, + pIndex, + packetId ); + + /* The header is to be sent first. */ + pIterator->iov_base = unsubscribeheader; + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + pIterator->iov_len = ( size_t ) ( pIndex - unsubscribeheader ); + totalPacketLength += pIterator->iov_len; + pIterator++; + ioVectorLength++; + + while( ( status == MQTTSuccess ) && ( unsubscriptionsSent < subscriptionCount ) ) + { + /* Reset the index for next iteration. */ + topicFieldLengthIndex = 0; + + /* Check whether the subscription topic will fit in the given vector. */ + while( ( ioVectorLength <= ( MQTT_SUB_UNSUB_MAX_VECTORS - CORE_MQTT_UNSUBSCRIBE_PER_TOPIC_VECTOR_LENGTH ) ) && + ( unsubscriptionsSent < subscriptionCount ) ) + { + /* The topic filter gets sent next. */ + vectorsAdded = addEncodedStringToVector( serializedTopicFieldLength[ topicFieldLengthIndex ], + pSubscriptionList[ unsubscriptionsSent ].pTopicFilter, + pSubscriptionList[ unsubscriptionsSent ].topicFilterLength, + pIterator, + &totalPacketLength ); + + /* Update the iterator to point to the next empty location. */ + pIterator = &pIterator[ vectorsAdded ]; + /* Update the total count based on how many vectors were added. */ + ioVectorLength += vectorsAdded; + + unsubscriptionsSent++; + + /* Update the index for next iteration. */ + topicFieldLengthIndex++; + } + + if( sendMessageVector( pContext, pIoVector, ioVectorLength ) != ( int32_t ) totalPacketLength ) + { + status = MQTTSendFailed; + } + + /* Update the iterator for the next potential loop iteration. */ + pIterator = pIoVector; + /* Reset the vector length for the next potential loop iteration. */ + ioVectorLength = 0U; + /* Reset the packet length for the next potential loop iteration. */ + totalPacketLength = 0U; + } + + return status; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t sendPublishWithoutCopy( MQTTContext_t * pContext, + const MQTTPublishInfo_t * pPublishInfo, + const uint8_t * pMqttHeader, + size_t headerSize, + uint16_t packetId ) +{ + MQTTStatus_t status = MQTTSuccess; + size_t ioVectorLength; + size_t totalMessageLength; + + /* Bytes required to encode the packet ID in an MQTT header according to + * the MQTT specification. */ + uint8_t serializedPacketID[ 2U ]; + + /* Maximum number of vectors required to encode and send a publish + * packet. The breakdown is shown below. + * Fixed header (including topic string length) 0 + 1 = 1 + * Topic string + 1 = 2 + * Packet ID (only when QoS > QoS0) + 1 = 3 + * Payload + 1 = 4 */ + TransportOutVector_t pIoVector[ 4U ]; + + /* The header is sent first. */ + pIoVector[ 0U ].iov_base = pMqttHeader; + pIoVector[ 0U ].iov_len = headerSize; + totalMessageLength = headerSize; + + /* Then the topic name has to be sent. */ + pIoVector[ 1U ].iov_base = pPublishInfo->pTopicName; + pIoVector[ 1U ].iov_len = pPublishInfo->topicNameLength; + totalMessageLength += pPublishInfo->topicNameLength; + + /* The next field's index should be 2 as the first two fields + * have been filled in. */ + ioVectorLength = 2U; + + if( pPublishInfo->qos > MQTTQoS0 ) + { + /* Encode the packet ID. */ + serializedPacketID[ 0 ] = ( ( uint8_t ) ( ( packetId ) >> 8 ) ); + serializedPacketID[ 1 ] = ( ( uint8_t ) ( ( packetId ) & 0x00ffU ) ); + + pIoVector[ ioVectorLength ].iov_base = serializedPacketID; + pIoVector[ ioVectorLength ].iov_len = sizeof( serializedPacketID ); + + ioVectorLength++; + totalMessageLength += sizeof( serializedPacketID ); + } + + /* Publish packets are allowed to contain no payload. */ + if( pPublishInfo->payloadLength > 0U ) + { + pIoVector[ ioVectorLength ].iov_base = pPublishInfo->pPayload; + pIoVector[ ioVectorLength ].iov_len = pPublishInfo->payloadLength; + + ioVectorLength++; + totalMessageLength += pPublishInfo->payloadLength; + } + + if( sendMessageVector( pContext, pIoVector, ioVectorLength ) != ( int32_t ) totalMessageLength ) + { + status = MQTTSendFailed; + } + + return status; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t sendConnectWithoutCopy( MQTTContext_t * pContext, + const MQTTConnectInfo_t * pConnectInfo, + const MQTTPublishInfo_t * pWillInfo, + size_t remainingLength ) +{ + MQTTStatus_t status = MQTTSuccess; + TransportOutVector_t * iterator; + size_t ioVectorLength = 0U; + size_t totalMessageLength = 0U; + int32_t bytesSentOrError; + uint8_t * pIndex; + uint8_t serializedClientIDLength[ 2 ]; + uint8_t serializedTopicLength[ 2 ]; + uint8_t serializedPayloadLength[ 2 ]; + uint8_t serializedUsernameLength[ 2 ]; + uint8_t serializedPasswordLength[ 2 ]; + size_t vectorsAdded; + + /* Maximum number of bytes required by the 'fixed' part of the CONNECT + * packet header according to the MQTT specification. + * MQTT Control Byte 0 + 1 = 1 + * Remaining length (max) + 4 = 5 + * Protocol Name Length + 2 = 7 + * Protocol Name (MQTT) + 4 = 11 + * Protocol level + 1 = 12 + * Connect flags + 1 = 13 + * Keep alive + 2 = 15 */ + uint8_t connectPacketHeader[ 15U ]; + + /* The maximum vectors required to encode and send a connect packet. The + * breakdown is shown below. + * Fixed header 0 + 1 = 1 + * Client ID + 2 = 3 + * Will topic + 2 = 5 + * Will payload + 2 = 7 + * Username + 2 = 9 + * Password + 2 = 11 */ + TransportOutVector_t pIoVector[ 11U ]; + + iterator = pIoVector; + pIndex = connectPacketHeader; + + /* Validate arguments. */ + if( ( pWillInfo != NULL ) && ( pWillInfo->pTopicName == NULL ) ) + { + LogError( ( "pWillInfo->pTopicName cannot be NULL if Will is present." ) ); + status = MQTTBadParameter; + } + else + { + pIndex = MQTT_SerializeConnectFixedHeader( pIndex, + pConnectInfo, + pWillInfo, + remainingLength ); + + assert( ( pIndex - connectPacketHeader ) <= sizeof( connectPacketHeader ) ); + + /* The header gets sent first. */ + iterator->iov_base = connectPacketHeader; + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + iterator->iov_len = ( size_t ) ( pIndex - connectPacketHeader ); + totalMessageLength += iterator->iov_len; + iterator++; + ioVectorLength++; + + /* Serialize the client ID. */ + vectorsAdded = addEncodedStringToVector( serializedClientIDLength, + pConnectInfo->pClientIdentifier, + pConnectInfo->clientIdentifierLength, + iterator, + &totalMessageLength ); + + /* Update the iterator to point to the next empty slot. */ + iterator = &iterator[ vectorsAdded ]; + ioVectorLength += vectorsAdded; + + if( pWillInfo != NULL ) + { + /* Serialize the topic. */ + vectorsAdded = addEncodedStringToVector( serializedTopicLength, + pWillInfo->pTopicName, + pWillInfo->topicNameLength, + iterator, + &totalMessageLength ); + + /* Update the iterator to point to the next empty slot. */ + iterator = &iterator[ vectorsAdded ]; + ioVectorLength += vectorsAdded; + + + /* Serialize the payload. Payload of last will and testament can be NULL. */ + vectorsAdded = addEncodedStringToVector( serializedPayloadLength, + pWillInfo->pPayload, + ( uint16_t ) pWillInfo->payloadLength, + iterator, + &totalMessageLength ); + + /* Update the iterator to point to the next empty slot. */ + iterator = &iterator[ vectorsAdded ]; + ioVectorLength += vectorsAdded; + } + + /* Encode the user name if provided. */ + if( pConnectInfo->pUserName != NULL ) + { + /* Serialize the user name string. */ + vectorsAdded = addEncodedStringToVector( serializedUsernameLength, + pConnectInfo->pUserName, + pConnectInfo->userNameLength, + iterator, + &totalMessageLength ); + + /* Update the iterator to point to the next empty slot. */ + iterator = &iterator[ vectorsAdded ]; + ioVectorLength += vectorsAdded; + } + + /* Encode the password if provided. */ + if( pConnectInfo->pPassword != NULL ) + { + /* Serialize the user name string. */ + vectorsAdded = addEncodedStringToVector( serializedPasswordLength, + pConnectInfo->pPassword, + pConnectInfo->passwordLength, + iterator, + &totalMessageLength ); + /* Update the iterator to point to the next empty slot. */ + iterator = &iterator[ vectorsAdded ]; + ioVectorLength += vectorsAdded; + } + + bytesSentOrError = sendMessageVector( pContext, pIoVector, ioVectorLength ); + + if( bytesSentOrError != ( int32_t ) totalMessageLength ) + { + status = MQTTSendFailed; + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t receiveConnack( const MQTTContext_t * pContext, + uint32_t timeoutMs, + bool cleanSession, + MQTTPacketInfo_t * pIncomingPacket, + bool * pSessionPresent ) +{ + MQTTStatus_t status = MQTTSuccess; + MQTTGetCurrentTimeFunc_t getTimeStamp = NULL; + uint32_t entryTimeMs = 0U, remainingTimeMs = 0U, timeTakenMs = 0U; + bool breakFromLoop = false; + uint16_t loopCount = 0U; + + assert( pContext != NULL ); + assert( pIncomingPacket != NULL ); + assert( pContext->getTime != NULL ); + + getTimeStamp = pContext->getTime; + + /* Get the entry time for the function. */ + entryTimeMs = getTimeStamp(); + + do + { + /* Transport read for incoming CONNACK packet type and length. + * MQTT_GetIncomingPacketTypeAndLength is a blocking call and it is + * returned after a transport receive timeout, an error, or a successful + * receive of packet type and length. */ + status = MQTT_GetIncomingPacketTypeAndLength( pContext->transportInterface.recv, + pContext->transportInterface.pNetworkContext, + pIncomingPacket ); + + /* The loop times out based on 2 conditions. + * 1. If timeoutMs is greater than 0: + * Loop times out based on the timeout calculated by getTime() + * function. + * 2. If timeoutMs is 0: + * Loop times out based on the maximum number of retries config + * MQTT_MAX_CONNACK_RECEIVE_RETRY_COUNT. This config will control + * maximum the number of retry attempts to read the CONNACK packet. + * A value of 0 for the config will try once to read CONNACK. */ + if( timeoutMs > 0U ) + { + breakFromLoop = calculateElapsedTime( getTimeStamp(), entryTimeMs ) >= timeoutMs; + } + else + { + breakFromLoop = loopCount >= MQTT_MAX_CONNACK_RECEIVE_RETRY_COUNT; + loopCount++; + } + + /* Loop until there is data to read or if we have exceeded the timeout/retries. */ + } while( ( status == MQTTNoDataAvailable ) && ( breakFromLoop == false ) ); + + if( status == MQTTSuccess ) + { + /* Time taken in this function so far. */ + timeTakenMs = calculateElapsedTime( getTimeStamp(), entryTimeMs ); + + if( timeTakenMs < timeoutMs ) + { + /* Calculate remaining time for receiving the remainder of + * the packet. */ + remainingTimeMs = timeoutMs - timeTakenMs; + } + + /* Reading the remainder of the packet by transport recv. + * Attempt to read once even if the timeout has expired. + * Invoking receivePacket with remainingTime as 0 would attempt to + * recv from network once. If using retries, the remainder of the + * CONNACK packet is tried to be read only once. Reading once would be + * good as the packet type and remaining length was already read. Hence, + * the probability of the remaining 2 bytes available to read is very high. */ + if( pIncomingPacket->type == MQTT_PACKET_TYPE_CONNACK ) + { + status = receivePacket( pContext, + *pIncomingPacket, + remainingTimeMs ); + } + else + { + LogError( ( "Incorrect packet type %X received while expecting" + " CONNACK(%X).", + ( unsigned int ) pIncomingPacket->type, + MQTT_PACKET_TYPE_CONNACK ) ); + status = MQTTBadResponse; + } + } + + if( status == MQTTSuccess ) + { + /* Update the packet info pointer to the buffer read. */ + pIncomingPacket->pRemainingData = pContext->networkBuffer.pBuffer; + + /* Deserialize CONNACK. */ + status = MQTT_DeserializeAck( pIncomingPacket, NULL, pSessionPresent ); + } + + /* If a clean session is requested, a session present should not be set by + * broker. */ + if( status == MQTTSuccess ) + { + if( ( cleanSession == true ) && ( *pSessionPresent == true ) ) + { + LogError( ( "Unexpected session present flag in CONNACK response from broker." + " CONNECT request with clean session was made with broker." ) ); + status = MQTTBadResponse; + } + } + + if( status == MQTTSuccess ) + { + LogDebug( ( "Received MQTT CONNACK successfully from broker." ) ); + } + else + { + LogError( ( "CONNACK recv failed with status = %s.", + MQTT_Status_strerror( status ) ) ); + } + + return status; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t handleSessionResumption( MQTTContext_t * pContext, + bool sessionPresent ) +{ + MQTTStatus_t status = MQTTSuccess; + MQTTStateCursor_t cursor = MQTT_STATE_CURSOR_INITIALIZER; + uint16_t packetId = MQTT_PACKET_ID_INVALID; + MQTTPublishState_t state = MQTTStateNull; + + assert( pContext != NULL ); + + /* Reset the index and clear the buffer when a new session is established. */ + pContext->index = 0; + ( void ) memset( pContext->networkBuffer.pBuffer, 0, pContext->networkBuffer.size ); + + if( sessionPresent == true ) + { + /* Get the next packet ID for which a PUBREL need to be resent. */ + packetId = MQTT_PubrelToResend( pContext, &cursor, &state ); + + /* Resend all the PUBREL acks after session is reestablished. */ + while( ( packetId != MQTT_PACKET_ID_INVALID ) && + ( status == MQTTSuccess ) ) + { + status = sendPublishAcks( pContext, packetId, state ); + + packetId = MQTT_PubrelToResend( pContext, &cursor, &state ); + } + } + else + { + /* Clear any existing records if a new session is established. */ + if( pContext->outgoingPublishRecordMaxCount > 0U ) + { + ( void ) memset( pContext->outgoingPublishRecords, + 0x00, + pContext->outgoingPublishRecordMaxCount * sizeof( *pContext->outgoingPublishRecords ) ); + } + + if( pContext->incomingPublishRecordMaxCount > 0U ) + { + ( void ) memset( pContext->incomingPublishRecords, + 0x00, + pContext->incomingPublishRecordMaxCount * sizeof( *pContext->incomingPublishRecords ) ); + } + } + + return status; +} + +static MQTTStatus_t validatePublishParams( const MQTTContext_t * pContext, + const MQTTPublishInfo_t * pPublishInfo, + uint16_t packetId ) +{ + MQTTStatus_t status = MQTTSuccess; + + /* Validate arguments. */ + if( ( pContext == NULL ) || ( pPublishInfo == NULL ) ) + { + LogError( ( "Argument cannot be NULL: pContext=%p, " + "pPublishInfo=%p.", + ( void * ) pContext, + ( void * ) pPublishInfo ) ); + status = MQTTBadParameter; + } + else if( ( pPublishInfo->qos != MQTTQoS0 ) && ( packetId == 0U ) ) + { + LogError( ( "Packet Id is 0 for PUBLISH with QoS=%u.", + ( unsigned int ) pPublishInfo->qos ) ); + status = MQTTBadParameter; + } + else if( ( pPublishInfo->payloadLength > 0U ) && ( pPublishInfo->pPayload == NULL ) ) + { + LogError( ( "A nonzero payload length requires a non-NULL payload: " + "payloadLength=%lu, pPayload=%p.", + ( unsigned long ) pPublishInfo->payloadLength, + pPublishInfo->pPayload ) ); + status = MQTTBadParameter; + } + else if( ( pContext->outgoingPublishRecords == NULL ) && ( pPublishInfo->qos > MQTTQoS0 ) ) + { + LogError( ( "Trying to publish a QoS > MQTTQoS0 packet when outgoing publishes " + "for QoS1/QoS2 have not been enabled. Please, call MQTT_InitStatefulQoS " + "to initialize and enable the use of QoS1/QoS2 publishes." ) ); + status = MQTTBadParameter; + } + else + { + /* MISRA else */ + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTT_Init( MQTTContext_t * pContext, + const TransportInterface_t * pTransportInterface, + MQTTGetCurrentTimeFunc_t getTimeFunction, + MQTTEventCallback_t userCallback, + const MQTTFixedBuffer_t * pNetworkBuffer ) +{ + MQTTStatus_t status = MQTTSuccess; + + /* Validate arguments. */ + if( ( pContext == NULL ) || ( pTransportInterface == NULL ) || + ( pNetworkBuffer == NULL ) ) + { + LogError( ( "Argument cannot be NULL: pContext=%p, " + "pTransportInterface=%p, " + "pNetworkBuffer=%p", + ( void * ) pContext, + ( void * ) pTransportInterface, + ( void * ) pNetworkBuffer ) ); + status = MQTTBadParameter; + } + else if( getTimeFunction == NULL ) + { + LogError( ( "Invalid parameter: getTimeFunction is NULL" ) ); + status = MQTTBadParameter; + } + else if( userCallback == NULL ) + { + LogError( ( "Invalid parameter: userCallback is NULL" ) ); + status = MQTTBadParameter; + } + else if( pTransportInterface->recv == NULL ) + { + LogError( ( "Invalid parameter: pTransportInterface->recv is NULL" ) ); + status = MQTTBadParameter; + } + else if( pTransportInterface->send == NULL ) + { + LogError( ( "Invalid parameter: pTransportInterface->send is NULL" ) ); + status = MQTTBadParameter; + } + else + { + ( void ) memset( pContext, 0x00, sizeof( MQTTContext_t ) ); + + pContext->connectStatus = MQTTNotConnected; + pContext->transportInterface = *pTransportInterface; + pContext->getTime = getTimeFunction; + pContext->appCallback = userCallback; + pContext->networkBuffer = *pNetworkBuffer; + + /* Zero is not a valid packet ID per MQTT spec. Start from 1. */ + pContext->nextPacketId = 1; + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTT_InitStatefulQoS( MQTTContext_t * pContext, + MQTTPubAckInfo_t * pOutgoingPublishRecords, + size_t outgoingPublishCount, + MQTTPubAckInfo_t * pIncomingPublishRecords, + size_t incomingPublishCount ) +{ + MQTTStatus_t status = MQTTSuccess; + + if( pContext == NULL ) + { + LogError( ( "Argument cannot be NULL: pContext=%p\n", + ( void * ) pContext ) ); + status = MQTTBadParameter; + } + + /* Check whether the arguments make sense. Not equal here behaves + * like an exclusive-or operator for boolean values. */ + else if( ( outgoingPublishCount == 0U ) != + ( pOutgoingPublishRecords == NULL ) ) + { + LogError( ( "Arguments do not match: pOutgoingPublishRecords=%p, " + "outgoingPublishCount=%lu", + ( void * ) pOutgoingPublishRecords, + outgoingPublishCount ) ); + status = MQTTBadParameter; + } + + /* Check whether the arguments make sense. Not equal here behaves + * like an exclusive-or operator for boolean values. */ + else if( ( incomingPublishCount == 0U ) != + ( pIncomingPublishRecords == NULL ) ) + { + LogError( ( "Arguments do not match: pIncomingPublishRecords=%p, " + "incomingPublishCount=%lu", + ( void * ) pIncomingPublishRecords, + incomingPublishCount ) ); + status = MQTTBadParameter; + } + else if( pContext->appCallback == NULL ) + { + LogError( ( "MQTT_InitStatefulQoS must be called only after MQTT_Init has" + " been called succesfully.\n" ) ); + status = MQTTBadParameter; + } + else + { + pContext->incomingPublishRecordMaxCount = incomingPublishCount; + pContext->incomingPublishRecords = pIncomingPublishRecords; + pContext->outgoingPublishRecordMaxCount = outgoingPublishCount; + pContext->outgoingPublishRecords = pOutgoingPublishRecords; + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTT_CancelCallback( const MQTTContext_t * pContext, + uint16_t packetId ) +{ + MQTTStatus_t status = MQTTSuccess; + + if( pContext == NULL ) + { + LogWarn( ( "pContext is NULL\n" ) ); + status = MQTTBadParameter; + } + else if( pContext->outgoingPublishRecords == NULL ) + { + LogError( ( "QoS1/QoS2 is not initialized for use. Please, " + "call MQTT_InitStatefulQoS to enable QoS1 and QoS2 " + "publishes.\n" ) ); + status = MQTTBadParameter; + } + else + { + MQTT_PRE_STATE_UPDATE_HOOK( pContext ); + + status = MQTT_RemoveStateRecord( pContext, + packetId ); + + MQTT_POST_STATE_UPDATE_HOOK( pContext ); + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTT_Connect( MQTTContext_t * pContext, + const MQTTConnectInfo_t * pConnectInfo, + const MQTTPublishInfo_t * pWillInfo, + uint32_t timeoutMs, + bool * pSessionPresent ) +{ + size_t remainingLength = 0UL, packetSize = 0UL; + MQTTStatus_t status = MQTTSuccess; + MQTTPacketInfo_t incomingPacket = { 0 }; + + incomingPacket.type = ( uint8_t ) 0; + + if( ( pContext == NULL ) || ( pConnectInfo == NULL ) || ( pSessionPresent == NULL ) ) + { + LogError( ( "Argument cannot be NULL: pContext=%p, " + "pConnectInfo=%p, pSessionPresent=%p.", + ( void * ) pContext, + ( void * ) pConnectInfo, + ( void * ) pSessionPresent ) ); + status = MQTTBadParameter; + } + + if( status == MQTTSuccess ) + { + /* Get MQTT connect packet size and remaining length. */ + status = MQTT_GetConnectPacketSize( pConnectInfo, + pWillInfo, + &remainingLength, + &packetSize ); + LogDebug( ( "CONNECT packet size is %lu and remaining length is %lu.", + ( unsigned long ) packetSize, + ( unsigned long ) remainingLength ) ); + } + + if( status == MQTTSuccess ) + { + MQTT_PRE_SEND_HOOK( pContext ); + + status = sendConnectWithoutCopy( pContext, + pConnectInfo, + pWillInfo, + remainingLength ); + + MQTT_POST_SEND_HOOK( pContext ); + } + + /* Read CONNACK from transport layer. */ + if( status == MQTTSuccess ) + { + status = receiveConnack( pContext, + timeoutMs, + pConnectInfo->cleanSession, + &incomingPacket, + pSessionPresent ); + } + + if( status == MQTTSuccess ) + { + /* Resend PUBRELs when reestablishing a session, or clear records for new sessions. */ + status = handleSessionResumption( pContext, *pSessionPresent ); + } + + if( status == MQTTSuccess ) + { + LogInfo( ( "MQTT connection established with the broker." ) ); + pContext->connectStatus = MQTTConnected; + /* Initialize keep-alive fields after a successful connection. */ + pContext->keepAliveIntervalSec = pConnectInfo->keepAliveSeconds; + pContext->waitingForPingResp = false; + pContext->pingReqSendTimeMs = 0U; + } + else + { + LogError( ( "MQTT connection failed with status = %s.", + MQTT_Status_strerror( status ) ) ); + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTT_Subscribe( MQTTContext_t * pContext, + const MQTTSubscribeInfo_t * pSubscriptionList, + size_t subscriptionCount, + uint16_t packetId ) +{ + size_t remainingLength = 0UL, packetSize = 0UL; + + /* Validate arguments. */ + MQTTStatus_t status = validateSubscribeUnsubscribeParams( pContext, + pSubscriptionList, + subscriptionCount, + packetId ); + + if( status == MQTTSuccess ) + { + /* Get the remaining length and packet size.*/ + status = MQTT_GetSubscribePacketSize( pSubscriptionList, + subscriptionCount, + &remainingLength, + &packetSize ); + LogDebug( ( "SUBSCRIBE packet size is %lu and remaining length is %lu.", + ( unsigned long ) packetSize, + ( unsigned long ) remainingLength ) ); + } + + if( status == MQTTSuccess ) + { + MQTT_PRE_SEND_HOOK( pContext ); + + /* Send MQTT SUBSCRIBE packet. */ + status = sendSubscribeWithoutCopy( pContext, + pSubscriptionList, + subscriptionCount, + packetId, + remainingLength ); + + MQTT_POST_SEND_HOOK( pContext ); + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTT_Publish( MQTTContext_t * pContext, + const MQTTPublishInfo_t * pPublishInfo, + uint16_t packetId ) +{ + size_t headerSize = 0UL; + size_t remainingLength = 0UL; + size_t packetSize = 0UL; + MQTTPublishState_t publishStatus = MQTTStateNull; + bool stateUpdateHookExecuted = false; + + /* Maximum number of bytes required by the 'fixed' part of the PUBLISH + * packet header according to the MQTT specifications. + * Header byte 0 + 1 = 1 + * Length (max) + 4 = 5 + * Topic string length + 2 = 7 + * + * Note that since publish is one of the most common operations in MQTT + * connection, we have moved the topic string length to the 'fixed' part of + * the header so efficiency. Otherwise, we would need an extra vector and + * an extra call to 'send' (in case writev is not defined) to send the + * topic length. */ + uint8_t mqttHeader[ 7U ]; + + /* Validate arguments. */ + MQTTStatus_t status = validatePublishParams( pContext, pPublishInfo, packetId ); + + if( status == MQTTSuccess ) + { + /* Get the remaining length and packet size.*/ + status = MQTT_GetPublishPacketSize( pPublishInfo, + &remainingLength, + &packetSize ); + } + + if( status == MQTTSuccess ) + { + status = MQTT_SerializePublishHeaderWithoutTopic( pPublishInfo, + remainingLength, + mqttHeader, + &headerSize ); + } + + if( ( status == MQTTSuccess ) && ( pPublishInfo->qos > MQTTQoS0 ) ) + { + MQTT_PRE_STATE_UPDATE_HOOK( pContext ); + + /* Set the flag so that the corresponding hook can be called later. */ + stateUpdateHookExecuted = true; + + status = MQTT_ReserveState( pContext, + packetId, + pPublishInfo->qos ); + + /* State already exists for a duplicate packet. + * If a state doesn't exist, it will be handled as a new publish in + * state engine. */ + if( ( status == MQTTStateCollision ) && ( pPublishInfo->dup == true ) ) + { + status = MQTTSuccess; + } + } + + if( status == MQTTSuccess ) + { + /* Take the mutex as multiple send calls are required for sending this + * packet. */ + MQTT_PRE_SEND_HOOK( pContext ); + + status = sendPublishWithoutCopy( pContext, + pPublishInfo, + mqttHeader, + headerSize, + packetId ); + + /* Give the mutex away for the next taker. */ + MQTT_POST_SEND_HOOK( pContext ); + } + + if( ( status == MQTTSuccess ) && + ( pPublishInfo->qos > MQTTQoS0 ) ) + { + /* Update state machine after PUBLISH is sent. + * Only to be done for QoS1 or QoS2. */ + status = MQTT_UpdateStatePublish( pContext, + packetId, + MQTT_SEND, + pPublishInfo->qos, + &publishStatus ); + + if( status != MQTTSuccess ) + { + LogError( ( "Update state for publish failed with status %s." + " However PUBLISH packet was sent to the broker." + " Any further handling of ACKs for the packet Id" + " will fail.", + MQTT_Status_strerror( status ) ) ); + } + } + + if( stateUpdateHookExecuted == true ) + { + /* Regardless of the status, if the mutex was taken due to the + * packet being of QoS > QoS0, then it should be relinquished. */ + MQTT_POST_STATE_UPDATE_HOOK( pContext ); + } + + if( status != MQTTSuccess ) + { + LogError( ( "MQTT PUBLISH failed with status %s.", + MQTT_Status_strerror( status ) ) ); + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTT_Ping( MQTTContext_t * pContext ) +{ + int32_t sendResult = 0; + MQTTStatus_t status = MQTTSuccess; + size_t packetSize = 0U; + /* MQTT ping packets are of fixed length. */ + uint8_t pingreqPacket[ 2U ]; + MQTTFixedBuffer_t localBuffer; + + localBuffer.pBuffer = pingreqPacket; + localBuffer.size = sizeof( pingreqPacket ); + + if( pContext == NULL ) + { + LogError( ( "pContext is NULL." ) ); + status = MQTTBadParameter; + } + + if( status == MQTTSuccess ) + { + /* Get MQTT PINGREQ packet size. */ + status = MQTT_GetPingreqPacketSize( &packetSize ); + + if( status == MQTTSuccess ) + { + assert( packetSize == localBuffer.size ); + LogDebug( ( "MQTT PINGREQ packet size is %lu.", + ( unsigned long ) packetSize ) ); + } + else + { + LogError( ( "Failed to get the PINGREQ packet size." ) ); + } + } + + if( status == MQTTSuccess ) + { + /* Serialize MQTT PINGREQ. */ + status = MQTT_SerializePingreq( &localBuffer ); + } + + if( status == MQTTSuccess ) + { + /* Take the mutex as the send call should not be interrupted in + * between. */ + MQTT_PRE_SEND_HOOK( pContext ); + + /* Send the serialized PINGREQ packet to transport layer. + * Here, we do not use the vectored IO approach for efficiency as the + * Ping packet does not have numerous fields which need to be copied + * from the user provided buffers. Thus it can be sent directly. */ + sendResult = sendBuffer( pContext, + localBuffer.pBuffer, + packetSize ); + + /* Give the mutex away. */ + MQTT_POST_SEND_HOOK( pContext ); + + /* It is an error to not send the entire PINGREQ packet. */ + if( sendResult < ( int32_t ) packetSize ) + { + LogError( ( "Transport send failed for PINGREQ packet." ) ); + status = MQTTSendFailed; + } + else + { + pContext->pingReqSendTimeMs = pContext->lastPacketTxTime; + pContext->waitingForPingResp = true; + LogDebug( ( "Sent %ld bytes of PINGREQ packet.", + ( long int ) sendResult ) ); + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTT_Unsubscribe( MQTTContext_t * pContext, + const MQTTSubscribeInfo_t * pSubscriptionList, + size_t subscriptionCount, + uint16_t packetId ) +{ + size_t remainingLength = 0UL, packetSize = 0UL; + + /* Validate arguments. */ + MQTTStatus_t status = validateSubscribeUnsubscribeParams( pContext, + pSubscriptionList, + subscriptionCount, + packetId ); + + if( status == MQTTSuccess ) + { + /* Get the remaining length and packet size.*/ + status = MQTT_GetUnsubscribePacketSize( pSubscriptionList, + subscriptionCount, + &remainingLength, + &packetSize ); + LogDebug( ( "UNSUBSCRIBE packet size is %lu and remaining length is %lu.", + ( unsigned long ) packetSize, + ( unsigned long ) remainingLength ) ); + } + + if( status == MQTTSuccess ) + { + /* Take the mutex because the below call should not be interrupted. */ + MQTT_PRE_SEND_HOOK( pContext ); + + status = sendUnsubscribeWithoutCopy( pContext, + pSubscriptionList, + subscriptionCount, + packetId, + remainingLength ); + + /* Give the mutex away. */ + MQTT_POST_SEND_HOOK( pContext ); + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTT_Disconnect( MQTTContext_t * pContext ) +{ + size_t packetSize = 0U; + int32_t sendResult = 0; + MQTTStatus_t status = MQTTSuccess; + MQTTFixedBuffer_t localBuffer; + uint8_t disconnectPacket[ 2U ]; + + localBuffer.pBuffer = disconnectPacket; + localBuffer.size = 2U; + + /* Validate arguments. */ + if( pContext == NULL ) + { + LogError( ( "pContext cannot be NULL." ) ); + status = MQTTBadParameter; + } + + if( status == MQTTSuccess ) + { + /* Get MQTT DISCONNECT packet size. */ + status = MQTT_GetDisconnectPacketSize( &packetSize ); + LogDebug( ( "MQTT DISCONNECT packet size is %lu.", + ( unsigned long ) packetSize ) ); + } + + if( status == MQTTSuccess ) + { + /* Serialize MQTT DISCONNECT packet. */ + status = MQTT_SerializeDisconnect( &localBuffer ); + } + + if( status == MQTTSuccess ) + { + /* Take the mutex because the below call should not be interrupted. */ + MQTT_PRE_SEND_HOOK( pContext ); + + /* Here we do not use vectors as the disconnect packet has fixed fields + * which do not reside in user provided buffers. Thus, it can be sent + * using a simple send call. */ + sendResult = sendBuffer( pContext, + localBuffer.pBuffer, + packetSize ); + + /* Give the mutex away. */ + MQTT_POST_SEND_HOOK( pContext ); + + if( sendResult < ( int32_t ) packetSize ) + { + LogError( ( "Transport send failed for DISCONNECT packet." ) ); + status = MQTTSendFailed; + } + else + { + LogDebug( ( "Sent %ld bytes of DISCONNECT packet.", + ( long int ) sendResult ) ); + } + } + + if( status == MQTTSuccess ) + { + LogInfo( ( "Disconnected from the broker." ) ); + pContext->connectStatus = MQTTNotConnected; + + /* Reset the index and clean the buffer on a successful disconnect. */ + pContext->index = 0; + ( void ) memset( pContext->networkBuffer.pBuffer, 0, pContext->networkBuffer.size ); + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTT_ProcessLoop( MQTTContext_t * pContext ) +{ + MQTTStatus_t status = MQTTBadParameter; + + if( pContext == NULL ) + { + LogError( ( "Invalid input parameter: MQTT Context cannot be NULL." ) ); + } + else if( pContext->getTime == NULL ) + { + LogError( ( "Invalid input parameter: MQTT Context must have valid getTime." ) ); + } + else if( pContext->networkBuffer.pBuffer == NULL ) + { + LogError( ( "Invalid input parameter: The MQTT context's networkBuffer must not be NULL." ) ); + } + else + { + pContext->controlPacketSent = false; + status = receiveSingleIteration( pContext, true ); + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTT_ReceiveLoop( MQTTContext_t * pContext ) +{ + MQTTStatus_t status = MQTTBadParameter; + + if( pContext == NULL ) + { + LogError( ( "Invalid input parameter: MQTT Context cannot be NULL." ) ); + } + else if( pContext->getTime == NULL ) + { + LogError( ( "Invalid input parameter: MQTT Context must have a valid getTime function." ) ); + } + else if( pContext->networkBuffer.pBuffer == NULL ) + { + LogError( ( "Invalid input parameter: MQTT context's networkBuffer must not be NULL." ) ); + } + else + { + status = receiveSingleIteration( pContext, false ); + } + + return status; +} + +/*-----------------------------------------------------------*/ + +uint16_t MQTT_GetPacketId( MQTTContext_t * pContext ) +{ + uint16_t packetId = 0U; + + if( pContext != NULL ) + { + MQTT_PRE_STATE_UPDATE_HOOK( pContext ); + + packetId = pContext->nextPacketId; + + /* A packet ID of zero is not a valid packet ID. When the max ID + * is reached the next one should start at 1. */ + if( pContext->nextPacketId == ( uint16_t ) UINT16_MAX ) + { + pContext->nextPacketId = 1; + } + else + { + pContext->nextPacketId++; + } + + MQTT_POST_STATE_UPDATE_HOOK( pContext ); + } + + return packetId; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTT_MatchTopic( const char * pTopicName, + const uint16_t topicNameLength, + const char * pTopicFilter, + const uint16_t topicFilterLength, + bool * pIsMatch ) +{ + MQTTStatus_t status = MQTTSuccess; + bool topicFilterStartsWithWildcard = false; + bool matchStatus = false; + + if( ( pTopicName == NULL ) || ( topicNameLength == 0u ) ) + { + LogError( ( "Invalid paramater: Topic name should be non-NULL and its " + "length should be > 0: TopicName=%p, TopicNameLength=%hu", + ( void * ) pTopicName, + ( unsigned short ) topicNameLength ) ); + + status = MQTTBadParameter; + } + else if( ( pTopicFilter == NULL ) || ( topicFilterLength == 0u ) ) + { + LogError( ( "Invalid paramater: Topic filter should be non-NULL and " + "its length should be > 0: TopicName=%p, TopicFilterLength=%hu", + ( void * ) pTopicFilter, + ( unsigned short ) topicFilterLength ) ); + status = MQTTBadParameter; + } + else if( pIsMatch == NULL ) + { + LogError( ( "Invalid paramater: Output parameter, pIsMatch, is NULL" ) ); + status = MQTTBadParameter; + } + else + { + /* Check for an exact match if the incoming topic name and the registered + * topic filter length match. */ + if( topicNameLength == topicFilterLength ) + { + matchStatus = strncmp( pTopicName, pTopicFilter, topicNameLength ) == 0; + } + + if( matchStatus == false ) + { + /* If an exact match was not found, match against wildcard characters in + * topic filter.*/ + + /* Determine if topic filter starts with a wildcard. */ + topicFilterStartsWithWildcard = ( pTopicFilter[ 0 ] == '+' ) || + ( pTopicFilter[ 0 ] == '#' ); + + /* Note: According to the MQTT 3.1.1 specification, incoming PUBLISH topic names + * starting with "$" character cannot be matched against topic filter starting with + * a wildcard, i.e. for example, "$SYS/sport" cannot be matched with "#" or + * "+/sport" topic filters. */ + if( !( ( pTopicName[ 0 ] == '$' ) && ( topicFilterStartsWithWildcard == true ) ) ) + { + matchStatus = matchTopicFilter( pTopicName, topicNameLength, pTopicFilter, topicFilterLength ); + } + } + + /* Update the output parameter with the match result. */ + *pIsMatch = matchStatus; + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTT_GetSubAckStatusCodes( const MQTTPacketInfo_t * pSubackPacket, + uint8_t ** pPayloadStart, + size_t * pPayloadSize ) +{ + MQTTStatus_t status = MQTTSuccess; + + if( pSubackPacket == NULL ) + { + LogError( ( "Invalid parameter: pSubackPacket is NULL." ) ); + status = MQTTBadParameter; + } + else if( pPayloadStart == NULL ) + { + LogError( ( "Invalid parameter: pPayloadStart is NULL." ) ); + status = MQTTBadParameter; + } + else if( pPayloadSize == NULL ) + { + LogError( ( "Invalid parameter: pPayloadSize is NULL." ) ); + status = MQTTBadParameter; + } + else if( pSubackPacket->type != MQTT_PACKET_TYPE_SUBACK ) + { + LogError( ( "Invalid parameter: Input packet is not a SUBACK packet: " + "ExpectedType=%02x, InputType=%02x", + ( int ) MQTT_PACKET_TYPE_SUBACK, + ( int ) pSubackPacket->type ) ); + status = MQTTBadParameter; + } + else if( pSubackPacket->pRemainingData == NULL ) + { + LogError( ( "Invalid parameter: pSubackPacket->pRemainingData is NULL" ) ); + status = MQTTBadParameter; + } + + /* A SUBACK must have a remaining length of at least 3 to accommodate the + * packet identifier and at least 1 return code. */ + else if( pSubackPacket->remainingLength < 3U ) + { + LogError( ( "Invalid parameter: Packet remaining length is invalid: " + "Should be greater than 2 for SUBACK packet: InputRemainingLength=%lu", + ( unsigned long ) pSubackPacket->remainingLength ) ); + status = MQTTBadParameter; + } + else + { + /* According to the MQTT 3.1.1 protocol specification, the "Remaining Length" field is a + * length of the variable header (2 bytes) plus the length of the payload. + * Therefore, we add 2 positions for the starting address of the payload, and + * subtract 2 bytes from the remaining length for the length of the payload.*/ + *pPayloadStart = &pSubackPacket->pRemainingData[ sizeof( uint16_t ) ]; + *pPayloadSize = pSubackPacket->remainingLength - sizeof( uint16_t ); + } + + return status; +} + +/*-----------------------------------------------------------*/ + +const char * MQTT_Status_strerror( MQTTStatus_t status ) +{ + const char * str = NULL; + + switch( status ) + { + case MQTTSuccess: + str = "MQTTSuccess"; + break; + + case MQTTBadParameter: + str = "MQTTBadParameter"; + break; + + case MQTTNoMemory: + str = "MQTTNoMemory"; + break; + + case MQTTSendFailed: + str = "MQTTSendFailed"; + break; + + case MQTTRecvFailed: + str = "MQTTRecvFailed"; + break; + + case MQTTBadResponse: + str = "MQTTBadResponse"; + break; + + case MQTTServerRefused: + str = "MQTTServerRefused"; + break; + + case MQTTNoDataAvailable: + str = "MQTTNoDataAvailable"; + break; + + case MQTTIllegalState: + str = "MQTTIllegalState"; + break; + + case MQTTStateCollision: + str = "MQTTStateCollision"; + break; + + case MQTTKeepAliveTimeout: + str = "MQTTKeepAliveTimeout"; + break; + + case MQTTNeedMoreBytes: + str = "MQTTNeedMoreBytes"; + break; + + default: + str = "Invalid MQTT Status code"; + break; + } + + return str; +} + +/*-----------------------------------------------------------*/ diff --git a/formatting/goodFiles/source/tasks.c b/formatting/goodFiles/source/tasks.c new file mode 100644 index 00000000..72200ec4 --- /dev/null +++ b/formatting/goodFiles/source/tasks.c @@ -0,0 +1,5532 @@ +/* + * FreeRTOS Kernel + * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS + * + */ + +/* Standard includes. */ +#include +#include + +/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining + * all the API functions to use the MPU wrappers. That should only be done when + * task.h is included from an application file. */ +#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "timers.h" +#include "stack_macros.h" + +/* Lint e9021, e961 and e750 are suppressed as a MISRA exception justified + * because the MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined + * for the header files above, but not in this file, in order to generate the + * correct privileged Vs unprivileged linkage and placement. */ +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e961 !e750 !e9021. */ + +/* Set configUSE_STATS_FORMATTING_FUNCTIONS to 2 to include the stats formatting + * functions but without including stdio.h here. */ +#if ( configUSE_STATS_FORMATTING_FUNCTIONS == 1 ) + +/* At the bottom of this file are two optional functions that can be used + * to generate human readable text from the raw data generated by the + * uxTaskGetSystemState() function. Note the formatting functions are provided + * for convenience only, and are NOT considered part of the kernel. */ + #include +#endif /* configUSE_STATS_FORMATTING_FUNCTIONS == 1 ) */ + +#if ( configUSE_PREEMPTION == 0 ) + +/* If the cooperative scheduler is being used then a yield should not be + * performed just because a higher priority task has been woken. */ + #define taskYIELD_IF_USING_PREEMPTION() +#else + #define taskYIELD_IF_USING_PREEMPTION() portYIELD_WITHIN_API() +#endif + +/* Values that can be assigned to the ucNotifyState member of the TCB. */ +#define taskNOT_WAITING_NOTIFICATION ( ( uint8_t ) 0 ) /* Must be zero as it is the initialised value. */ +#define taskWAITING_NOTIFICATION ( ( uint8_t ) 1 ) +#define taskNOTIFICATION_RECEIVED ( ( uint8_t ) 2 ) + +/* + * The value used to fill the stack of a task when the task is created. This + * is used purely for checking the high water mark for tasks. + */ +#define tskSTACK_FILL_BYTE ( 0xa5U ) + +/* Bits used to record how a task's stack and TCB were allocated. */ +#define tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB ( ( uint8_t ) 0 ) +#define tskSTATICALLY_ALLOCATED_STACK_ONLY ( ( uint8_t ) 1 ) +#define tskSTATICALLY_ALLOCATED_STACK_AND_TCB ( ( uint8_t ) 2 ) + +/* If any of the following are set then task stacks are filled with a known + * value so the high water mark can be determined. If none of the following are + * set then don't fill the stack so there is no unnecessary dependency on memset. */ +#if ( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) ) + #define tskSET_NEW_STACKS_TO_KNOWN_VALUE 1 +#else + #define tskSET_NEW_STACKS_TO_KNOWN_VALUE 0 +#endif + +/* + * Macros used by vListTask to indicate which state a task is in. + */ +#define tskRUNNING_CHAR ( 'X' ) +#define tskBLOCKED_CHAR ( 'B' ) +#define tskREADY_CHAR ( 'R' ) +#define tskDELETED_CHAR ( 'D' ) +#define tskSUSPENDED_CHAR ( 'S' ) + +/* + * Some kernel aware debuggers require the data the debugger needs access to to + * be global, rather than file scope. + */ +#ifdef portREMOVE_STATIC_QUALIFIER + #define static +#endif + +/* The name allocated to the Idle task. This can be overridden by defining + * configIDLE_TASK_NAME in FreeRTOSConfig.h. */ +#ifndef configIDLE_TASK_NAME + #define configIDLE_TASK_NAME "IDLE" +#endif + +#if ( configUSE_PORT_OPTIMISED_TASK_SELECTION == 0 ) + +/* If configUSE_PORT_OPTIMISED_TASK_SELECTION is 0 then task selection is + * performed in a generic way that is not optimised to any particular + * microcontroller architecture. */ + +/* uxTopReadyPriority holds the priority of the highest priority ready + * state task. */ + #define taskRECORD_READY_PRIORITY( uxPriority ) \ + do { \ + if( ( uxPriority ) > uxTopReadyPriority ) \ + { \ + uxTopReadyPriority = ( uxPriority ); \ + } \ + } while( 0 ) /* taskRECORD_READY_PRIORITY */ + +/*-----------------------------------------------------------*/ + + #define taskSELECT_HIGHEST_PRIORITY_TASK() \ + do { \ + UBaseType_t uxTopPriority = uxTopReadyPriority; \ + \ + /* Find the highest priority queue that contains ready tasks. */ \ + while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopPriority ] ) ) ) \ + { \ + configASSERT( uxTopPriority ); \ + --uxTopPriority; \ + } \ + \ + /* listGET_OWNER_OF_NEXT_ENTRY indexes through the list, so the tasks of \ + * the same priority get an equal share of the processor time. */ \ + listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) ); \ + uxTopReadyPriority = uxTopPriority; \ + } while( 0 ) /* taskSELECT_HIGHEST_PRIORITY_TASK */ + +/*-----------------------------------------------------------*/ + +/* Define away taskRESET_READY_PRIORITY() and portRESET_READY_PRIORITY() as + * they are only required when a port optimised method of task selection is + * being used. */ + #define taskRESET_READY_PRIORITY( uxPriority ) + #define portRESET_READY_PRIORITY( uxPriority, uxTopReadyPriority ) + +#else /* configUSE_PORT_OPTIMISED_TASK_SELECTION */ + +/* If configUSE_PORT_OPTIMISED_TASK_SELECTION is 1 then task selection is + * performed in a way that is tailored to the particular microcontroller + * architecture being used. */ + +/* A port optimised version is provided. Call the port defined macros. */ + #define taskRECORD_READY_PRIORITY( uxPriority ) portRECORD_READY_PRIORITY( ( uxPriority ), uxTopReadyPriority ) + +/*-----------------------------------------------------------*/ + + #define taskSELECT_HIGHEST_PRIORITY_TASK() \ + do { \ + UBaseType_t uxTopPriority; \ + \ + /* Find the highest priority list that contains ready tasks. */ \ + portGET_HIGHEST_PRIORITY( uxTopPriority, uxTopReadyPriority ); \ + configASSERT( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ uxTopPriority ] ) ) > 0 ); \ + listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) ); \ + } while( 0 ) + +/*-----------------------------------------------------------*/ + +/* A port optimised version is provided, call it only if the TCB being reset + * is being referenced from a ready list. If it is referenced from a delayed + * or suspended list then it won't be in a ready list. */ + #define taskRESET_READY_PRIORITY( uxPriority ) \ + do { \ + if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ ( uxPriority ) ] ) ) == ( UBaseType_t ) 0 ) \ + { \ + portRESET_READY_PRIORITY( ( uxPriority ), ( uxTopReadyPriority ) ); \ + } \ + } while( 0 ) + +#endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */ + +/*-----------------------------------------------------------*/ + +/* pxDelayedTaskList and pxOverflowDelayedTaskList are switched when the tick + * count overflows. */ +#define taskSWITCH_DELAYED_LISTS() \ + do { \ + List_t * pxTemp; \ + \ + /* The delayed tasks list should be empty when the lists are switched. */ \ + configASSERT( ( listLIST_IS_EMPTY( pxDelayedTaskList ) ) ); \ + \ + pxTemp = pxDelayedTaskList; \ + pxDelayedTaskList = pxOverflowDelayedTaskList; \ + pxOverflowDelayedTaskList = pxTemp; \ + xNumOfOverflows++; \ + prvResetNextTaskUnblockTime(); \ + } while( 0 ) + +/*-----------------------------------------------------------*/ + +/* + * Place the task represented by pxTCB into the appropriate ready list for + * the task. It is inserted at the end of the list. + */ +#define prvAddTaskToReadyList( pxTCB ) \ + do { \ + traceMOVED_TASK_TO_READY_STATE( pxTCB ); \ + taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority ); \ + listINSERT_END( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) ); \ + tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB ); \ + } while( 0 ) +/*-----------------------------------------------------------*/ + +/* + * Several functions take a TaskHandle_t parameter that can optionally be NULL, + * where NULL is used to indicate that the handle of the currently executing + * task should be used in place of the parameter. This macro simply checks to + * see if the parameter is NULL and returns a pointer to the appropriate TCB. + */ +#define prvGetTCBFromHandle( pxHandle ) ( ( ( pxHandle ) == NULL ) ? pxCurrentTCB : ( pxHandle ) ) + +/* The item value of the event list item is normally used to hold the priority + * of the task to which it belongs (coded to allow it to be held in reverse + * priority order). However, it is occasionally borrowed for other purposes. It + * is important its value is not updated due to a task priority change while it is + * being used for another purpose. The following bit definition is used to inform + * the scheduler that the value should not be changed - in which case it is the + * responsibility of whichever module is using the value to ensure it gets set back + * to its original value when it is released. */ +#if ( configTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_16_BITS ) + #define taskEVENT_LIST_ITEM_VALUE_IN_USE 0x8000U +#elif ( configTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_32_BITS ) + #define taskEVENT_LIST_ITEM_VALUE_IN_USE 0x80000000UL +#elif ( configTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_64_BITS ) + #define taskEVENT_LIST_ITEM_VALUE_IN_USE 0x8000000000000000ULL +#endif + +/* + * Task control block. A task control block (TCB) is allocated for each task, + * and stores task state information, including a pointer to the task's context + * (the task's run time environment, including register values) + */ +typedef struct tskTaskControlBlock /* The old naming convention is used to prevent breaking kernel aware debuggers. */ +{ + volatile StackType_t * pxTopOfStack; /**< Points to the location of the last item placed on the tasks stack. THIS MUST BE THE FIRST MEMBER OF THE TCB STRUCT. */ + + #if ( portUSING_MPU_WRAPPERS == 1 ) + xMPU_SETTINGS xMPUSettings; /**< The MPU settings are defined as part of the port layer. THIS MUST BE THE SECOND MEMBER OF THE TCB STRUCT. */ + #endif + + ListItem_t xStateListItem; /**< The list that the state list item of a task is reference from denotes the state of that task (Ready, Blocked, Suspended ). */ + ListItem_t xEventListItem; /**< Used to reference a task from an event list. */ + UBaseType_t uxPriority; /**< The priority of the task. 0 is the lowest priority. */ + StackType_t * pxStack; /**< Points to the start of the stack. */ + char pcTaskName[ configMAX_TASK_NAME_LEN ]; /**< Descriptive name given to the task when created. Facilitates debugging only. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + + #if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) ) + StackType_t * pxEndOfStack; /**< Points to the highest valid address for the stack. */ + #endif + + #if ( portCRITICAL_NESTING_IN_TCB == 1 ) + UBaseType_t uxCriticalNesting; /**< Holds the critical section nesting depth for ports that do not maintain their own count in the port layer. */ + #endif + + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxTCBNumber; /**< Stores a number that increments each time a TCB is created. It allows debuggers to determine when a task has been deleted and then recreated. */ + UBaseType_t uxTaskNumber; /**< Stores a number specifically for use by third party trace code. */ + #endif + + #if ( configUSE_MUTEXES == 1 ) + UBaseType_t uxBasePriority; /**< The priority last assigned to the task - used by the priority inheritance mechanism. */ + UBaseType_t uxMutexesHeld; + #endif + + #if ( configUSE_APPLICATION_TASK_TAG == 1 ) + TaskHookFunction_t pxTaskTag; + #endif + + #if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) + void * pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ]; + #endif + + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + configRUN_TIME_COUNTER_TYPE ulRunTimeCounter; /**< Stores the amount of time the task has spent in the Running state. */ + #endif + + #if ( configUSE_C_RUNTIME_TLS_SUPPORT == 1 ) + configTLS_BLOCK_TYPE xTLSBlock; /**< Memory block used as Thread Local Storage (TLS) Block for the task. */ + #endif + + #if ( configUSE_TASK_NOTIFICATIONS == 1 ) + volatile uint32_t ulNotifiedValue[ configTASK_NOTIFICATION_ARRAY_ENTRIES ]; + volatile uint8_t ucNotifyState[ configTASK_NOTIFICATION_ARRAY_ENTRIES ]; + #endif + + /* See the comments in FreeRTOS.h with the definition of + * tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE. */ + #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e731 !e9029 Macro has been consolidated for readability reasons. */ + uint8_t ucStaticallyAllocated; /**< Set to pdTRUE if the task is a statically allocated to ensure no attempt is made to free the memory. */ + #endif + + #if ( INCLUDE_xTaskAbortDelay == 1 ) + uint8_t ucDelayAborted; + #endif + + #if ( configUSE_POSIX_ERRNO == 1 ) + int iTaskErrno; + #endif +} tskTCB; + +/* The old tskTCB name is maintained above then typedefed to the new TCB_t name + * below to enable the use of older kernel aware debuggers. */ +typedef tskTCB TCB_t; + +/*lint -save -e956 A manual analysis and inspection has been used to determine + * which static variables must be declared volatile. */ +portDONT_DISCARD PRIVILEGED_DATA TCB_t * volatile pxCurrentTCB = NULL; + +/* Lists for ready and blocked tasks. -------------------- + * xDelayedTaskList1 and xDelayedTaskList2 could be moved to function scope but + * doing so breaks some kernel aware debuggers and debuggers that rely on removing + * the static qualifier. */ +PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ]; /**< Prioritised ready tasks. */ +PRIVILEGED_DATA static List_t xDelayedTaskList1; /**< Delayed tasks. */ +PRIVILEGED_DATA static List_t xDelayedTaskList2; /**< Delayed tasks (two lists are used - one for delays that have overflowed the current tick count. */ +PRIVILEGED_DATA static List_t * volatile pxDelayedTaskList; /**< Points to the delayed task list currently being used. */ +PRIVILEGED_DATA static List_t * volatile pxOverflowDelayedTaskList; /**< Points to the delayed task list currently being used to hold tasks that have overflowed the current tick count. */ +PRIVILEGED_DATA static List_t xPendingReadyList; /**< Tasks that have been readied while the scheduler was suspended. They will be moved to the ready list when the scheduler is resumed. */ + +#if ( INCLUDE_vTaskDelete == 1 ) + + PRIVILEGED_DATA static List_t xTasksWaitingTermination; /**< Tasks that have been deleted - but their memory not yet freed. */ + PRIVILEGED_DATA static volatile UBaseType_t uxDeletedTasksWaitingCleanUp = ( UBaseType_t ) 0U; + +#endif + +#if ( INCLUDE_vTaskSuspend == 1 ) + + PRIVILEGED_DATA static List_t xSuspendedTaskList; /**< Tasks that are currently suspended. */ + +#endif + +/* Global POSIX errno. Its value is changed upon context switching to match + * the errno of the currently running task. */ +#if ( configUSE_POSIX_ERRNO == 1 ) + int FreeRTOS_errno = 0; +#endif + +/* Other file private variables. --------------------------------*/ +PRIVILEGED_DATA static volatile UBaseType_t uxCurrentNumberOfTasks = ( UBaseType_t ) 0U; +PRIVILEGED_DATA static volatile TickType_t xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT; +PRIVILEGED_DATA static volatile UBaseType_t uxTopReadyPriority = tskIDLE_PRIORITY; +PRIVILEGED_DATA static volatile BaseType_t xSchedulerRunning = pdFALSE; +PRIVILEGED_DATA static volatile TickType_t xPendedTicks = ( TickType_t ) 0U; +PRIVILEGED_DATA static volatile BaseType_t xYieldPending = pdFALSE; +PRIVILEGED_DATA static volatile BaseType_t xNumOfOverflows = ( BaseType_t ) 0; +PRIVILEGED_DATA static UBaseType_t uxTaskNumber = ( UBaseType_t ) 0U; +PRIVILEGED_DATA static volatile TickType_t xNextTaskUnblockTime = ( TickType_t ) 0U; /* Initialised to portMAX_DELAY before the scheduler starts. */ +PRIVILEGED_DATA static TaskHandle_t xIdleTaskHandle = NULL; /**< Holds the handle of the idle task. The idle task is created automatically when the scheduler is started. */ + +/* Improve support for OpenOCD. The kernel tracks Ready tasks via priority lists. + * For tracking the state of remote threads, OpenOCD uses uxTopUsedPriority + * to determine the number of priority lists to read back from the remote target. */ +const volatile UBaseType_t uxTopUsedPriority = configMAX_PRIORITIES - 1U; + +/* Context switches are held pending while the scheduler is suspended. Also, + * interrupts must not manipulate the xStateListItem of a TCB, or any of the + * lists the xStateListItem can be referenced from, if the scheduler is suspended. + * If an interrupt needs to unblock a task while the scheduler is suspended then it + * moves the task's event list item into the xPendingReadyList, ready for the + * kernel to move the task from the pending ready list into the real ready list + * when the scheduler is unsuspended. The pending ready list itself can only be + * accessed from a critical section. */ +PRIVILEGED_DATA static volatile UBaseType_t uxSchedulerSuspended = ( UBaseType_t ) 0U; + +#if ( configGENERATE_RUN_TIME_STATS == 1 ) + +/* Do not move these variables to function scope as doing so prevents the + * code working with debuggers that need to remove the static qualifier. */ + PRIVILEGED_DATA static configRUN_TIME_COUNTER_TYPE ulTaskSwitchedInTime = 0UL; /**< Holds the value of a timer/counter the last time a task was switched in. */ + PRIVILEGED_DATA static volatile configRUN_TIME_COUNTER_TYPE ulTotalRunTime = 0UL; /**< Holds the total amount of execution time as defined by the run time counter clock. */ + +#endif + +/*lint -restore */ + +/*-----------------------------------------------------------*/ + +/* File private functions. --------------------------------*/ + +/** + * Utility task that simply returns pdTRUE if the task referenced by xTask is + * currently in the Suspended state, or pdFALSE if the task referenced by xTask + * is in any other state. + */ +#if ( INCLUDE_vTaskSuspend == 1 ) + + static BaseType_t prvTaskIsTaskSuspended( const TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +#endif /* INCLUDE_vTaskSuspend */ + +/* + * Utility to ready all the lists used by the scheduler. This is called + * automatically upon the creation of the first task. + */ +static void prvInitialiseTaskLists( void ) PRIVILEGED_FUNCTION; + +/* + * The idle task, which as all tasks is implemented as a never ending loop. + * The idle task is automatically created and added to the ready lists upon + * creation of the first user task. + * + * The portTASK_FUNCTION_PROTO() macro is used to allow port/compiler specific + * language extensions. The equivalent prototype for this function is: + * + * void prvIdleTask( void *pvParameters ); + * + */ +static portTASK_FUNCTION_PROTO( prvIdleTask, pvParameters ) PRIVILEGED_FUNCTION; + +/* + * Utility to free all memory allocated by the scheduler to hold a TCB, + * including the stack pointed to by the TCB. + * + * This does not free memory allocated by the task itself (i.e. memory + * allocated by calls to pvPortMalloc from within the tasks application code). + */ +#if ( INCLUDE_vTaskDelete == 1 ) + + static void prvDeleteTCB( TCB_t * pxTCB ) PRIVILEGED_FUNCTION; + +#endif + +/* + * Used only by the idle task. This checks to see if anything has been placed + * in the list of tasks waiting to be deleted. If so the task is cleaned up + * and its TCB deleted. + */ +static void prvCheckTasksWaitingTermination( void ) PRIVILEGED_FUNCTION; + +/* + * The currently executing task is entering the Blocked state. Add the task to + * either the current or the overflow delayed task list. + */ +static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait, + const BaseType_t xCanBlockIndefinitely ) PRIVILEGED_FUNCTION; + +/* + * Fills an TaskStatus_t structure with information on each task that is + * referenced from the pxList list (which may be a ready list, a delayed list, + * a suspended list, etc.). + * + * THIS FUNCTION IS INTENDED FOR DEBUGGING ONLY, AND SHOULD NOT BE CALLED FROM + * NORMAL APPLICATION CODE. + */ +#if ( configUSE_TRACE_FACILITY == 1 ) + + static UBaseType_t prvListTasksWithinSingleList( TaskStatus_t * pxTaskStatusArray, + List_t * pxList, + eTaskState eState ) PRIVILEGED_FUNCTION; + +#endif + +/* + * Searches pxList for a task with name pcNameToQuery - returning a handle to + * the task if it is found, or NULL if the task is not found. + */ +#if ( INCLUDE_xTaskGetHandle == 1 ) + + static TCB_t * prvSearchForNameWithinSingleList( List_t * pxList, + const char pcNameToQuery[] ) PRIVILEGED_FUNCTION; + +#endif + +/* + * When a task is created, the stack of the task is filled with a known value. + * This function determines the 'high water mark' of the task stack by + * determining how much of the stack remains at the original preset value. + */ +#if ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) ) + + static configSTACK_DEPTH_TYPE prvTaskCheckFreeStackSpace( const uint8_t * pucStackByte ) PRIVILEGED_FUNCTION; + +#endif + +/* + * Return the amount of time, in ticks, that will pass before the kernel will + * next move a task from the Blocked state to the Running state. + * + * This conditional compilation should use inequality to 0, not equality to 1. + * This is to ensure portSUPPRESS_TICKS_AND_SLEEP() can be called when user + * defined low power mode implementations require configUSE_TICKLESS_IDLE to be + * set to a value other than 1. + */ +#if ( configUSE_TICKLESS_IDLE != 0 ) + + static TickType_t prvGetExpectedIdleTime( void ) PRIVILEGED_FUNCTION; + +#endif + +/* + * Set xNextTaskUnblockTime to the time at which the next Blocked state task + * will exit the Blocked state. + */ +static void prvResetNextTaskUnblockTime( void ) PRIVILEGED_FUNCTION; + +#if ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) + +/* + * Helper function used to pad task names with spaces when printing out + * human readable tables of task information. + */ + static char * prvWriteNameToBuffer( char * pcBuffer, + const char * pcTaskName ) PRIVILEGED_FUNCTION; + +#endif + +/* + * Called after a Task_t structure has been allocated either statically or + * dynamically to fill in the structure's members. + */ +static void prvInitialiseNewTask( TaskFunction_t pxTaskCode, + const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const uint32_t ulStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pxCreatedTask, + TCB_t * pxNewTCB, + const MemoryRegion_t * const xRegions ) PRIVILEGED_FUNCTION; + +/* + * Called after a new task has been created and initialised to place the task + * under the control of the scheduler. + */ +static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB ) PRIVILEGED_FUNCTION; + +/* + * freertos_tasks_c_additions_init() should only be called if the user definable + * macro FREERTOS_TASKS_C_ADDITIONS_INIT() is defined, as that is the only macro + * called by the function. + */ +#ifdef FREERTOS_TASKS_C_ADDITIONS_INIT + + static void freertos_tasks_c_additions_init( void ) PRIVILEGED_FUNCTION; + +#endif + +/*-----------------------------------------------------------*/ + +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + + TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode, + const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const uint32_t ulStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + StackType_t * const puxStackBuffer, + StaticTask_t * const pxTaskBuffer ) + { + TCB_t * pxNewTCB; + TaskHandle_t xReturn; + + configASSERT( puxStackBuffer != NULL ); + configASSERT( pxTaskBuffer != NULL ); + + #if ( configASSERT_DEFINED == 1 ) + { + /* Sanity check that the size of the structure used to declare a + * variable of type StaticTask_t equals the size of the real task + * structure. */ + volatile size_t xSize = sizeof( StaticTask_t ); + configASSERT( xSize == sizeof( TCB_t ) ); + ( void ) xSize; /* Prevent lint warning when configASSERT() is not used. */ + } + #endif /* configASSERT_DEFINED */ + + if( ( pxTaskBuffer != NULL ) && ( puxStackBuffer != NULL ) ) + { + /* The memory used for the task's TCB and stack are passed into this + * function - use them. */ + pxNewTCB = ( TCB_t * ) pxTaskBuffer; /*lint !e740 !e9087 Unusual cast is ok as the structures are designed to have the same alignment, and the size is checked by an assert. */ + memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) ); + pxNewTCB->pxStack = ( StackType_t * ) puxStackBuffer; + + #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e731 !e9029 Macro has been consolidated for readability reasons. */ + { + /* Tasks can be created statically or dynamically, so note this + * task was created statically in case the task is later deleted. */ + pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_AND_TCB; + } + #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */ + + prvInitialiseNewTask( pxTaskCode, pcName, ulStackDepth, pvParameters, uxPriority, &xReturn, pxNewTCB, NULL ); + prvAddNewTaskToReadyList( pxNewTCB ); + } + else + { + xReturn = NULL; + } + + return xReturn; + } + +#endif /* SUPPORT_STATIC_ALLOCATION */ +/*-----------------------------------------------------------*/ + +#if ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + + BaseType_t xTaskCreateRestrictedStatic( const TaskParameters_t * const pxTaskDefinition, + TaskHandle_t * pxCreatedTask ) + { + TCB_t * pxNewTCB; + BaseType_t xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; + + configASSERT( pxTaskDefinition->puxStackBuffer != NULL ); + configASSERT( pxTaskDefinition->pxTaskBuffer != NULL ); + + if( ( pxTaskDefinition->puxStackBuffer != NULL ) && ( pxTaskDefinition->pxTaskBuffer != NULL ) ) + { + /* Allocate space for the TCB. Where the memory comes from depends + * on the implementation of the port malloc function and whether or + * not static allocation is being used. */ + pxNewTCB = ( TCB_t * ) pxTaskDefinition->pxTaskBuffer; + memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) ); + + /* Store the stack location in the TCB. */ + pxNewTCB->pxStack = pxTaskDefinition->puxStackBuffer; + + #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) + { + /* Tasks can be created statically or dynamically, so note this + * task was created statically in case the task is later deleted. */ + pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_AND_TCB; + } + #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */ + + prvInitialiseNewTask( pxTaskDefinition->pvTaskCode, + pxTaskDefinition->pcName, + ( uint32_t ) pxTaskDefinition->usStackDepth, + pxTaskDefinition->pvParameters, + pxTaskDefinition->uxPriority, + pxCreatedTask, pxNewTCB, + pxTaskDefinition->xRegions ); + + prvAddNewTaskToReadyList( pxNewTCB ); + xReturn = pdPASS; + } + + return xReturn; + } + +#endif /* ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) */ +/*-----------------------------------------------------------*/ + +#if ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + + BaseType_t xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition, + TaskHandle_t * pxCreatedTask ) + { + TCB_t * pxNewTCB; + BaseType_t xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; + + configASSERT( pxTaskDefinition->puxStackBuffer ); + + if( pxTaskDefinition->puxStackBuffer != NULL ) + { + /* Allocate space for the TCB. Where the memory comes from depends + * on the implementation of the port malloc function and whether or + * not static allocation is being used. */ + pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); + + if( pxNewTCB != NULL ) + { + memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) ); + + /* Store the stack location in the TCB. */ + pxNewTCB->pxStack = pxTaskDefinition->puxStackBuffer; + + #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) + { + /* Tasks can be created statically or dynamically, so note + * this task had a statically allocated stack in case it is + * later deleted. The TCB was allocated dynamically. */ + pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_ONLY; + } + #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */ + + prvInitialiseNewTask( pxTaskDefinition->pvTaskCode, + pxTaskDefinition->pcName, + ( uint32_t ) pxTaskDefinition->usStackDepth, + pxTaskDefinition->pvParameters, + pxTaskDefinition->uxPriority, + pxCreatedTask, pxNewTCB, + pxTaskDefinition->xRegions ); + + prvAddNewTaskToReadyList( pxNewTCB ); + xReturn = pdPASS; + } + } + + return xReturn; + } + +#endif /* portUSING_MPU_WRAPPERS */ +/*-----------------------------------------------------------*/ + +#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + + BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, + const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const configSTACK_DEPTH_TYPE usStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pxCreatedTask ) + { + TCB_t * pxNewTCB; + BaseType_t xReturn; + + /* If the stack grows down then allocate the stack then the TCB so the stack + * does not grow into the TCB. Likewise if the stack grows up then allocate + * the TCB then the stack. */ + #if ( portSTACK_GROWTH > 0 ) + { + /* Allocate space for the TCB. Where the memory comes from depends on + * the implementation of the port malloc function and whether or not static + * allocation is being used. */ + pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); + + if( pxNewTCB != NULL ) + { + memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) ); + + /* Allocate space for the stack used by the task being created. + * The base of the stack memory stored in the TCB so the task can + * be deleted later if required. */ + pxNewTCB->pxStack = ( StackType_t * ) pvPortMallocStack( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + + if( pxNewTCB->pxStack == NULL ) + { + /* Could not allocate the stack. Delete the allocated TCB. */ + vPortFree( pxNewTCB ); + pxNewTCB = NULL; + } + } + } + #else /* portSTACK_GROWTH */ + { + StackType_t * pxStack; + + /* Allocate space for the stack used by the task being created. */ + pxStack = pvPortMallocStack( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack and this allocation is the stack. */ + + if( pxStack != NULL ) + { + /* Allocate space for the TCB. */ + pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); /*lint !e9087 !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack, and the first member of TCB_t is always a pointer to the task's stack. */ + + if( pxNewTCB != NULL ) + { + memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) ); + + /* Store the stack location in the TCB. */ + pxNewTCB->pxStack = pxStack; + } + else + { + /* The stack cannot be used as the TCB was not created. Free + * it again. */ + vPortFreeStack( pxStack ); + } + } + else + { + pxNewTCB = NULL; + } + } + #endif /* portSTACK_GROWTH */ + + if( pxNewTCB != NULL ) + { + #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e9029 !e731 Macro has been consolidated for readability reasons. */ + { + /* Tasks can be created statically or dynamically, so note this + * task was created dynamically in case it is later deleted. */ + pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB; + } + #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */ + + prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL ); + prvAddNewTaskToReadyList( pxNewTCB ); + xReturn = pdPASS; + } + else + { + xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; + } + + return xReturn; + } + +#endif /* configSUPPORT_DYNAMIC_ALLOCATION */ +/*-----------------------------------------------------------*/ + +static void prvInitialiseNewTask( TaskFunction_t pxTaskCode, + const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const uint32_t ulStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pxCreatedTask, + TCB_t * pxNewTCB, + const MemoryRegion_t * const xRegions ) +{ + StackType_t * pxTopOfStack; + UBaseType_t x; + + #if ( portUSING_MPU_WRAPPERS == 1 ) + /* Should the task be created in privileged mode? */ + BaseType_t xRunPrivileged; + + if( ( uxPriority & portPRIVILEGE_BIT ) != 0U ) + { + xRunPrivileged = pdTRUE; + } + else + { + xRunPrivileged = pdFALSE; + } + uxPriority &= ~portPRIVILEGE_BIT; + #endif /* portUSING_MPU_WRAPPERS == 1 */ + + /* Avoid dependency on memset() if it is not required. */ + #if ( tskSET_NEW_STACKS_TO_KNOWN_VALUE == 1 ) + { + /* Fill the stack with a known value to assist debugging. */ + ( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) ); + } + #endif /* tskSET_NEW_STACKS_TO_KNOWN_VALUE */ + + /* Calculate the top of stack address. This depends on whether the stack + * grows from high memory to low (as per the 80x86) or vice versa. + * portSTACK_GROWTH is used to make the result positive or negative as required + * by the port. */ + #if ( portSTACK_GROWTH < 0 ) + { + pxTopOfStack = &( pxNewTCB->pxStack[ ulStackDepth - ( uint32_t ) 1 ] ); + pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); /*lint !e923 !e9033 !e9078 MISRA exception. Avoiding casts between pointers and integers is not practical. Size differences accounted for using portPOINTER_SIZE_TYPE type. Checked by assert(). */ + + /* Check the alignment of the calculated top of stack is correct. */ + configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) ); + + #if ( configRECORD_STACK_HIGH_ADDRESS == 1 ) + { + /* Also record the stack's high address, which may assist + * debugging. */ + pxNewTCB->pxEndOfStack = pxTopOfStack; + } + #endif /* configRECORD_STACK_HIGH_ADDRESS */ + } + #else /* portSTACK_GROWTH */ + { + pxTopOfStack = pxNewTCB->pxStack; + + /* Check the alignment of the stack buffer is correct. */ + configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxNewTCB->pxStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) ); + + /* The other extreme of the stack space is required if stack checking is + * performed. */ + pxNewTCB->pxEndOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 ); + } + #endif /* portSTACK_GROWTH */ + + /* Store the task name in the TCB. */ + if( pcName != NULL ) + { + for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ ) + { + pxNewTCB->pcTaskName[ x ] = pcName[ x ]; + + /* Don't copy all configMAX_TASK_NAME_LEN if the string is shorter than + * configMAX_TASK_NAME_LEN characters just in case the memory after the + * string is not accessible (extremely unlikely). */ + if( pcName[ x ] == ( char ) 0x00 ) + { + break; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + /* Ensure the name string is terminated in the case that the string length + * was greater or equal to configMAX_TASK_NAME_LEN. */ + pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0'; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* This is used as an array index so must ensure it's not too large. */ + configASSERT( uxPriority < configMAX_PRIORITIES ); + + if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES ) + { + uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + pxNewTCB->uxPriority = uxPriority; + #if ( configUSE_MUTEXES == 1 ) + { + pxNewTCB->uxBasePriority = uxPriority; + } + #endif /* configUSE_MUTEXES */ + + vListInitialiseItem( &( pxNewTCB->xStateListItem ) ); + vListInitialiseItem( &( pxNewTCB->xEventListItem ) ); + + /* Set the pxNewTCB as a link back from the ListItem_t. This is so we can get + * back to the containing TCB from a generic item in a list. */ + listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB ); + + /* Event lists are always in priority order. */ + listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB ); + + #if ( portUSING_MPU_WRAPPERS == 1 ) + { + vPortStoreTaskMPUSettings( &( pxNewTCB->xMPUSettings ), xRegions, pxNewTCB->pxStack, ulStackDepth ); + } + #else + { + /* Avoid compiler warning about unreferenced parameter. */ + ( void ) xRegions; + } + #endif + + #if ( configUSE_C_RUNTIME_TLS_SUPPORT == 1 ) + { + /* Allocate and initialize memory for the task's TLS Block. */ + configINIT_TLS_BLOCK( pxNewTCB->xTLSBlock, pxTopOfStack ); + } + #endif + + /* Initialize the TCB stack to look as if the task was already running, + * but had been interrupted by the scheduler. The return address is set + * to the start of the task function. Once the stack has been initialised + * the top of stack variable is updated. */ + #if ( portUSING_MPU_WRAPPERS == 1 ) + { + /* If the port has capability to detect stack overflow, + * pass the stack end address to the stack initialization + * function as well. */ + #if ( portHAS_STACK_OVERFLOW_CHECKING == 1 ) + { + #if ( portSTACK_GROWTH < 0 ) + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxStack, pxTaskCode, pvParameters, xRunPrivileged, &( pxNewTCB->xMPUSettings ) ); + } + #else /* portSTACK_GROWTH */ + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxEndOfStack, pxTaskCode, pvParameters, xRunPrivileged, &( pxNewTCB->xMPUSettings ) ); + } + #endif /* portSTACK_GROWTH */ + } + #else /* portHAS_STACK_OVERFLOW_CHECKING */ + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters, xRunPrivileged, &( pxNewTCB->xMPUSettings ) ); + } + #endif /* portHAS_STACK_OVERFLOW_CHECKING */ + } + #else /* portUSING_MPU_WRAPPERS */ + { + /* If the port has capability to detect stack overflow, + * pass the stack end address to the stack initialization + * function as well. */ + #if ( portHAS_STACK_OVERFLOW_CHECKING == 1 ) + { + #if ( portSTACK_GROWTH < 0 ) + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxStack, pxTaskCode, pvParameters ); + } + #else /* portSTACK_GROWTH */ + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxEndOfStack, pxTaskCode, pvParameters ); + } + #endif /* portSTACK_GROWTH */ + } + #else /* portHAS_STACK_OVERFLOW_CHECKING */ + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters ); + } + #endif /* portHAS_STACK_OVERFLOW_CHECKING */ + } + #endif /* portUSING_MPU_WRAPPERS */ + + if( pxCreatedTask != NULL ) + { + /* Pass the handle out in an anonymous way. The handle can be used to + * change the created task's priority, delete the created task, etc.*/ + *pxCreatedTask = ( TaskHandle_t ) pxNewTCB; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } +} +/*-----------------------------------------------------------*/ + +static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB ) +{ + /* Ensure interrupts don't access the task lists while the lists are being + * updated. */ + taskENTER_CRITICAL(); + { + uxCurrentNumberOfTasks++; + + if( pxCurrentTCB == NULL ) + { + /* There are no other tasks, or all the other tasks are in + * the suspended state - make this the current task. */ + pxCurrentTCB = pxNewTCB; + + if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 ) + { + /* This is the first task to be created so do the preliminary + * initialisation required. We will not recover if this call + * fails, but we will report the failure. */ + prvInitialiseTaskLists(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* If the scheduler is not already running, make this task the + * current task if it is the highest priority task to be created + * so far. */ + if( xSchedulerRunning == pdFALSE ) + { + if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority ) + { + pxCurrentTCB = pxNewTCB; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + uxTaskNumber++; + + #if ( configUSE_TRACE_FACILITY == 1 ) + { + /* Add a counter into the TCB for tracing only. */ + pxNewTCB->uxTCBNumber = uxTaskNumber; + } + #endif /* configUSE_TRACE_FACILITY */ + traceTASK_CREATE( pxNewTCB ); + + prvAddTaskToReadyList( pxNewTCB ); + + portSETUP_TCB( pxNewTCB ); + } + taskEXIT_CRITICAL(); + + if( xSchedulerRunning != pdFALSE ) + { + /* If the created task is of a higher priority than the current task + * then it should run now. */ + if( pxCurrentTCB->uxPriority < pxNewTCB->uxPriority ) + { + taskYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } +} +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskDelete == 1 ) + + void vTaskDelete( TaskHandle_t xTaskToDelete ) + { + TCB_t * pxTCB; + + taskENTER_CRITICAL(); + { + /* If null is passed in here then it is the calling task that is + * being deleted. */ + pxTCB = prvGetTCBFromHandle( xTaskToDelete ); + + /* Remove task from the ready/delayed list. */ + if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) + { + taskRESET_READY_PRIORITY( pxTCB->uxPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Is the task waiting on an event also? */ + if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) + { + ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Increment the uxTaskNumber also so kernel aware debuggers can + * detect that the task lists need re-generating. This is done before + * portPRE_TASK_DELETE_HOOK() as in the Windows port that macro will + * not return. */ + uxTaskNumber++; + + if( pxTCB == pxCurrentTCB ) + { + /* A task is deleting itself. This cannot complete within the + * task itself, as a context switch to another task is required. + * Place the task in the termination list. The idle task will + * check the termination list and free up any memory allocated by + * the scheduler for the TCB and stack of the deleted task. */ + vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) ); + + /* Increment the ucTasksDeleted variable so the idle task knows + * there is a task that has been deleted and that it should therefore + * check the xTasksWaitingTermination list. */ + ++uxDeletedTasksWaitingCleanUp; + + /* Call the delete hook before portPRE_TASK_DELETE_HOOK() as + * portPRE_TASK_DELETE_HOOK() does not return in the Win32 port. */ + traceTASK_DELETE( pxTCB ); + + /* The pre-delete hook is primarily for the Windows simulator, + * in which Windows specific clean up operations are performed, + * after which it is not possible to yield away from this task - + * hence xYieldPending is used to latch that a context switch is + * required. */ + portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending ); + } + else + { + --uxCurrentNumberOfTasks; + traceTASK_DELETE( pxTCB ); + + /* Reset the next expected unblock time in case it referred to + * the task that has just been deleted. */ + prvResetNextTaskUnblockTime(); + } + } + taskEXIT_CRITICAL(); + + /* If the task is not deleting itself, call prvDeleteTCB from outside of + * critical section. If a task deletes itself, prvDeleteTCB is called + * from prvCheckTasksWaitingTermination which is called from Idle task. */ + if( pxTCB != pxCurrentTCB ) + { + prvDeleteTCB( pxTCB ); + } + + /* Force a reschedule if it is the currently running task that has just + * been deleted. */ + if( xSchedulerRunning != pdFALSE ) + { + if( pxTCB == pxCurrentTCB ) + { + configASSERT( uxSchedulerSuspended == ( UBaseType_t ) 0U ); + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + +#endif /* INCLUDE_vTaskDelete */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_xTaskDelayUntil == 1 ) + + BaseType_t xTaskDelayUntil( TickType_t * const pxPreviousWakeTime, + const TickType_t xTimeIncrement ) + { + TickType_t xTimeToWake; + BaseType_t xAlreadyYielded, xShouldDelay = pdFALSE; + + configASSERT( pxPreviousWakeTime ); + configASSERT( ( xTimeIncrement > 0U ) ); + configASSERT( uxSchedulerSuspended == ( UBaseType_t ) 0U ); + + vTaskSuspendAll(); + { + /* Minor optimisation. The tick count cannot change in this + * block. */ + const TickType_t xConstTickCount = xTickCount; + + /* Generate the tick time at which the task wants to wake. */ + xTimeToWake = *pxPreviousWakeTime + xTimeIncrement; + + if( xConstTickCount < *pxPreviousWakeTime ) + { + /* The tick count has overflowed since this function was + * lasted called. In this case the only time we should ever + * actually delay is if the wake time has also overflowed, + * and the wake time is greater than the tick time. When this + * is the case it is as if neither time had overflowed. */ + if( ( xTimeToWake < *pxPreviousWakeTime ) && ( xTimeToWake > xConstTickCount ) ) + { + xShouldDelay = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* The tick time has not overflowed. In this case we will + * delay if either the wake time has overflowed, and/or the + * tick time is less than the wake time. */ + if( ( xTimeToWake < *pxPreviousWakeTime ) || ( xTimeToWake > xConstTickCount ) ) + { + xShouldDelay = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + /* Update the wake time ready for the next call. */ + *pxPreviousWakeTime = xTimeToWake; + + if( xShouldDelay != pdFALSE ) + { + traceTASK_DELAY_UNTIL( xTimeToWake ); + + /* prvAddCurrentTaskToDelayedList() needs the block time, not + * the time to wake, so subtract the current tick count. */ + prvAddCurrentTaskToDelayedList( xTimeToWake - xConstTickCount, pdFALSE ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + xAlreadyYielded = xTaskResumeAll(); + + /* Force a reschedule if xTaskResumeAll has not already done so, we may + * have put ourselves to sleep. */ + if( xAlreadyYielded == pdFALSE ) + { + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return xShouldDelay; + } + +#endif /* INCLUDE_xTaskDelayUntil */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskDelay == 1 ) + + void vTaskDelay( const TickType_t xTicksToDelay ) + { + BaseType_t xAlreadyYielded = pdFALSE; + + /* A delay time of zero just forces a reschedule. */ + if( xTicksToDelay > ( TickType_t ) 0U ) + { + configASSERT( uxSchedulerSuspended == ( UBaseType_t ) 0U ); + vTaskSuspendAll(); + { + traceTASK_DELAY(); + + /* A task that is removed from the event list while the + * scheduler is suspended will not get placed in the ready + * list or removed from the blocked list until the scheduler + * is resumed. + * + * This task cannot be in an event list as it is the currently + * executing task. */ + prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE ); + } + xAlreadyYielded = xTaskResumeAll(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Force a reschedule if xTaskResumeAll has not already done so, we may + * have put ourselves to sleep. */ + if( xAlreadyYielded == pdFALSE ) + { + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + +#endif /* INCLUDE_vTaskDelay */ +/*-----------------------------------------------------------*/ + +#if ( ( INCLUDE_eTaskGetState == 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_xTaskAbortDelay == 1 ) ) + + eTaskState eTaskGetState( TaskHandle_t xTask ) + { + eTaskState eReturn; + List_t const * pxStateList; + List_t const * pxEventList; + List_t const * pxDelayedList; + List_t const * pxOverflowedDelayedList; + const TCB_t * const pxTCB = xTask; + + configASSERT( pxTCB ); + + if( pxTCB == pxCurrentTCB ) + { + /* The task calling this function is querying its own state. */ + eReturn = eRunning; + } + else + { + taskENTER_CRITICAL(); + { + pxStateList = listLIST_ITEM_CONTAINER( &( pxTCB->xStateListItem ) ); + pxEventList = listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ); + pxDelayedList = pxDelayedTaskList; + pxOverflowedDelayedList = pxOverflowDelayedTaskList; + } + taskEXIT_CRITICAL(); + + if( pxEventList == &xPendingReadyList ) + { + /* The task has been placed on the pending ready list, so its + * state is eReady regardless of what list the task's state list + * item is currently placed on. */ + eReturn = eReady; + } + else if( ( pxStateList == pxDelayedList ) || ( pxStateList == pxOverflowedDelayedList ) ) + { + /* The task being queried is referenced from one of the Blocked + * lists. */ + eReturn = eBlocked; + } + + #if ( INCLUDE_vTaskSuspend == 1 ) + else if( pxStateList == &xSuspendedTaskList ) + { + /* The task being queried is referenced from the suspended + * list. Is it genuinely suspended or is it blocked + * indefinitely? */ + if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL ) + { + #if ( configUSE_TASK_NOTIFICATIONS == 1 ) + { + BaseType_t x; + + /* The task does not appear on the event list item of + * and of the RTOS objects, but could still be in the + * blocked state if it is waiting on its notification + * rather than waiting on an object. If not, is + * suspended. */ + eReturn = eSuspended; + + for( x = 0; x < configTASK_NOTIFICATION_ARRAY_ENTRIES; x++ ) + { + if( pxTCB->ucNotifyState[ x ] == taskWAITING_NOTIFICATION ) + { + eReturn = eBlocked; + break; + } + } + } + #else /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */ + { + eReturn = eSuspended; + } + #endif /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */ + } + else + { + eReturn = eBlocked; + } + } + #endif /* if ( INCLUDE_vTaskSuspend == 1 ) */ + + #if ( INCLUDE_vTaskDelete == 1 ) + else if( ( pxStateList == &xTasksWaitingTermination ) || ( pxStateList == NULL ) ) + { + /* The task being queried is referenced from the deleted + * tasks list, or it is not referenced from any lists at + * all. */ + eReturn = eDeleted; + } + #endif + + else /*lint !e525 Negative indentation is intended to make use of pre-processor clearer. */ + { + /* If the task is not in any other state, it must be in the + * Ready (including pending ready) state. */ + eReturn = eReady; + } + } + + return eReturn; + } /*lint !e818 xTask cannot be a pointer to const because it is a typedef. */ + +#endif /* INCLUDE_eTaskGetState */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_uxTaskPriorityGet == 1 ) + + UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask ) + { + TCB_t const * pxTCB; + UBaseType_t uxReturn; + + taskENTER_CRITICAL(); + { + /* If null is passed in here then it is the priority of the task + * that called uxTaskPriorityGet() that is being queried. */ + pxTCB = prvGetTCBFromHandle( xTask ); + uxReturn = pxTCB->uxPriority; + } + taskEXIT_CRITICAL(); + + return uxReturn; + } + +#endif /* INCLUDE_uxTaskPriorityGet */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_uxTaskPriorityGet == 1 ) + + UBaseType_t uxTaskPriorityGetFromISR( const TaskHandle_t xTask ) + { + TCB_t const * pxTCB; + UBaseType_t uxReturn; + UBaseType_t uxSavedInterruptState; + + /* RTOS ports that support interrupt nesting have the concept of a + * maximum system call (or maximum API call) interrupt priority. + * Interrupts that are above the maximum system call priority are keep + * permanently enabled, even when the RTOS kernel is in a critical section, + * but cannot make any calls to FreeRTOS API functions. If configASSERT() + * is defined in FreeRTOSConfig.h then + * portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + * failure if a FreeRTOS API function is called from an interrupt that has + * been assigned a priority above the configured maximum system call + * priority. Only FreeRTOS functions that end in FromISR can be called + * from interrupts that have been assigned a priority at or (logically) + * below the maximum system call interrupt priority. FreeRTOS maintains a + * separate interrupt safe API to ensure interrupt entry is as fast and as + * simple as possible. More information (albeit Cortex-M specific) is + * provided on the following link: + * https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + uxSavedInterruptState = portSET_INTERRUPT_MASK_FROM_ISR(); + { + /* If null is passed in here then it is the priority of the calling + * task that is being queried. */ + pxTCB = prvGetTCBFromHandle( xTask ); + uxReturn = pxTCB->uxPriority; + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptState ); + + return uxReturn; + } + +#endif /* INCLUDE_uxTaskPriorityGet */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskPrioritySet == 1 ) + + void vTaskPrioritySet( TaskHandle_t xTask, + UBaseType_t uxNewPriority ) + { + TCB_t * pxTCB; + UBaseType_t uxCurrentBasePriority, uxPriorityUsedOnEntry; + BaseType_t xYieldRequired = pdFALSE; + + configASSERT( uxNewPriority < configMAX_PRIORITIES ); + + /* Ensure the new priority is valid. */ + if( uxNewPriority >= ( UBaseType_t ) configMAX_PRIORITIES ) + { + uxNewPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + taskENTER_CRITICAL(); + { + /* If null is passed in here then it is the priority of the calling + * task that is being changed. */ + pxTCB = prvGetTCBFromHandle( xTask ); + + traceTASK_PRIORITY_SET( pxTCB, uxNewPriority ); + + #if ( configUSE_MUTEXES == 1 ) + { + uxCurrentBasePriority = pxTCB->uxBasePriority; + } + #else + { + uxCurrentBasePriority = pxTCB->uxPriority; + } + #endif + + if( uxCurrentBasePriority != uxNewPriority ) + { + /* The priority change may have readied a task of higher + * priority than the calling task. */ + if( uxNewPriority > uxCurrentBasePriority ) + { + if( pxTCB != pxCurrentTCB ) + { + /* The priority of a task other than the currently + * running task is being raised. Is the priority being + * raised above that of the running task? */ + if( uxNewPriority > pxCurrentTCB->uxPriority ) + { + xYieldRequired = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* The priority of the running task is being raised, + * but the running task must already be the highest + * priority task able to run so no yield is required. */ + } + } + else if( pxTCB == pxCurrentTCB ) + { + /* Setting the priority of the running task down means + * there may now be another task of higher priority that + * is ready to execute. */ + xYieldRequired = pdTRUE; + } + else + { + /* Setting the priority of any other task down does not + * require a yield as the running task must be above the + * new priority of the task being modified. */ + } + + /* Remember the ready list the task might be referenced from + * before its uxPriority member is changed so the + * taskRESET_READY_PRIORITY() macro can function correctly. */ + uxPriorityUsedOnEntry = pxTCB->uxPriority; + + #if ( configUSE_MUTEXES == 1 ) + { + /* Only change the priority being used if the task is not + * currently using an inherited priority. */ + if( pxTCB->uxBasePriority == pxTCB->uxPriority ) + { + pxTCB->uxPriority = uxNewPriority; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* The base priority gets set whatever. */ + pxTCB->uxBasePriority = uxNewPriority; + } + #else /* if ( configUSE_MUTEXES == 1 ) */ + { + pxTCB->uxPriority = uxNewPriority; + } + #endif /* if ( configUSE_MUTEXES == 1 ) */ + + /* Only reset the event list item value if the value is not + * being used for anything else. */ + if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL ) + { + listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxNewPriority ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* If the task is in the blocked or suspended list we need do + * nothing more than change its priority variable. However, if + * the task is in a ready list it needs to be removed and placed + * in the list appropriate to its new priority. */ + if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxPriorityUsedOnEntry ] ), &( pxTCB->xStateListItem ) ) != pdFALSE ) + { + /* The task is currently in its ready list - remove before + * adding it to its new ready list. As we are in a critical + * section we can do this even if the scheduler is suspended. */ + if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) + { + /* It is known that the task is in its ready list so + * there is no need to check again and the port level + * reset macro can be called directly. */ + portRESET_READY_PRIORITY( uxPriorityUsedOnEntry, uxTopReadyPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + prvAddTaskToReadyList( pxTCB ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + if( xYieldRequired != pdFALSE ) + { + taskYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Remove compiler warning about unused variables when the port + * optimised task selection is not being used. */ + ( void ) uxPriorityUsedOnEntry; + } + } + taskEXIT_CRITICAL(); + } + +#endif /* INCLUDE_vTaskPrioritySet */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskSuspend == 1 ) + + void vTaskSuspend( TaskHandle_t xTaskToSuspend ) + { + TCB_t * pxTCB; + + taskENTER_CRITICAL(); + { + /* If null is passed in here then it is the running task that is + * being suspended. */ + pxTCB = prvGetTCBFromHandle( xTaskToSuspend ); + + traceTASK_SUSPEND( pxTCB ); + + /* Remove task from the ready/delayed list and place in the + * suspended list. */ + if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) + { + taskRESET_READY_PRIORITY( pxTCB->uxPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Is the task waiting on an event also? */ + if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) + { + ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) ); + + #if ( configUSE_TASK_NOTIFICATIONS == 1 ) + { + BaseType_t x; + + for( x = 0; x < configTASK_NOTIFICATION_ARRAY_ENTRIES; x++ ) + { + if( pxTCB->ucNotifyState[ x ] == taskWAITING_NOTIFICATION ) + { + /* The task was blocked to wait for a notification, but is + * now suspended, so no notification was received. */ + pxTCB->ucNotifyState[ x ] = taskNOT_WAITING_NOTIFICATION; + } + } + } + #endif /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */ + } + taskEXIT_CRITICAL(); + + if( xSchedulerRunning != pdFALSE ) + { + /* Reset the next expected unblock time in case it referred to the + * task that is now in the Suspended state. */ + taskENTER_CRITICAL(); + { + prvResetNextTaskUnblockTime(); + } + taskEXIT_CRITICAL(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + if( pxTCB == pxCurrentTCB ) + { + if( xSchedulerRunning != pdFALSE ) + { + /* The current task has just been suspended. */ + configASSERT( uxSchedulerSuspended == ( UBaseType_t ) 0U ); + portYIELD_WITHIN_API(); + } + else + { + /* The scheduler is not running, but the task that was pointed + * to by pxCurrentTCB has just been suspended and pxCurrentTCB + * must be adjusted to point to a different task. */ + if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == uxCurrentNumberOfTasks ) /*lint !e931 Right has no side effect, just volatile. */ + { + /* No other tasks are ready, so set pxCurrentTCB back to + * NULL so when the next task is created pxCurrentTCB will + * be set to point to it no matter what its relative priority + * is. */ + pxCurrentTCB = NULL; + } + else + { + vTaskSwitchContext(); + } + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + +#endif /* INCLUDE_vTaskSuspend */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskSuspend == 1 ) + + static BaseType_t prvTaskIsTaskSuspended( const TaskHandle_t xTask ) + { + BaseType_t xReturn = pdFALSE; + const TCB_t * const pxTCB = xTask; + + /* Accesses xPendingReadyList so must be called from a critical + * section. */ + + /* It does not make sense to check if the calling task is suspended. */ + configASSERT( xTask ); + + /* Is the task being resumed actually in the suspended list? */ + if( listIS_CONTAINED_WITHIN( &xSuspendedTaskList, &( pxTCB->xStateListItem ) ) != pdFALSE ) + { + /* Has the task already been resumed from within an ISR? */ + if( listIS_CONTAINED_WITHIN( &xPendingReadyList, &( pxTCB->xEventListItem ) ) == pdFALSE ) + { + /* Is it in the suspended list because it is in the Suspended + * state, or because is is blocked with no timeout? */ + if( listIS_CONTAINED_WITHIN( NULL, &( pxTCB->xEventListItem ) ) != pdFALSE ) /*lint !e961. The cast is only redundant when NULL is used. */ + { + xReturn = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return xReturn; + } /*lint !e818 xTask cannot be a pointer to const because it is a typedef. */ + +#endif /* INCLUDE_vTaskSuspend */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskSuspend == 1 ) + + void vTaskResume( TaskHandle_t xTaskToResume ) + { + TCB_t * const pxTCB = xTaskToResume; + + /* It does not make sense to resume the calling task. */ + configASSERT( xTaskToResume ); + + /* The parameter cannot be NULL as it is impossible to resume the + * currently executing task. */ + if( ( pxTCB != pxCurrentTCB ) && ( pxTCB != NULL ) ) + { + taskENTER_CRITICAL(); + { + if( prvTaskIsTaskSuspended( pxTCB ) != pdFALSE ) + { + traceTASK_RESUME( pxTCB ); + + /* The ready list can be accessed even if the scheduler is + * suspended because this is inside a critical section. */ + ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); + prvAddTaskToReadyList( pxTCB ); + + /* A higher priority task may have just been resumed. */ + if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ) + { + /* This yield may not cause the task just resumed to run, + * but will leave the lists in the correct state for the + * next yield. */ + taskYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + +#endif /* INCLUDE_vTaskSuspend */ + +/*-----------------------------------------------------------*/ + +#if ( ( INCLUDE_xTaskResumeFromISR == 1 ) && ( INCLUDE_vTaskSuspend == 1 ) ) + + BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ) + { + BaseType_t xYieldRequired = pdFALSE; + TCB_t * const pxTCB = xTaskToResume; + UBaseType_t uxSavedInterruptStatus; + + configASSERT( xTaskToResume ); + + /* RTOS ports that support interrupt nesting have the concept of a + * maximum system call (or maximum API call) interrupt priority. + * Interrupts that are above the maximum system call priority are keep + * permanently enabled, even when the RTOS kernel is in a critical section, + * but cannot make any calls to FreeRTOS API functions. If configASSERT() + * is defined in FreeRTOSConfig.h then + * portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + * failure if a FreeRTOS API function is called from an interrupt that has + * been assigned a priority above the configured maximum system call + * priority. Only FreeRTOS functions that end in FromISR can be called + * from interrupts that have been assigned a priority at or (logically) + * below the maximum system call interrupt priority. FreeRTOS maintains a + * separate interrupt safe API to ensure interrupt entry is as fast and as + * simple as possible. More information (albeit Cortex-M specific) is + * provided on the following link: + * https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + { + if( prvTaskIsTaskSuspended( pxTCB ) != pdFALSE ) + { + traceTASK_RESUME_FROM_ISR( pxTCB ); + + /* Check the ready lists can be accessed. */ + if( uxSchedulerSuspended == ( UBaseType_t ) 0U ) + { + /* Ready lists can be accessed so move the task from the + * suspended list to the ready list directly. */ + if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ) + { + xYieldRequired = pdTRUE; + + /* Mark that a yield is pending in case the user is not + * using the return value to initiate a context switch + * from the ISR using portYIELD_FROM_ISR. */ + xYieldPending = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); + prvAddTaskToReadyList( pxTCB ); + } + else + { + /* The delayed or ready lists cannot be accessed so the task + * is held in the pending ready list until the scheduler is + * unsuspended. */ + vListInsertEnd( &( xPendingReadyList ), &( pxTCB->xEventListItem ) ); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + return xYieldRequired; + } + +#endif /* ( ( INCLUDE_xTaskResumeFromISR == 1 ) && ( INCLUDE_vTaskSuspend == 1 ) ) */ +/*-----------------------------------------------------------*/ + +void vTaskStartScheduler( void ) +{ + BaseType_t xReturn; + + /* Add the idle task at the lowest priority. */ + #if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + { + StaticTask_t * pxIdleTaskTCBBuffer = NULL; + StackType_t * pxIdleTaskStackBuffer = NULL; + uint32_t ulIdleTaskStackSize; + + /* The Idle task is created using user provided RAM - obtain the + * address of the RAM then create the idle task. */ + vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &ulIdleTaskStackSize ); + xIdleTaskHandle = xTaskCreateStatic( prvIdleTask, + configIDLE_TASK_NAME, + ulIdleTaskStackSize, + ( void * ) NULL, /*lint !e961. The cast is not redundant for all compilers. */ + portPRIVILEGE_BIT, /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */ + pxIdleTaskStackBuffer, + pxIdleTaskTCBBuffer ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */ + + if( xIdleTaskHandle != NULL ) + { + xReturn = pdPASS; + } + else + { + xReturn = pdFAIL; + } + } + #else /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */ + { + /* The Idle task is being created using dynamically allocated RAM. */ + xReturn = xTaskCreate( prvIdleTask, + configIDLE_TASK_NAME, + configMINIMAL_STACK_SIZE, + ( void * ) NULL, + portPRIVILEGE_BIT, /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */ + &xIdleTaskHandle ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */ + } + #endif /* configSUPPORT_STATIC_ALLOCATION */ + + #if ( configUSE_TIMERS == 1 ) + { + if( xReturn == pdPASS ) + { + xReturn = xTimerCreateTimerTask(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_TIMERS */ + + if( xReturn == pdPASS ) + { + /* freertos_tasks_c_additions_init() should only be called if the user + * definable macro FREERTOS_TASKS_C_ADDITIONS_INIT() is defined, as that is + * the only macro called by the function. */ + #ifdef FREERTOS_TASKS_C_ADDITIONS_INIT + { + freertos_tasks_c_additions_init(); + } + #endif + + /* Interrupts are turned off here, to ensure a tick does not occur + * before or during the call to xPortStartScheduler(). The stacks of + * the created tasks contain a status word with interrupts switched on + * so interrupts will automatically get re-enabled when the first task + * starts to run. */ + portDISABLE_INTERRUPTS(); + + #if ( configUSE_C_RUNTIME_TLS_SUPPORT == 1 ) + { + /* Switch C-Runtime's TLS Block to point to the TLS + * block specific to the task that will run first. */ + configSET_TLS_BLOCK( pxCurrentTCB->xTLSBlock ); + } + #endif + + xNextTaskUnblockTime = portMAX_DELAY; + xSchedulerRunning = pdTRUE; + xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT; + + /* If configGENERATE_RUN_TIME_STATS is defined then the following + * macro must be defined to configure the timer/counter used to generate + * the run time counter time base. NOTE: If configGENERATE_RUN_TIME_STATS + * is set to 0 and the following line fails to build then ensure you do not + * have portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() defined in your + * FreeRTOSConfig.h file. */ + portCONFIGURE_TIMER_FOR_RUN_TIME_STATS(); + + traceTASK_SWITCHED_IN(); + + /* Setting up the timer tick is hardware specific and thus in the + * portable interface. */ + xPortStartScheduler(); + + /* In most cases, xPortStartScheduler() will not return. If it + * returns pdTRUE then there was not enough heap memory available + * to create either the Idle or the Timer task. If it returned + * pdFALSE, then the application called xTaskEndScheduler(). + * Most ports don't implement xTaskEndScheduler() as there is + * nothing to return to. */ + } + else + { + /* This line will only be reached if the kernel could not be started, + * because there was not enough FreeRTOS heap to create the idle task + * or the timer task. */ + configASSERT( xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY ); + } + + /* Prevent compiler warnings if INCLUDE_xTaskGetIdleTaskHandle is set to 0, + * meaning xIdleTaskHandle is not used anywhere else. */ + ( void ) xIdleTaskHandle; + + /* OpenOCD makes use of uxTopUsedPriority for thread debugging. Prevent uxTopUsedPriority + * from getting optimized out as it is no longer used by the kernel. */ + ( void ) uxTopUsedPriority; +} +/*-----------------------------------------------------------*/ + +void vTaskEndScheduler( void ) +{ + /* Stop the scheduler interrupts and call the portable scheduler end + * routine so the original ISRs can be restored if necessary. The port + * layer must ensure interrupts enable bit is left in the correct state. */ + portDISABLE_INTERRUPTS(); + xSchedulerRunning = pdFALSE; + vPortEndScheduler(); +} +/*----------------------------------------------------------*/ + +void vTaskSuspendAll( void ) +{ + /* A critical section is not required as the variable is of type + * BaseType_t. Please read Richard Barry's reply in the following link to a + * post in the FreeRTOS support forum before reporting this as a bug! - + * https://goo.gl/wu4acr */ + + /* portSOFTWARE_BARRIER() is only implemented for emulated/simulated ports that + * do not otherwise exhibit real time behaviour. */ + portSOFTWARE_BARRIER(); + + /* The scheduler is suspended if uxSchedulerSuspended is non-zero. An increment + * is used to allow calls to vTaskSuspendAll() to nest. */ + ++uxSchedulerSuspended; + + /* Enforces ordering for ports and optimised compilers that may otherwise place + * the above increment elsewhere. */ + portMEMORY_BARRIER(); +} +/*----------------------------------------------------------*/ + +#if ( configUSE_TICKLESS_IDLE != 0 ) + + static TickType_t prvGetExpectedIdleTime( void ) + { + TickType_t xReturn; + UBaseType_t uxHigherPriorityReadyTasks = pdFALSE; + + /* uxHigherPriorityReadyTasks takes care of the case where + * configUSE_PREEMPTION is 0, so there may be tasks above the idle priority + * task that are in the Ready state, even though the idle task is + * running. */ + #if ( configUSE_PORT_OPTIMISED_TASK_SELECTION == 0 ) + { + if( uxTopReadyPriority > tskIDLE_PRIORITY ) + { + uxHigherPriorityReadyTasks = pdTRUE; + } + } + #else + { + const UBaseType_t uxLeastSignificantBit = ( UBaseType_t ) 0x01; + + /* When port optimised task selection is used the uxTopReadyPriority + * variable is used as a bit map. If bits other than the least + * significant bit are set then there are tasks that have a priority + * above the idle priority that are in the Ready state. This takes + * care of the case where the co-operative scheduler is in use. */ + if( uxTopReadyPriority > uxLeastSignificantBit ) + { + uxHigherPriorityReadyTasks = pdTRUE; + } + } + #endif /* if ( configUSE_PORT_OPTIMISED_TASK_SELECTION == 0 ) */ + + if( pxCurrentTCB->uxPriority > tskIDLE_PRIORITY ) + { + xReturn = 0; + } + else if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > 1 ) + { + /* There are other idle priority tasks in the ready state. If + * time slicing is used then the very next tick interrupt must be + * processed. */ + xReturn = 0; + } + else if( uxHigherPriorityReadyTasks != pdFALSE ) + { + /* There are tasks in the Ready state that have a priority above the + * idle priority. This path can only be reached if + * configUSE_PREEMPTION is 0. */ + xReturn = 0; + } + else + { + xReturn = xNextTaskUnblockTime - xTickCount; + } + + return xReturn; + } + +#endif /* configUSE_TICKLESS_IDLE */ +/*----------------------------------------------------------*/ + +BaseType_t xTaskResumeAll( void ) +{ + TCB_t * pxTCB = NULL; + BaseType_t xAlreadyYielded = pdFALSE; + + /* If uxSchedulerSuspended is zero then this function does not match a + * previous call to vTaskSuspendAll(). */ + configASSERT( uxSchedulerSuspended != ( UBaseType_t ) 0U ); + + /* It is possible that an ISR caused a task to be removed from an event + * list while the scheduler was suspended. If this was the case then the + * removed task will have been added to the xPendingReadyList. Once the + * scheduler has been resumed it is safe to move all the pending ready + * tasks from this list into their appropriate ready list. */ + taskENTER_CRITICAL(); + { + --uxSchedulerSuspended; + + if( uxSchedulerSuspended == ( UBaseType_t ) 0U ) + { + if( uxCurrentNumberOfTasks > ( UBaseType_t ) 0U ) + { + /* Move any readied tasks from the pending list into the + * appropriate ready list. */ + while( listLIST_IS_EMPTY( &xPendingReadyList ) == pdFALSE ) + { + pxTCB = listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyList ) ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + listREMOVE_ITEM( &( pxTCB->xEventListItem ) ); + portMEMORY_BARRIER(); + listREMOVE_ITEM( &( pxTCB->xStateListItem ) ); + prvAddTaskToReadyList( pxTCB ); + + /* If the moved task has a priority higher than the current + * task then a yield must be performed. */ + if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ) + { + xYieldPending = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + if( pxTCB != NULL ) + { + /* A task was unblocked while the scheduler was suspended, + * which may have prevented the next unblock time from being + * re-calculated, in which case re-calculate it now. Mainly + * important for low power tickless implementations, where + * this can prevent an unnecessary exit from low power + * state. */ + prvResetNextTaskUnblockTime(); + } + + /* If any ticks occurred while the scheduler was suspended then + * they should be processed now. This ensures the tick count does + * not slip, and that any delayed tasks are resumed at the correct + * time. */ + { + TickType_t xPendedCounts = xPendedTicks; /* Non-volatile copy. */ + + if( xPendedCounts > ( TickType_t ) 0U ) + { + do + { + if( xTaskIncrementTick() != pdFALSE ) + { + xYieldPending = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + --xPendedCounts; + } while( xPendedCounts > ( TickType_t ) 0U ); + + xPendedTicks = 0; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + if( xYieldPending != pdFALSE ) + { + #if ( configUSE_PREEMPTION != 0 ) + { + xAlreadyYielded = pdTRUE; + } + #endif + taskYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); + + return xAlreadyYielded; +} +/*-----------------------------------------------------------*/ + +TickType_t xTaskGetTickCount( void ) +{ + TickType_t xTicks; + + /* Critical section required if running on a 16 bit processor. */ + portTICK_TYPE_ENTER_CRITICAL(); + { + xTicks = xTickCount; + } + portTICK_TYPE_EXIT_CRITICAL(); + + return xTicks; +} +/*-----------------------------------------------------------*/ + +TickType_t xTaskGetTickCountFromISR( void ) +{ + TickType_t xReturn; + UBaseType_t uxSavedInterruptStatus; + + /* RTOS ports that support interrupt nesting have the concept of a maximum + * system call (or maximum API call) interrupt priority. Interrupts that are + * above the maximum system call priority are kept permanently enabled, even + * when the RTOS kernel is in a critical section, but cannot make any calls to + * FreeRTOS API functions. If configASSERT() is defined in FreeRTOSConfig.h + * then portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + * failure if a FreeRTOS API function is called from an interrupt that has been + * assigned a priority above the configured maximum system call priority. + * Only FreeRTOS functions that end in FromISR can be called from interrupts + * that have been assigned a priority at or (logically) below the maximum + * system call interrupt priority. FreeRTOS maintains a separate interrupt + * safe API to ensure interrupt entry is as fast and as simple as possible. + * More information (albeit Cortex-M specific) is provided on the following + * link: https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + uxSavedInterruptStatus = portTICK_TYPE_SET_INTERRUPT_MASK_FROM_ISR(); + { + xReturn = xTickCount; + } + portTICK_TYPE_CLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +UBaseType_t uxTaskGetNumberOfTasks( void ) +{ + /* A critical section is not required because the variables are of type + * BaseType_t. */ + return uxCurrentNumberOfTasks; +} +/*-----------------------------------------------------------*/ + +char * pcTaskGetName( TaskHandle_t xTaskToQuery ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ +{ + TCB_t * pxTCB; + + /* If null is passed in here then the name of the calling task is being + * queried. */ + pxTCB = prvGetTCBFromHandle( xTaskToQuery ); + configASSERT( pxTCB ); + return &( pxTCB->pcTaskName[ 0 ] ); +} +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_xTaskGetHandle == 1 ) + + static TCB_t * prvSearchForNameWithinSingleList( List_t * pxList, + const char pcNameToQuery[] ) + { + TCB_t * pxNextTCB; + TCB_t * pxFirstTCB; + TCB_t * pxReturn = NULL; + UBaseType_t x; + char cNextChar; + BaseType_t xBreakLoop; + + /* This function is called with the scheduler suspended. */ + + if( listCURRENT_LIST_LENGTH( pxList ) > ( UBaseType_t ) 0 ) + { + listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + + do + { + listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + + /* Check each character in the name looking for a match or + * mismatch. */ + xBreakLoop = pdFALSE; + + for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ ) + { + cNextChar = pxNextTCB->pcTaskName[ x ]; + + if( cNextChar != pcNameToQuery[ x ] ) + { + /* Characters didn't match. */ + xBreakLoop = pdTRUE; + } + else if( cNextChar == ( char ) 0x00 ) + { + /* Both strings terminated, a match must have been + * found. */ + pxReturn = pxNextTCB; + xBreakLoop = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + if( xBreakLoop != pdFALSE ) + { + break; + } + } + + if( pxReturn != NULL ) + { + /* The handle has been found. */ + break; + } + } while( pxNextTCB != pxFirstTCB ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return pxReturn; + } + +#endif /* INCLUDE_xTaskGetHandle */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_xTaskGetHandle == 1 ) + + TaskHandle_t xTaskGetHandle( const char * pcNameToQuery ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + { + UBaseType_t uxQueue = configMAX_PRIORITIES; + TCB_t * pxTCB; + + /* Task names will be truncated to configMAX_TASK_NAME_LEN - 1 bytes. */ + configASSERT( strlen( pcNameToQuery ) < configMAX_TASK_NAME_LEN ); + + vTaskSuspendAll(); + { + /* Search the ready lists. */ + do + { + uxQueue--; + pxTCB = prvSearchForNameWithinSingleList( ( List_t * ) &( pxReadyTasksLists[ uxQueue ] ), pcNameToQuery ); + + if( pxTCB != NULL ) + { + /* Found the handle. */ + break; + } + } while( uxQueue > ( UBaseType_t ) tskIDLE_PRIORITY ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + + /* Search the delayed lists. */ + if( pxTCB == NULL ) + { + pxTCB = prvSearchForNameWithinSingleList( ( List_t * ) pxDelayedTaskList, pcNameToQuery ); + } + + if( pxTCB == NULL ) + { + pxTCB = prvSearchForNameWithinSingleList( ( List_t * ) pxOverflowDelayedTaskList, pcNameToQuery ); + } + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + if( pxTCB == NULL ) + { + /* Search the suspended list. */ + pxTCB = prvSearchForNameWithinSingleList( &xSuspendedTaskList, pcNameToQuery ); + } + } + #endif + + #if ( INCLUDE_vTaskDelete == 1 ) + { + if( pxTCB == NULL ) + { + /* Search the deleted list. */ + pxTCB = prvSearchForNameWithinSingleList( &xTasksWaitingTermination, pcNameToQuery ); + } + } + #endif + } + ( void ) xTaskResumeAll(); + + return pxTCB; + } + +#endif /* INCLUDE_xTaskGetHandle */ +/*-----------------------------------------------------------*/ + +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + + BaseType_t xTaskGetStaticBuffers( TaskHandle_t xTask, + StackType_t ** ppuxStackBuffer, + StaticTask_t ** ppxTaskBuffer ) + { + BaseType_t xReturn; + TCB_t * pxTCB; + + configASSERT( ppuxStackBuffer != NULL ); + configASSERT( ppxTaskBuffer != NULL ); + + pxTCB = prvGetTCBFromHandle( xTask ); + + #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE == 1 ) + { + if( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_AND_TCB ) + { + *ppuxStackBuffer = pxTCB->pxStack; + *ppxTaskBuffer = ( StaticTask_t * ) pxTCB; + xReturn = pdTRUE; + } + else if( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_ONLY ) + { + *ppuxStackBuffer = pxTCB->pxStack; + *ppxTaskBuffer = NULL; + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + } + #else /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE == 1 */ + { + *ppuxStackBuffer = pxTCB->pxStack; + *ppxTaskBuffer = ( StaticTask_t * ) pxTCB; + xReturn = pdTRUE; + } + #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE == 1 */ + + return xReturn; + } + +#endif /* configSUPPORT_STATIC_ALLOCATION */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, + const UBaseType_t uxArraySize, + configRUN_TIME_COUNTER_TYPE * const pulTotalRunTime ) + { + UBaseType_t uxTask = 0, uxQueue = configMAX_PRIORITIES; + + vTaskSuspendAll(); + { + /* Is there a space in the array for each task in the system? */ + if( uxArraySize >= uxCurrentNumberOfTasks ) + { + /* Fill in an TaskStatus_t structure with information on each + * task in the Ready state. */ + do + { + uxQueue--; + uxTask += prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), &( pxReadyTasksLists[ uxQueue ] ), eReady ); + } while( uxQueue > ( UBaseType_t ) tskIDLE_PRIORITY ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + + /* Fill in an TaskStatus_t structure with information on each + * task in the Blocked state. */ + uxTask += prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), ( List_t * ) pxDelayedTaskList, eBlocked ); + uxTask += prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), ( List_t * ) pxOverflowDelayedTaskList, eBlocked ); + + #if ( INCLUDE_vTaskDelete == 1 ) + { + /* Fill in an TaskStatus_t structure with information on + * each task that has been deleted but not yet cleaned up. */ + uxTask += prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), &xTasksWaitingTermination, eDeleted ); + } + #endif + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + /* Fill in an TaskStatus_t structure with information on + * each task in the Suspended state. */ + uxTask += prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), &xSuspendedTaskList, eSuspended ); + } + #endif + + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + { + if( pulTotalRunTime != NULL ) + { + #ifdef portALT_GET_RUN_TIME_COUNTER_VALUE + portALT_GET_RUN_TIME_COUNTER_VALUE( ( *pulTotalRunTime ) ); + #else + *pulTotalRunTime = ( configRUN_TIME_COUNTER_TYPE ) portGET_RUN_TIME_COUNTER_VALUE(); + #endif + } + } + #else /* if ( configGENERATE_RUN_TIME_STATS == 1 ) */ + { + if( pulTotalRunTime != NULL ) + { + *pulTotalRunTime = 0; + } + } + #endif /* if ( configGENERATE_RUN_TIME_STATS == 1 ) */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + ( void ) xTaskResumeAll(); + + return uxTask; + } + +#endif /* configUSE_TRACE_FACILITY */ +/*----------------------------------------------------------*/ + +#if ( INCLUDE_xTaskGetIdleTaskHandle == 1 ) + + TaskHandle_t xTaskGetIdleTaskHandle( void ) + { + /* If xTaskGetIdleTaskHandle() is called before the scheduler has been + * started, then xIdleTaskHandle will be NULL. */ + configASSERT( ( xIdleTaskHandle != NULL ) ); + return xIdleTaskHandle; + } + +#endif /* INCLUDE_xTaskGetIdleTaskHandle */ +/*----------------------------------------------------------*/ + +/* This conditional compilation should use inequality to 0, not equality to 1. + * This is to ensure vTaskStepTick() is available when user defined low power mode + * implementations require configUSE_TICKLESS_IDLE to be set to a value other than + * 1. */ +#if ( configUSE_TICKLESS_IDLE != 0 ) + + void vTaskStepTick( TickType_t xTicksToJump ) + { + /* Correct the tick count value after a period during which the tick + * was suppressed. Note this does *not* call the tick hook function for + * each stepped tick. */ + configASSERT( ( xTickCount + xTicksToJump ) <= xNextTaskUnblockTime ); + + if( ( xTickCount + xTicksToJump ) == xNextTaskUnblockTime ) + { + /* Arrange for xTickCount to reach xNextTaskUnblockTime in + * xTaskIncrementTick() when the scheduler resumes. This ensures + * that any delayed tasks are resumed at the correct time. */ + configASSERT( uxSchedulerSuspended != ( UBaseType_t ) 0U ); + configASSERT( xTicksToJump != ( TickType_t ) 0 ); + + /* Prevent the tick interrupt modifying xPendedTicks simultaneously. */ + taskENTER_CRITICAL(); + { + xPendedTicks++; + } + taskEXIT_CRITICAL(); + xTicksToJump--; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + xTickCount += xTicksToJump; + traceINCREASE_TICK_COUNT( xTicksToJump ); + } + +#endif /* configUSE_TICKLESS_IDLE */ +/*----------------------------------------------------------*/ + +BaseType_t xTaskCatchUpTicks( TickType_t xTicksToCatchUp ) +{ + BaseType_t xYieldOccurred; + + /* Must not be called with the scheduler suspended as the implementation + * relies on xPendedTicks being wound down to 0 in xTaskResumeAll(). */ + configASSERT( uxSchedulerSuspended == ( UBaseType_t ) 0U ); + + /* Use xPendedTicks to mimic xTicksToCatchUp number of ticks occurring when + * the scheduler is suspended so the ticks are executed in xTaskResumeAll(). */ + vTaskSuspendAll(); + + /* Prevent the tick interrupt modifying xPendedTicks simultaneously. */ + taskENTER_CRITICAL(); + { + xPendedTicks += xTicksToCatchUp; + } + taskEXIT_CRITICAL(); + xYieldOccurred = xTaskResumeAll(); + + return xYieldOccurred; +} +/*----------------------------------------------------------*/ + +#if ( INCLUDE_xTaskAbortDelay == 1 ) + + BaseType_t xTaskAbortDelay( TaskHandle_t xTask ) + { + TCB_t * pxTCB = xTask; + BaseType_t xReturn; + + configASSERT( pxTCB ); + + vTaskSuspendAll(); + { + /* A task can only be prematurely removed from the Blocked state if + * it is actually in the Blocked state. */ + if( eTaskGetState( xTask ) == eBlocked ) + { + xReturn = pdPASS; + + /* Remove the reference to the task from the blocked list. An + * interrupt won't touch the xStateListItem because the + * scheduler is suspended. */ + ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); + + /* Is the task waiting on an event also? If so remove it from + * the event list too. Interrupts can touch the event list item, + * even though the scheduler is suspended, so a critical section + * is used. */ + taskENTER_CRITICAL(); + { + if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) + { + ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); + + /* This lets the task know it was forcibly removed from the + * blocked state so it should not re-evaluate its block time and + * then block again. */ + pxTCB->ucDelayAborted = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); + + /* Place the unblocked task into the appropriate ready list. */ + prvAddTaskToReadyList( pxTCB ); + + /* A task being unblocked cannot cause an immediate context + * switch if preemption is turned off. */ + #if ( configUSE_PREEMPTION == 1 ) + { + /* Preemption is on, but a context switch should only be + * performed if the unblocked task has a priority that is + * higher than the currently executing task. */ + if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ) + { + /* Pend the yield to be performed when the scheduler + * is unsuspended. */ + xYieldPending = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_PREEMPTION */ + } + else + { + xReturn = pdFAIL; + } + } + ( void ) xTaskResumeAll(); + + return xReturn; + } + +#endif /* INCLUDE_xTaskAbortDelay */ +/*----------------------------------------------------------*/ + +BaseType_t xTaskIncrementTick( void ) +{ + TCB_t * pxTCB; + TickType_t xItemValue; + BaseType_t xSwitchRequired = pdFALSE; + + /* Called by the portable layer each time a tick interrupt occurs. + * Increments the tick then checks to see if the new tick value will cause any + * tasks to be unblocked. */ + traceTASK_INCREMENT_TICK( xTickCount ); + + if( uxSchedulerSuspended == ( UBaseType_t ) 0U ) + { + /* Minor optimisation. The tick count cannot change in this + * block. */ + const TickType_t xConstTickCount = xTickCount + ( TickType_t ) 1; + + /* Increment the RTOS tick, switching the delayed and overflowed + * delayed lists if it wraps to 0. */ + xTickCount = xConstTickCount; + + if( xConstTickCount == ( TickType_t ) 0U ) /*lint !e774 'if' does not always evaluate to false as it is looking for an overflow. */ + { + taskSWITCH_DELAYED_LISTS(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* See if this tick has made a timeout expire. Tasks are stored in + * the queue in the order of their wake time - meaning once one task + * has been found whose block time has not expired there is no need to + * look any further down the list. */ + if( xConstTickCount >= xNextTaskUnblockTime ) + { + for( ; ; ) + { + if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE ) + { + /* The delayed list is empty. Set xNextTaskUnblockTime + * to the maximum possible value so it is extremely + * unlikely that the + * if( xTickCount >= xNextTaskUnblockTime ) test will pass + * next time through. */ + xNextTaskUnblockTime = portMAX_DELAY; /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + break; + } + else + { + /* The delayed list is not empty, get the value of the + * item at the head of the delayed list. This is the time + * at which the task at the head of the delayed list must + * be removed from the Blocked state. */ + pxTCB = listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) ); + + if( xConstTickCount < xItemValue ) + { + /* It is not time to unblock this item yet, but the + * item value is the time at which the task at the head + * of the blocked list must be removed from the Blocked + * state - so record the item value in + * xNextTaskUnblockTime. */ + xNextTaskUnblockTime = xItemValue; + break; /*lint !e9011 Code structure here is deemed easier to understand with multiple breaks. */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* It is time to remove the item from the Blocked state. */ + listREMOVE_ITEM( &( pxTCB->xStateListItem ) ); + + /* Is the task waiting on an event also? If so remove + * it from the event list. */ + if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) + { + listREMOVE_ITEM( &( pxTCB->xEventListItem ) ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Place the unblocked task into the appropriate ready + * list. */ + prvAddTaskToReadyList( pxTCB ); + + /* A task being unblocked cannot cause an immediate + * context switch if preemption is turned off. */ + #if ( configUSE_PREEMPTION == 1 ) + { + /* Preemption is on, but a context switch should + * only be performed if the unblocked task's + * priority is higher than the currently executing + * task. + * The case of equal priority tasks sharing + * processing time (which happens when both + * preemption and time slicing are on) is + * handled below.*/ + if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ) + { + xSwitchRequired = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_PREEMPTION */ + } + } + } + + /* Tasks of equal priority to the currently running task will share + * processing time (time slice) if preemption is on, and the application + * writer has not explicitly turned time slicing off. */ + #if ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) + { + if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ pxCurrentTCB->uxPriority ] ) ) > ( UBaseType_t ) 1 ) + { + xSwitchRequired = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) */ + + #if ( configUSE_TICK_HOOK == 1 ) + { + /* Guard against the tick hook being called when the pended tick + * count is being unwound (when the scheduler is being unlocked). */ + if( xPendedTicks == ( TickType_t ) 0 ) + { + vApplicationTickHook(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_TICK_HOOK */ + + #if ( configUSE_PREEMPTION == 1 ) + { + if( xYieldPending != pdFALSE ) + { + xSwitchRequired = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_PREEMPTION */ + } + else + { + ++xPendedTicks; + + /* The tick hook gets called at regular intervals, even if the + * scheduler is locked. */ + #if ( configUSE_TICK_HOOK == 1 ) + { + vApplicationTickHook(); + } + #endif + } + + return xSwitchRequired; +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_APPLICATION_TASK_TAG == 1 ) + + void vTaskSetApplicationTaskTag( TaskHandle_t xTask, + TaskHookFunction_t pxHookFunction ) + { + TCB_t * xTCB; + + /* If xTask is NULL then it is the task hook of the calling task that is + * getting set. */ + if( xTask == NULL ) + { + xTCB = ( TCB_t * ) pxCurrentTCB; + } + else + { + xTCB = xTask; + } + + /* Save the hook function in the TCB. A critical section is required as + * the value can be accessed from an interrupt. */ + taskENTER_CRITICAL(); + { + xTCB->pxTaskTag = pxHookFunction; + } + taskEXIT_CRITICAL(); + } + +#endif /* configUSE_APPLICATION_TASK_TAG */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_APPLICATION_TASK_TAG == 1 ) + + TaskHookFunction_t xTaskGetApplicationTaskTag( TaskHandle_t xTask ) + { + TCB_t * pxTCB; + TaskHookFunction_t xReturn; + + /* If xTask is NULL then set the calling task's hook. */ + pxTCB = prvGetTCBFromHandle( xTask ); + + /* Save the hook function in the TCB. A critical section is required as + * the value can be accessed from an interrupt. */ + taskENTER_CRITICAL(); + { + xReturn = pxTCB->pxTaskTag; + } + taskEXIT_CRITICAL(); + + return xReturn; + } + +#endif /* configUSE_APPLICATION_TASK_TAG */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_APPLICATION_TASK_TAG == 1 ) + + TaskHookFunction_t xTaskGetApplicationTaskTagFromISR( TaskHandle_t xTask ) + { + TCB_t * pxTCB; + TaskHookFunction_t xReturn; + UBaseType_t uxSavedInterruptStatus; + + /* If xTask is NULL then set the calling task's hook. */ + pxTCB = prvGetTCBFromHandle( xTask ); + + /* Save the hook function in the TCB. A critical section is required as + * the value can be accessed from an interrupt. */ + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + { + xReturn = pxTCB->pxTaskTag; + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + return xReturn; + } + +#endif /* configUSE_APPLICATION_TASK_TAG */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_APPLICATION_TASK_TAG == 1 ) + + BaseType_t xTaskCallApplicationTaskHook( TaskHandle_t xTask, + void * pvParameter ) + { + TCB_t * xTCB; + BaseType_t xReturn; + + /* If xTask is NULL then we are calling our own task hook. */ + if( xTask == NULL ) + { + xTCB = pxCurrentTCB; + } + else + { + xTCB = xTask; + } + + if( xTCB->pxTaskTag != NULL ) + { + xReturn = xTCB->pxTaskTag( pvParameter ); + } + else + { + xReturn = pdFAIL; + } + + return xReturn; + } + +#endif /* configUSE_APPLICATION_TASK_TAG */ +/*-----------------------------------------------------------*/ + +void vTaskSwitchContext( void ) +{ + if( uxSchedulerSuspended != ( UBaseType_t ) 0U ) + { + /* The scheduler is currently suspended - do not allow a context + * switch. */ + xYieldPending = pdTRUE; + } + else + { + xYieldPending = pdFALSE; + traceTASK_SWITCHED_OUT(); + + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + { + #ifdef portALT_GET_RUN_TIME_COUNTER_VALUE + portALT_GET_RUN_TIME_COUNTER_VALUE( ulTotalRunTime ); + #else + ulTotalRunTime = ( configRUN_TIME_COUNTER_TYPE ) portGET_RUN_TIME_COUNTER_VALUE(); + #endif + + /* Add the amount of time the task has been running to the + * accumulated time so far. The time the task started running was + * stored in ulTaskSwitchedInTime. Note that there is no overflow + * protection here so count values are only valid until the timer + * overflows. The guard against negative values is to protect + * against suspect run time stat counter implementations - which + * are provided by the application, not the kernel. */ + if( ulTotalRunTime > ulTaskSwitchedInTime ) + { + pxCurrentTCB->ulRunTimeCounter += ( ulTotalRunTime - ulTaskSwitchedInTime ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + ulTaskSwitchedInTime = ulTotalRunTime; + } + #endif /* configGENERATE_RUN_TIME_STATS */ + + /* Check for stack overflow, if configured. */ + taskCHECK_FOR_STACK_OVERFLOW(); + + /* Before the currently running task is switched out, save its errno. */ + #if ( configUSE_POSIX_ERRNO == 1 ) + { + pxCurrentTCB->iTaskErrno = FreeRTOS_errno; + } + #endif + + /* Select a new task to run using either the generic C or port + * optimised asm code. */ + taskSELECT_HIGHEST_PRIORITY_TASK(); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + traceTASK_SWITCHED_IN(); + + /* After the new task is switched in, update the global errno. */ + #if ( configUSE_POSIX_ERRNO == 1 ) + { + FreeRTOS_errno = pxCurrentTCB->iTaskErrno; + } + #endif + + #if ( configUSE_C_RUNTIME_TLS_SUPPORT == 1 ) + { + /* Switch C-Runtime's TLS Block to point to the TLS + * Block specific to this task. */ + configSET_TLS_BLOCK( pxCurrentTCB->xTLSBlock ); + } + #endif + } +} +/*-----------------------------------------------------------*/ + +void vTaskPlaceOnEventList( List_t * const pxEventList, + const TickType_t xTicksToWait ) +{ + configASSERT( pxEventList ); + + /* THIS FUNCTION MUST BE CALLED WITH EITHER INTERRUPTS DISABLED OR THE + * SCHEDULER SUSPENDED AND THE QUEUE BEING ACCESSED LOCKED. */ + + /* Place the event list item of the TCB in the appropriate event list. + * This is placed in the list in priority order so the highest priority task + * is the first to be woken by the event. + * + * Note: Lists are sorted in ascending order by ListItem_t.xItemValue. + * Normally, the xItemValue of a TCB's ListItem_t members is: + * xItemValue = ( configMAX_PRIORITIES - uxPriority ) + * Therefore, the event list is sorted in descending priority order. + * + * The queue that contains the event list is locked, preventing + * simultaneous access from interrupts. */ + vListInsert( pxEventList, &( pxCurrentTCB->xEventListItem ) ); + + prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE ); +} +/*-----------------------------------------------------------*/ + +void vTaskPlaceOnUnorderedEventList( List_t * pxEventList, + const TickType_t xItemValue, + const TickType_t xTicksToWait ) +{ + configASSERT( pxEventList ); + + /* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. It is used by + * the event groups implementation. */ + configASSERT( uxSchedulerSuspended != ( UBaseType_t ) 0U ); + + /* Store the item value in the event list item. It is safe to access the + * event list item here as interrupts won't access the event list item of a + * task that is not in the Blocked state. */ + listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xEventListItem ), xItemValue | taskEVENT_LIST_ITEM_VALUE_IN_USE ); + + /* Place the event list item of the TCB at the end of the appropriate event + * list. It is safe to access the event list here because it is part of an + * event group implementation - and interrupts don't access event groups + * directly (instead they access them indirectly by pending function calls to + * the task level). */ + listINSERT_END( pxEventList, &( pxCurrentTCB->xEventListItem ) ); + + prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE ); +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_TIMERS == 1 ) + + void vTaskPlaceOnEventListRestricted( List_t * const pxEventList, + TickType_t xTicksToWait, + const BaseType_t xWaitIndefinitely ) + { + configASSERT( pxEventList ); + + /* This function should not be called by application code hence the + * 'Restricted' in its name. It is not part of the public API. It is + * designed for use by kernel code, and has special calling requirements - + * it should be called with the scheduler suspended. */ + + + /* Place the event list item of the TCB in the appropriate event list. + * In this case it is assume that this is the only task that is going to + * be waiting on this event list, so the faster vListInsertEnd() function + * can be used in place of vListInsert. */ + listINSERT_END( pxEventList, &( pxCurrentTCB->xEventListItem ) ); + + /* If the task should block indefinitely then set the block time to a + * value that will be recognised as an indefinite delay inside the + * prvAddCurrentTaskToDelayedList() function. */ + if( xWaitIndefinitely != pdFALSE ) + { + xTicksToWait = portMAX_DELAY; + } + + traceTASK_DELAY_UNTIL( ( xTickCount + xTicksToWait ) ); + prvAddCurrentTaskToDelayedList( xTicksToWait, xWaitIndefinitely ); + } + +#endif /* configUSE_TIMERS */ +/*-----------------------------------------------------------*/ + +BaseType_t xTaskRemoveFromEventList( const List_t * const pxEventList ) +{ + TCB_t * pxUnblockedTCB; + BaseType_t xReturn; + + /* THIS FUNCTION MUST BE CALLED FROM A CRITICAL SECTION. It can also be + * called from a critical section within an ISR. */ + + /* The event list is sorted in priority order, so the first in the list can + * be removed as it is known to be the highest priority. Remove the TCB from + * the delayed list, and add it to the ready list. + * + * If an event is for a queue that is locked then this function will never + * get called - the lock count on the queue will get modified instead. This + * means exclusive access to the event list is guaranteed here. + * + * This function assumes that a check has already been made to ensure that + * pxEventList is not empty. */ + pxUnblockedTCB = listGET_OWNER_OF_HEAD_ENTRY( pxEventList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + configASSERT( pxUnblockedTCB ); + listREMOVE_ITEM( &( pxUnblockedTCB->xEventListItem ) ); + + if( uxSchedulerSuspended == ( UBaseType_t ) 0U ) + { + listREMOVE_ITEM( &( pxUnblockedTCB->xStateListItem ) ); + prvAddTaskToReadyList( pxUnblockedTCB ); + + #if ( configUSE_TICKLESS_IDLE != 0 ) + { + /* If a task is blocked on a kernel object then xNextTaskUnblockTime + * might be set to the blocked task's time out time. If the task is + * unblocked for a reason other than a timeout xNextTaskUnblockTime is + * normally left unchanged, because it is automatically reset to a new + * value when the tick count equals xNextTaskUnblockTime. However if + * tickless idling is used it might be more important to enter sleep mode + * at the earliest possible time - so reset xNextTaskUnblockTime here to + * ensure it is updated at the earliest possible time. */ + prvResetNextTaskUnblockTime(); + } + #endif + } + else + { + /* The delayed and ready lists cannot be accessed, so hold this task + * pending until the scheduler is resumed. */ + listINSERT_END( &( xPendingReadyList ), &( pxUnblockedTCB->xEventListItem ) ); + } + + if( pxUnblockedTCB->uxPriority > pxCurrentTCB->uxPriority ) + { + /* Return true if the task removed from the event list has a higher + * priority than the calling task. This allows the calling task to know if + * it should force a context switch now. */ + xReturn = pdTRUE; + + /* Mark that a yield is pending in case the user is not using the + * "xHigherPriorityTaskWoken" parameter to an ISR safe FreeRTOS function. */ + xYieldPending = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +void vTaskRemoveFromUnorderedEventList( ListItem_t * pxEventListItem, + const TickType_t xItemValue ) +{ + TCB_t * pxUnblockedTCB; + + /* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. It is used by + * the event flags implementation. */ + configASSERT( uxSchedulerSuspended != ( UBaseType_t ) 0U ); + + /* Store the new item value in the event list. */ + listSET_LIST_ITEM_VALUE( pxEventListItem, xItemValue | taskEVENT_LIST_ITEM_VALUE_IN_USE ); + + /* Remove the event list form the event flag. Interrupts do not access + * event flags. */ + pxUnblockedTCB = listGET_LIST_ITEM_OWNER( pxEventListItem ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + configASSERT( pxUnblockedTCB ); + listREMOVE_ITEM( pxEventListItem ); + + #if ( configUSE_TICKLESS_IDLE != 0 ) + { + /* If a task is blocked on a kernel object then xNextTaskUnblockTime + * might be set to the blocked task's time out time. If the task is + * unblocked for a reason other than a timeout xNextTaskUnblockTime is + * normally left unchanged, because it is automatically reset to a new + * value when the tick count equals xNextTaskUnblockTime. However if + * tickless idling is used it might be more important to enter sleep mode + * at the earliest possible time - so reset xNextTaskUnblockTime here to + * ensure it is updated at the earliest possible time. */ + prvResetNextTaskUnblockTime(); + } + #endif + + /* Remove the task from the delayed list and add it to the ready list. The + * scheduler is suspended so interrupts will not be accessing the ready + * lists. */ + listREMOVE_ITEM( &( pxUnblockedTCB->xStateListItem ) ); + prvAddTaskToReadyList( pxUnblockedTCB ); + + if( pxUnblockedTCB->uxPriority > pxCurrentTCB->uxPriority ) + { + /* The unblocked task has a priority above that of the calling task, so + * a context switch is required. This function is called with the + * scheduler suspended so xYieldPending is set so the context switch + * occurs immediately that the scheduler is resumed (unsuspended). */ + xYieldPending = pdTRUE; + } +} +/*-----------------------------------------------------------*/ + +void vTaskSetTimeOutState( TimeOut_t * const pxTimeOut ) +{ + configASSERT( pxTimeOut ); + taskENTER_CRITICAL(); + { + pxTimeOut->xOverflowCount = xNumOfOverflows; + pxTimeOut->xTimeOnEntering = xTickCount; + } + taskEXIT_CRITICAL(); +} +/*-----------------------------------------------------------*/ + +void vTaskInternalSetTimeOutState( TimeOut_t * const pxTimeOut ) +{ + /* For internal use only as it does not use a critical section. */ + pxTimeOut->xOverflowCount = xNumOfOverflows; + pxTimeOut->xTimeOnEntering = xTickCount; +} +/*-----------------------------------------------------------*/ + +BaseType_t xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, + TickType_t * const pxTicksToWait ) +{ + BaseType_t xReturn; + + configASSERT( pxTimeOut ); + configASSERT( pxTicksToWait ); + + taskENTER_CRITICAL(); + { + /* Minor optimisation. The tick count cannot change in this block. */ + const TickType_t xConstTickCount = xTickCount; + const TickType_t xElapsedTime = xConstTickCount - pxTimeOut->xTimeOnEntering; + + #if ( INCLUDE_xTaskAbortDelay == 1 ) + if( pxCurrentTCB->ucDelayAborted != ( uint8_t ) pdFALSE ) + { + /* The delay was aborted, which is not the same as a time out, + * but has the same result. */ + pxCurrentTCB->ucDelayAborted = pdFALSE; + xReturn = pdTRUE; + } + else + #endif + + #if ( INCLUDE_vTaskSuspend == 1 ) + if( *pxTicksToWait == portMAX_DELAY ) + { + /* If INCLUDE_vTaskSuspend is set to 1 and the block time + * specified is the maximum block time then the task should block + * indefinitely, and therefore never time out. */ + xReturn = pdFALSE; + } + else + #endif + + if( ( xNumOfOverflows != pxTimeOut->xOverflowCount ) && ( xConstTickCount >= pxTimeOut->xTimeOnEntering ) ) /*lint !e525 Indentation preferred as is to make code within pre-processor directives clearer. */ + { + /* The tick count is greater than the time at which + * vTaskSetTimeout() was called, but has also overflowed since + * vTaskSetTimeOut() was called. It must have wrapped all the way + * around and gone past again. This passed since vTaskSetTimeout() + * was called. */ + xReturn = pdTRUE; + *pxTicksToWait = ( TickType_t ) 0; + } + else if( xElapsedTime < *pxTicksToWait ) /*lint !e961 Explicit casting is only redundant with some compilers, whereas others require it to prevent integer conversion errors. */ + { + /* Not a genuine timeout. Adjust parameters for time remaining. */ + *pxTicksToWait -= xElapsedTime; + vTaskInternalSetTimeOutState( pxTimeOut ); + xReturn = pdFALSE; + } + else + { + *pxTicksToWait = ( TickType_t ) 0; + xReturn = pdTRUE; + } + } + taskEXIT_CRITICAL(); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +void vTaskMissedYield( void ) +{ + xYieldPending = pdTRUE; +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + UBaseType_t uxTaskGetTaskNumber( TaskHandle_t xTask ) + { + UBaseType_t uxReturn; + TCB_t const * pxTCB; + + if( xTask != NULL ) + { + pxTCB = xTask; + uxReturn = pxTCB->uxTaskNumber; + } + else + { + uxReturn = 0U; + } + + return uxReturn; + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + void vTaskSetTaskNumber( TaskHandle_t xTask, + const UBaseType_t uxHandle ) + { + TCB_t * pxTCB; + + if( xTask != NULL ) + { + pxTCB = xTask; + pxTCB->uxTaskNumber = uxHandle; + } + } + +#endif /* configUSE_TRACE_FACILITY */ + +/* + * ----------------------------------------------------------- + * The Idle task. + * ---------------------------------------------------------- + * + * The portTASK_FUNCTION() macro is used to allow port/compiler specific + * language extensions. The equivalent prototype for this function is: + * + * void prvIdleTask( void *pvParameters ); + * + */ + +static portTASK_FUNCTION( prvIdleTask, pvParameters ) +{ + /* Stop warnings. */ + ( void ) pvParameters; + + /** THIS IS THE RTOS IDLE TASK - WHICH IS CREATED AUTOMATICALLY WHEN THE + * SCHEDULER IS STARTED. **/ + + /* In case a task that has a secure context deletes itself, in which case + * the idle task is responsible for deleting the task's secure context, if + * any. */ + portALLOCATE_SECURE_CONTEXT( configMINIMAL_SECURE_STACK_SIZE ); + + for( ; ; ) + { + /* See if any tasks have deleted themselves - if so then the idle task + * is responsible for freeing the deleted task's TCB and stack. */ + prvCheckTasksWaitingTermination(); + + #if ( configUSE_PREEMPTION == 0 ) + { + /* If we are not using preemption we keep forcing a task switch to + * see if any other task has become available. If we are using + * preemption we don't need to do this as any task becoming available + * will automatically get the processor anyway. */ + taskYIELD(); + } + #endif /* configUSE_PREEMPTION */ + + #if ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ) + { + /* When using preemption tasks of equal priority will be + * timesliced. If a task that is sharing the idle priority is ready + * to run then the idle task should yield before the end of the + * timeslice. + * + * A critical region is not required here as we are just reading from + * the list, and an occasional incorrect value will not matter. If + * the ready list at the idle priority contains more than one task + * then a task other than the idle task is ready to execute. */ + if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > ( UBaseType_t ) 1 ) + { + taskYIELD(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ) */ + + #if ( configUSE_IDLE_HOOK == 1 ) + { + /* Call the user defined function from within the idle task. */ + vApplicationIdleHook(); + } + #endif /* configUSE_IDLE_HOOK */ + + /* This conditional compilation should use inequality to 0, not equality + * to 1. This is to ensure portSUPPRESS_TICKS_AND_SLEEP() is called when + * user defined low power mode implementations require + * configUSE_TICKLESS_IDLE to be set to a value other than 1. */ + #if ( configUSE_TICKLESS_IDLE != 0 ) + { + TickType_t xExpectedIdleTime; + + /* It is not desirable to suspend then resume the scheduler on + * each iteration of the idle task. Therefore, a preliminary + * test of the expected idle time is performed without the + * scheduler suspended. The result here is not necessarily + * valid. */ + xExpectedIdleTime = prvGetExpectedIdleTime(); + + if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP ) + { + vTaskSuspendAll(); + { + /* Now the scheduler is suspended, the expected idle + * time can be sampled again, and this time its value can + * be used. */ + configASSERT( xNextTaskUnblockTime >= xTickCount ); + xExpectedIdleTime = prvGetExpectedIdleTime(); + + /* Define the following macro to set xExpectedIdleTime to 0 + * if the application does not want + * portSUPPRESS_TICKS_AND_SLEEP() to be called. */ + configPRE_SUPPRESS_TICKS_AND_SLEEP_PROCESSING( xExpectedIdleTime ); + + if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP ) + { + traceLOW_POWER_IDLE_BEGIN(); + portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ); + traceLOW_POWER_IDLE_END(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + ( void ) xTaskResumeAll(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_TICKLESS_IDLE */ + } +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_TICKLESS_IDLE != 0 ) + + eSleepModeStatus eTaskConfirmSleepModeStatus( void ) + { + #if ( INCLUDE_vTaskSuspend == 1 ) + /* The idle task exists in addition to the application tasks. */ + const UBaseType_t uxNonApplicationTasks = 1; + #endif /* INCLUDE_vTaskSuspend */ + + eSleepModeStatus eReturn = eStandardSleep; + + /* This function must be called from a critical section. */ + + if( listCURRENT_LIST_LENGTH( &xPendingReadyList ) != 0 ) + { + /* A task was made ready while the scheduler was suspended. */ + eReturn = eAbortSleep; + } + else if( xYieldPending != pdFALSE ) + { + /* A yield was pended while the scheduler was suspended. */ + eReturn = eAbortSleep; + } + else if( xPendedTicks != 0 ) + { + /* A tick interrupt has already occurred but was held pending + * because the scheduler is suspended. */ + eReturn = eAbortSleep; + } + + #if ( INCLUDE_vTaskSuspend == 1 ) + else if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == ( uxCurrentNumberOfTasks - uxNonApplicationTasks ) ) + { + /* If all the tasks are in the suspended list (which might mean they + * have an infinite block time rather than actually being suspended) + * then it is safe to turn all clocks off and just wait for external + * interrupts. */ + eReturn = eNoTasksWaitingTimeout; + } + #endif /* INCLUDE_vTaskSuspend */ + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return eReturn; + } + +#endif /* configUSE_TICKLESS_IDLE */ +/*-----------------------------------------------------------*/ + +#if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 ) + + void vTaskSetThreadLocalStoragePointer( TaskHandle_t xTaskToSet, + BaseType_t xIndex, + void * pvValue ) + { + TCB_t * pxTCB; + + if( ( xIndex >= 0 ) && + ( xIndex < configNUM_THREAD_LOCAL_STORAGE_POINTERS ) ) + { + pxTCB = prvGetTCBFromHandle( xTaskToSet ); + configASSERT( pxTCB != NULL ); + pxTCB->pvThreadLocalStoragePointers[ xIndex ] = pvValue; + } + } + +#endif /* configNUM_THREAD_LOCAL_STORAGE_POINTERS */ +/*-----------------------------------------------------------*/ + +#if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 ) + + void * pvTaskGetThreadLocalStoragePointer( TaskHandle_t xTaskToQuery, + BaseType_t xIndex ) + { + void * pvReturn = NULL; + TCB_t * pxTCB; + + if( ( xIndex >= 0 ) && + ( xIndex < configNUM_THREAD_LOCAL_STORAGE_POINTERS ) ) + { + pxTCB = prvGetTCBFromHandle( xTaskToQuery ); + pvReturn = pxTCB->pvThreadLocalStoragePointers[ xIndex ]; + } + else + { + pvReturn = NULL; + } + + return pvReturn; + } + +#endif /* configNUM_THREAD_LOCAL_STORAGE_POINTERS */ +/*-----------------------------------------------------------*/ + +#if ( portUSING_MPU_WRAPPERS == 1 ) + + void vTaskAllocateMPURegions( TaskHandle_t xTaskToModify, + const MemoryRegion_t * const xRegions ) + { + TCB_t * pxTCB; + + /* If null is passed in here then we are modifying the MPU settings of + * the calling task. */ + pxTCB = prvGetTCBFromHandle( xTaskToModify ); + + vPortStoreTaskMPUSettings( &( pxTCB->xMPUSettings ), xRegions, NULL, 0 ); + } + +#endif /* portUSING_MPU_WRAPPERS */ +/*-----------------------------------------------------------*/ + +static void prvInitialiseTaskLists( void ) +{ + UBaseType_t uxPriority; + + for( uxPriority = ( UBaseType_t ) 0U; uxPriority < ( UBaseType_t ) configMAX_PRIORITIES; uxPriority++ ) + { + vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) ); + } + + vListInitialise( &xDelayedTaskList1 ); + vListInitialise( &xDelayedTaskList2 ); + vListInitialise( &xPendingReadyList ); + + #if ( INCLUDE_vTaskDelete == 1 ) + { + vListInitialise( &xTasksWaitingTermination ); + } + #endif /* INCLUDE_vTaskDelete */ + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + vListInitialise( &xSuspendedTaskList ); + } + #endif /* INCLUDE_vTaskSuspend */ + + /* Start with pxDelayedTaskList using list1 and the pxOverflowDelayedTaskList + * using list2. */ + pxDelayedTaskList = &xDelayedTaskList1; + pxOverflowDelayedTaskList = &xDelayedTaskList2; +} +/*-----------------------------------------------------------*/ + +static void prvCheckTasksWaitingTermination( void ) +{ + /** THIS FUNCTION IS CALLED FROM THE RTOS IDLE TASK **/ + + #if ( INCLUDE_vTaskDelete == 1 ) + { + TCB_t * pxTCB; + + /* uxDeletedTasksWaitingCleanUp is used to prevent taskENTER_CRITICAL() + * being called too often in the idle task. */ + while( uxDeletedTasksWaitingCleanUp > ( UBaseType_t ) 0U ) + { + taskENTER_CRITICAL(); + { + pxTCB = listGET_OWNER_OF_HEAD_ENTRY( ( &xTasksWaitingTermination ) ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); + --uxCurrentNumberOfTasks; + --uxDeletedTasksWaitingCleanUp; + } + taskEXIT_CRITICAL(); + + prvDeleteTCB( pxTCB ); + } + } + #endif /* INCLUDE_vTaskDelete */ +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + void vTaskGetInfo( TaskHandle_t xTask, + TaskStatus_t * pxTaskStatus, + BaseType_t xGetFreeStackSpace, + eTaskState eState ) + { + TCB_t * pxTCB; + + /* xTask is NULL then get the state of the calling task. */ + pxTCB = prvGetTCBFromHandle( xTask ); + + pxTaskStatus->xHandle = ( TaskHandle_t ) pxTCB; + pxTaskStatus->pcTaskName = ( const char * ) &( pxTCB->pcTaskName[ 0 ] ); + pxTaskStatus->uxCurrentPriority = pxTCB->uxPriority; + pxTaskStatus->pxStackBase = pxTCB->pxStack; + #if ( ( portSTACK_GROWTH > 0 ) && ( configRECORD_STACK_HIGH_ADDRESS == 1 ) ) + pxTaskStatus->pxTopOfStack = pxTCB->pxTopOfStack; + pxTaskStatus->pxEndOfStack = pxTCB->pxEndOfStack; + #endif + pxTaskStatus->xTaskNumber = pxTCB->uxTCBNumber; + + #if ( configUSE_MUTEXES == 1 ) + { + pxTaskStatus->uxBasePriority = pxTCB->uxBasePriority; + } + #else + { + pxTaskStatus->uxBasePriority = 0; + } + #endif + + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + { + pxTaskStatus->ulRunTimeCounter = pxTCB->ulRunTimeCounter; + } + #else + { + pxTaskStatus->ulRunTimeCounter = ( configRUN_TIME_COUNTER_TYPE ) 0; + } + #endif + + /* Obtaining the task state is a little fiddly, so is only done if the + * value of eState passed into this function is eInvalid - otherwise the + * state is just set to whatever is passed in. */ + if( eState != eInvalid ) + { + if( pxTCB == pxCurrentTCB ) + { + pxTaskStatus->eCurrentState = eRunning; + } + else + { + pxTaskStatus->eCurrentState = eState; + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + /* If the task is in the suspended list then there is a + * chance it is actually just blocked indefinitely - so really + * it should be reported as being in the Blocked state. */ + if( eState == eSuspended ) + { + vTaskSuspendAll(); + { + if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) + { + pxTaskStatus->eCurrentState = eBlocked; + } + } + ( void ) xTaskResumeAll(); + } + } + #endif /* INCLUDE_vTaskSuspend */ + + /* Tasks can be in pending ready list and other state list at the + * same time. These tasks are in ready state no matter what state + * list the task is in. */ + taskENTER_CRITICAL(); + { + if( listIS_CONTAINED_WITHIN( &xPendingReadyList, &( pxTCB->xEventListItem ) ) != pdFALSE ) + { + pxTaskStatus->eCurrentState = eReady; + } + } + taskEXIT_CRITICAL(); + } + } + else + { + pxTaskStatus->eCurrentState = eTaskGetState( pxTCB ); + } + + /* Obtaining the stack space takes some time, so the xGetFreeStackSpace + * parameter is provided to allow it to be skipped. */ + if( xGetFreeStackSpace != pdFALSE ) + { + #if ( portSTACK_GROWTH > 0 ) + { + pxTaskStatus->usStackHighWaterMark = prvTaskCheckFreeStackSpace( ( uint8_t * ) pxTCB->pxEndOfStack ); + } + #else + { + pxTaskStatus->usStackHighWaterMark = prvTaskCheckFreeStackSpace( ( uint8_t * ) pxTCB->pxStack ); + } + #endif + } + else + { + pxTaskStatus->usStackHighWaterMark = 0; + } + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + static UBaseType_t prvListTasksWithinSingleList( TaskStatus_t * pxTaskStatusArray, + List_t * pxList, + eTaskState eState ) + { + configLIST_VOLATILE TCB_t * pxNextTCB; + configLIST_VOLATILE TCB_t * pxFirstTCB; + UBaseType_t uxTask = 0; + + if( listCURRENT_LIST_LENGTH( pxList ) > ( UBaseType_t ) 0 ) + { + listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + + /* Populate an TaskStatus_t structure within the + * pxTaskStatusArray array for each task that is referenced from + * pxList. See the definition of TaskStatus_t in task.h for the + * meaning of each TaskStatus_t structure member. */ + do + { + listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + vTaskGetInfo( ( TaskHandle_t ) pxNextTCB, &( pxTaskStatusArray[ uxTask ] ), pdTRUE, eState ); + uxTask++; + } while( pxNextTCB != pxFirstTCB ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return uxTask; + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + +#if ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) ) + + static configSTACK_DEPTH_TYPE prvTaskCheckFreeStackSpace( const uint8_t * pucStackByte ) + { + uint32_t ulCount = 0U; + + while( *pucStackByte == ( uint8_t ) tskSTACK_FILL_BYTE ) + { + pucStackByte -= portSTACK_GROWTH; + ulCount++; + } + + ulCount /= ( uint32_t ) sizeof( StackType_t ); /*lint !e961 Casting is not redundant on smaller architectures. */ + + return ( configSTACK_DEPTH_TYPE ) ulCount; + } + +#endif /* ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) ) */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) + +/* uxTaskGetStackHighWaterMark() and uxTaskGetStackHighWaterMark2() are the + * same except for their return type. Using configSTACK_DEPTH_TYPE allows the + * user to determine the return type. It gets around the problem of the value + * overflowing on 8-bit types without breaking backward compatibility for + * applications that expect an 8-bit return type. */ + configSTACK_DEPTH_TYPE uxTaskGetStackHighWaterMark2( TaskHandle_t xTask ) + { + TCB_t * pxTCB; + uint8_t * pucEndOfStack; + configSTACK_DEPTH_TYPE uxReturn; + + /* uxTaskGetStackHighWaterMark() and uxTaskGetStackHighWaterMark2() are + * the same except for their return type. Using configSTACK_DEPTH_TYPE + * allows the user to determine the return type. It gets around the + * problem of the value overflowing on 8-bit types without breaking + * backward compatibility for applications that expect an 8-bit return + * type. */ + + pxTCB = prvGetTCBFromHandle( xTask ); + + #if portSTACK_GROWTH < 0 + { + pucEndOfStack = ( uint8_t * ) pxTCB->pxStack; + } + #else + { + pucEndOfStack = ( uint8_t * ) pxTCB->pxEndOfStack; + } + #endif + + uxReturn = prvTaskCheckFreeStackSpace( pucEndOfStack ); + + return uxReturn; + } + +#endif /* INCLUDE_uxTaskGetStackHighWaterMark2 */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) + + UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask ) + { + TCB_t * pxTCB; + uint8_t * pucEndOfStack; + UBaseType_t uxReturn; + + pxTCB = prvGetTCBFromHandle( xTask ); + + #if portSTACK_GROWTH < 0 + { + pucEndOfStack = ( uint8_t * ) pxTCB->pxStack; + } + #else + { + pucEndOfStack = ( uint8_t * ) pxTCB->pxEndOfStack; + } + #endif + + uxReturn = ( UBaseType_t ) prvTaskCheckFreeStackSpace( pucEndOfStack ); + + return uxReturn; + } + +#endif /* INCLUDE_uxTaskGetStackHighWaterMark */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskDelete == 1 ) + + static void prvDeleteTCB( TCB_t * pxTCB ) + { + /* This call is required specifically for the TriCore port. It must be + * above the vPortFree() calls. The call is also used by ports/demos that + * want to allocate and clean RAM statically. */ + portCLEAN_UP_TCB( pxTCB ); + + #if ( configUSE_C_RUNTIME_TLS_SUPPORT == 1 ) + { + /* Free up the memory allocated for the task's TLS Block. */ + configDEINIT_TLS_BLOCK( pxCurrentTCB->xTLSBlock ); + } + #endif + + #if ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) && ( portUSING_MPU_WRAPPERS == 0 ) ) + { + /* The task can only have been allocated dynamically - free both + * the stack and TCB. */ + vPortFreeStack( pxTCB->pxStack ); + vPortFree( pxTCB ); + } + #elif ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e731 !e9029 Macro has been consolidated for readability reasons. */ + { + /* The task could have been allocated statically or dynamically, so + * check what was statically allocated before trying to free the + * memory. */ + if( pxTCB->ucStaticallyAllocated == tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB ) + { + /* Both the stack and TCB were allocated dynamically, so both + * must be freed. */ + vPortFreeStack( pxTCB->pxStack ); + vPortFree( pxTCB ); + } + else if( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_ONLY ) + { + /* Only the stack was statically allocated, so the TCB is the + * only memory that must be freed. */ + vPortFree( pxTCB ); + } + else + { + /* Neither the stack nor the TCB were allocated dynamically, so + * nothing needs to be freed. */ + configASSERT( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_AND_TCB ); + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ + } + +#endif /* INCLUDE_vTaskDelete */ +/*-----------------------------------------------------------*/ + +static void prvResetNextTaskUnblockTime( void ) +{ + if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE ) + { + /* The new current delayed list is empty. Set xNextTaskUnblockTime to + * the maximum possible value so it is extremely unlikely that the + * if( xTickCount >= xNextTaskUnblockTime ) test will pass until + * there is an item in the delayed list. */ + xNextTaskUnblockTime = portMAX_DELAY; + } + else + { + /* The new current delayed list is not empty, get the value of + * the item at the head of the delayed list. This is the time at + * which the task at the head of the delayed list should be removed + * from the Blocked state. */ + xNextTaskUnblockTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxDelayedTaskList ); + } +} +/*-----------------------------------------------------------*/ + +#if ( ( INCLUDE_xTaskGetCurrentTaskHandle == 1 ) || ( configUSE_MUTEXES == 1 ) ) + + TaskHandle_t xTaskGetCurrentTaskHandle( void ) + { + TaskHandle_t xReturn; + + /* A critical section is not required as this is not called from + * an interrupt and the current TCB will always be the same for any + * individual execution thread. */ + xReturn = pxCurrentTCB; + + return xReturn; + } + +#endif /* ( ( INCLUDE_xTaskGetCurrentTaskHandle == 1 ) || ( configUSE_MUTEXES == 1 ) ) */ +/*-----------------------------------------------------------*/ + +#if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) + + BaseType_t xTaskGetSchedulerState( void ) + { + BaseType_t xReturn; + + if( xSchedulerRunning == pdFALSE ) + { + xReturn = taskSCHEDULER_NOT_STARTED; + } + else + { + if( uxSchedulerSuspended == ( UBaseType_t ) 0U ) + { + xReturn = taskSCHEDULER_RUNNING; + } + else + { + xReturn = taskSCHEDULER_SUSPENDED; + } + } + + return xReturn; + } + +#endif /* ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_MUTEXES == 1 ) + + BaseType_t xTaskPriorityInherit( TaskHandle_t const pxMutexHolder ) + { + TCB_t * const pxMutexHolderTCB = pxMutexHolder; + BaseType_t xReturn = pdFALSE; + + /* If the mutex was given back by an interrupt while the queue was + * locked then the mutex holder might now be NULL. _RB_ Is this still + * needed as interrupts can no longer use mutexes? */ + if( pxMutexHolder != NULL ) + { + /* If the holder of the mutex has a priority below the priority of + * the task attempting to obtain the mutex then it will temporarily + * inherit the priority of the task attempting to obtain the mutex. */ + if( pxMutexHolderTCB->uxPriority < pxCurrentTCB->uxPriority ) + { + /* Adjust the mutex holder state to account for its new + * priority. Only reset the event list item value if the value is + * not being used for anything else. */ + if( ( listGET_LIST_ITEM_VALUE( &( pxMutexHolderTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL ) + { + listSET_LIST_ITEM_VALUE( &( pxMutexHolderTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxCurrentTCB->uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* If the task being modified is in the ready state it will need + * to be moved into a new list. */ + if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ pxMutexHolderTCB->uxPriority ] ), &( pxMutexHolderTCB->xStateListItem ) ) != pdFALSE ) + { + if( uxListRemove( &( pxMutexHolderTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) + { + /* It is known that the task is in its ready list so + * there is no need to check again and the port level + * reset macro can be called directly. */ + portRESET_READY_PRIORITY( pxMutexHolderTCB->uxPriority, uxTopReadyPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Inherit the priority before being moved into the new list. */ + pxMutexHolderTCB->uxPriority = pxCurrentTCB->uxPriority; + prvAddTaskToReadyList( pxMutexHolderTCB ); + } + else + { + /* Just inherit the priority. */ + pxMutexHolderTCB->uxPriority = pxCurrentTCB->uxPriority; + } + + traceTASK_PRIORITY_INHERIT( pxMutexHolderTCB, pxCurrentTCB->uxPriority ); + + /* Inheritance occurred. */ + xReturn = pdTRUE; + } + else + { + if( pxMutexHolderTCB->uxBasePriority < pxCurrentTCB->uxPriority ) + { + /* The base priority of the mutex holder is lower than the + * priority of the task attempting to take the mutex, but the + * current priority of the mutex holder is not lower than the + * priority of the task attempting to take the mutex. + * Therefore the mutex holder must have already inherited a + * priority, but inheritance would have occurred if that had + * not been the case. */ + xReturn = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return xReturn; + } + +#endif /* configUSE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_MUTEXES == 1 ) + + BaseType_t xTaskPriorityDisinherit( TaskHandle_t const pxMutexHolder ) + { + TCB_t * const pxTCB = pxMutexHolder; + BaseType_t xReturn = pdFALSE; + + if( pxMutexHolder != NULL ) + { + /* A task can only have an inherited priority if it holds the mutex. + * If the mutex is held by a task then it cannot be given from an + * interrupt, and if a mutex is given by the holding task then it must + * be the running state task. */ + configASSERT( pxTCB == pxCurrentTCB ); + configASSERT( pxTCB->uxMutexesHeld ); + ( pxTCB->uxMutexesHeld )--; + + /* Has the holder of the mutex inherited the priority of another + * task? */ + if( pxTCB->uxPriority != pxTCB->uxBasePriority ) + { + /* Only disinherit if no other mutexes are held. */ + if( pxTCB->uxMutexesHeld == ( UBaseType_t ) 0 ) + { + /* A task can only have an inherited priority if it holds + * the mutex. If the mutex is held by a task then it cannot be + * given from an interrupt, and if a mutex is given by the + * holding task then it must be the running state task. Remove + * the holding task from the ready list. */ + if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) + { + portRESET_READY_PRIORITY( pxTCB->uxPriority, uxTopReadyPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Disinherit the priority before adding the task into the + * new ready list. */ + traceTASK_PRIORITY_DISINHERIT( pxTCB, pxTCB->uxBasePriority ); + pxTCB->uxPriority = pxTCB->uxBasePriority; + + /* Reset the event list item value. It cannot be in use for + * any other purpose if this task is running, and it must be + * running to give back the mutex. */ + listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxTCB->uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + prvAddTaskToReadyList( pxTCB ); + + /* Return true to indicate that a context switch is required. + * This is only actually required in the corner case whereby + * multiple mutexes were held and the mutexes were given back + * in an order different to that in which they were taken. + * If a context switch did not occur when the first mutex was + * returned, even if a task was waiting on it, then a context + * switch should occur when the last mutex is returned whether + * a task is waiting on it or not. */ + xReturn = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return xReturn; + } + +#endif /* configUSE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_MUTEXES == 1 ) + + void vTaskPriorityDisinheritAfterTimeout( TaskHandle_t const pxMutexHolder, + UBaseType_t uxHighestPriorityWaitingTask ) + { + TCB_t * const pxTCB = pxMutexHolder; + UBaseType_t uxPriorityUsedOnEntry, uxPriorityToUse; + const UBaseType_t uxOnlyOneMutexHeld = ( UBaseType_t ) 1; + + if( pxMutexHolder != NULL ) + { + /* If pxMutexHolder is not NULL then the holder must hold at least + * one mutex. */ + configASSERT( pxTCB->uxMutexesHeld ); + + /* Determine the priority to which the priority of the task that + * holds the mutex should be set. This will be the greater of the + * holding task's base priority and the priority of the highest + * priority task that is waiting to obtain the mutex. */ + if( pxTCB->uxBasePriority < uxHighestPriorityWaitingTask ) + { + uxPriorityToUse = uxHighestPriorityWaitingTask; + } + else + { + uxPriorityToUse = pxTCB->uxBasePriority; + } + + /* Does the priority need to change? */ + if( pxTCB->uxPriority != uxPriorityToUse ) + { + /* Only disinherit if no other mutexes are held. This is a + * simplification in the priority inheritance implementation. If + * the task that holds the mutex is also holding other mutexes then + * the other mutexes may have caused the priority inheritance. */ + if( pxTCB->uxMutexesHeld == uxOnlyOneMutexHeld ) + { + /* If a task has timed out because it already holds the + * mutex it was trying to obtain then it cannot of inherited + * its own priority. */ + configASSERT( pxTCB != pxCurrentTCB ); + + /* Disinherit the priority, remembering the previous + * priority to facilitate determining the subject task's + * state. */ + traceTASK_PRIORITY_DISINHERIT( pxTCB, uxPriorityToUse ); + uxPriorityUsedOnEntry = pxTCB->uxPriority; + pxTCB->uxPriority = uxPriorityToUse; + + /* Only reset the event list item value if the value is not + * being used for anything else. */ + if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL ) + { + listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriorityToUse ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* If the running task is not the task that holds the mutex + * then the task that holds the mutex could be in either the + * Ready, Blocked or Suspended states. Only remove the task + * from its current state list if it is in the Ready state as + * the task's priority is going to change and there is one + * Ready list per priority. */ + if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxPriorityUsedOnEntry ] ), &( pxTCB->xStateListItem ) ) != pdFALSE ) + { + if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) + { + /* It is known that the task is in its ready list so + * there is no need to check again and the port level + * reset macro can be called directly. */ + portRESET_READY_PRIORITY( pxTCB->uxPriority, uxTopReadyPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + prvAddTaskToReadyList( pxTCB ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + +#endif /* configUSE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if ( portCRITICAL_NESTING_IN_TCB == 1 ) + + void vTaskEnterCritical( void ) + { + portDISABLE_INTERRUPTS(); + + if( xSchedulerRunning != pdFALSE ) + { + ( pxCurrentTCB->uxCriticalNesting )++; + + /* This is not the interrupt safe version of the enter critical + * function so assert() if it is being called from an interrupt + * context. Only API functions that end in "FromISR" can be used in an + * interrupt. Only assert if the critical nesting count is 1 to + * protect against recursive calls if the assert function also uses a + * critical section. */ + if( pxCurrentTCB->uxCriticalNesting == 1 ) + { + portASSERT_IF_IN_ISR(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + +#endif /* portCRITICAL_NESTING_IN_TCB */ +/*-----------------------------------------------------------*/ + +#if ( portCRITICAL_NESTING_IN_TCB == 1 ) + + void vTaskExitCritical( void ) + { + if( xSchedulerRunning != pdFALSE ) + { + if( pxCurrentTCB->uxCriticalNesting > 0U ) + { + ( pxCurrentTCB->uxCriticalNesting )--; + + if( pxCurrentTCB->uxCriticalNesting == 0U ) + { + portENABLE_INTERRUPTS(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + +#endif /* portCRITICAL_NESTING_IN_TCB */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) + + static char * prvWriteNameToBuffer( char * pcBuffer, + const char * pcTaskName ) + { + size_t x; + + /* Start by copying the entire string. */ + strcpy( pcBuffer, pcTaskName ); + + /* Pad the end of the string with spaces to ensure columns line up when + * printed out. */ + for( x = strlen( pcBuffer ); x < ( size_t ) ( configMAX_TASK_NAME_LEN - 1 ); x++ ) + { + pcBuffer[ x ] = ' '; + } + + /* Terminate. */ + pcBuffer[ x ] = ( char ) 0x00; + + /* Return the new end of string. */ + return &( pcBuffer[ x ] ); + } + +#endif /* ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) */ +/*-----------------------------------------------------------*/ + +#if ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) ) + + void vTaskList( char * pcWriteBuffer ) + { + TaskStatus_t * pxTaskStatusArray; + UBaseType_t uxArraySize, x; + char cStatus; + + /* + * PLEASE NOTE: + * + * This function is provided for convenience only, and is used by many + * of the demo applications. Do not consider it to be part of the + * scheduler. + * + * vTaskList() calls uxTaskGetSystemState(), then formats part of the + * uxTaskGetSystemState() output into a human readable table that + * displays task: names, states, priority, stack usage and task number. + * Stack usage specified as the number of unused StackType_t words stack can hold + * on top of stack - not the number of bytes. + * + * vTaskList() has a dependency on the sprintf() C library function that + * might bloat the code size, use a lot of stack, and provide different + * results on different platforms. An alternative, tiny, third party, + * and limited functionality implementation of sprintf() is provided in + * many of the FreeRTOS/Demo sub-directories in a file called + * printf-stdarg.c (note printf-stdarg.c does not provide a full + * snprintf() implementation!). + * + * It is recommended that production systems call uxTaskGetSystemState() + * directly to get access to raw stats data, rather than indirectly + * through a call to vTaskList(). + */ + + + /* Make sure the write buffer does not contain a string. */ + *pcWriteBuffer = ( char ) 0x00; + + /* Take a snapshot of the number of tasks in case it changes while this + * function is executing. */ + uxArraySize = uxCurrentNumberOfTasks; + + /* Allocate an array index for each task. NOTE! if + * configSUPPORT_DYNAMIC_ALLOCATION is set to 0 then pvPortMalloc() will + * equate to NULL. */ + pxTaskStatusArray = pvPortMalloc( uxCurrentNumberOfTasks * sizeof( TaskStatus_t ) ); /*lint !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack and this allocation allocates a struct that has the alignment requirements of a pointer. */ + + if( pxTaskStatusArray != NULL ) + { + /* Generate the (binary) data. */ + uxArraySize = uxTaskGetSystemState( pxTaskStatusArray, uxArraySize, NULL ); + + /* Create a human readable table from the binary data. */ + for( x = 0; x < uxArraySize; x++ ) + { + switch( pxTaskStatusArray[ x ].eCurrentState ) + { + case eRunning: + cStatus = tskRUNNING_CHAR; + break; + + case eReady: + cStatus = tskREADY_CHAR; + break; + + case eBlocked: + cStatus = tskBLOCKED_CHAR; + break; + + case eSuspended: + cStatus = tskSUSPENDED_CHAR; + break; + + case eDeleted: + cStatus = tskDELETED_CHAR; + break; + + case eInvalid: /* Fall through. */ + default: /* Should not get here, but it is included + * to prevent static checking errors. */ + cStatus = ( char ) 0x00; + break; + } + + /* Write the task name to the string, padding with spaces so it + * can be printed in tabular form more easily. */ + pcWriteBuffer = prvWriteNameToBuffer( pcWriteBuffer, pxTaskStatusArray[ x ].pcTaskName ); + + /* Write the rest of the string. */ + sprintf( pcWriteBuffer, "\t%c\t%u\t%u\t%u\r\n", cStatus, ( unsigned int ) pxTaskStatusArray[ x ].uxCurrentPriority, ( unsigned int ) pxTaskStatusArray[ x ].usStackHighWaterMark, ( unsigned int ) pxTaskStatusArray[ x ].xTaskNumber ); /*lint !e586 sprintf() allowed as this is compiled with many compilers and this is a utility function only - not part of the core kernel implementation. */ + pcWriteBuffer += strlen( pcWriteBuffer ); /*lint !e9016 Pointer arithmetic ok on char pointers especially as in this case where it best denotes the intent of the code. */ + } + + /* Free the array again. NOTE! If configSUPPORT_DYNAMIC_ALLOCATION + * is 0 then vPortFree() will be #defined to nothing. */ + vPortFree( pxTaskStatusArray ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + +#endif /* ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) ) */ +/*----------------------------------------------------------*/ + +#if ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && ( configUSE_TRACE_FACILITY == 1 ) ) + + void vTaskGetRunTimeStats( char * pcWriteBuffer ) + { + TaskStatus_t * pxTaskStatusArray; + UBaseType_t uxArraySize, x; + configRUN_TIME_COUNTER_TYPE ulTotalTime, ulStatsAsPercentage; + + /* + * PLEASE NOTE: + * + * This function is provided for convenience only, and is used by many + * of the demo applications. Do not consider it to be part of the + * scheduler. + * + * vTaskGetRunTimeStats() calls uxTaskGetSystemState(), then formats part + * of the uxTaskGetSystemState() output into a human readable table that + * displays the amount of time each task has spent in the Running state + * in both absolute and percentage terms. + * + * vTaskGetRunTimeStats() has a dependency on the sprintf() C library + * function that might bloat the code size, use a lot of stack, and + * provide different results on different platforms. An alternative, + * tiny, third party, and limited functionality implementation of + * sprintf() is provided in many of the FreeRTOS/Demo sub-directories in + * a file called printf-stdarg.c (note printf-stdarg.c does not provide + * a full snprintf() implementation!). + * + * It is recommended that production systems call uxTaskGetSystemState() + * directly to get access to raw stats data, rather than indirectly + * through a call to vTaskGetRunTimeStats(). + */ + + /* Make sure the write buffer does not contain a string. */ + *pcWriteBuffer = ( char ) 0x00; + + /* Take a snapshot of the number of tasks in case it changes while this + * function is executing. */ + uxArraySize = uxCurrentNumberOfTasks; + + /* Allocate an array index for each task. NOTE! If + * configSUPPORT_DYNAMIC_ALLOCATION is set to 0 then pvPortMalloc() will + * equate to NULL. */ + pxTaskStatusArray = pvPortMalloc( uxCurrentNumberOfTasks * sizeof( TaskStatus_t ) ); /*lint !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack and this allocation allocates a struct that has the alignment requirements of a pointer. */ + + if( pxTaskStatusArray != NULL ) + { + /* Generate the (binary) data. */ + uxArraySize = uxTaskGetSystemState( pxTaskStatusArray, uxArraySize, &ulTotalTime ); + + /* For percentage calculations. */ + ulTotalTime /= 100UL; + + /* Avoid divide by zero errors. */ + if( ulTotalTime > 0UL ) + { + /* Create a human readable table from the binary data. */ + for( x = 0; x < uxArraySize; x++ ) + { + /* What percentage of the total run time has the task used? + * This will always be rounded down to the nearest integer. + * ulTotalRunTime has already been divided by 100. */ + ulStatsAsPercentage = pxTaskStatusArray[ x ].ulRunTimeCounter / ulTotalTime; + + /* Write the task name to the string, padding with + * spaces so it can be printed in tabular form more + * easily. */ + pcWriteBuffer = prvWriteNameToBuffer( pcWriteBuffer, pxTaskStatusArray[ x ].pcTaskName ); + + if( ulStatsAsPercentage > 0UL ) + { + #ifdef portLU_PRINTF_SPECIFIER_REQUIRED + { + sprintf( pcWriteBuffer, "\t%lu\t\t%lu%%\r\n", pxTaskStatusArray[ x ].ulRunTimeCounter, ulStatsAsPercentage ); + } + #else + { + /* sizeof( int ) == sizeof( long ) so a smaller + * printf() library can be used. */ + sprintf( pcWriteBuffer, "\t%u\t\t%u%%\r\n", ( unsigned int ) pxTaskStatusArray[ x ].ulRunTimeCounter, ( unsigned int ) ulStatsAsPercentage ); /*lint !e586 sprintf() allowed as this is compiled with many compilers and this is a utility function only - not part of the core kernel implementation. */ + } + #endif + } + else + { + /* If the percentage is zero here then the task has + * consumed less than 1% of the total run time. */ + #ifdef portLU_PRINTF_SPECIFIER_REQUIRED + { + sprintf( pcWriteBuffer, "\t%lu\t\t<1%%\r\n", pxTaskStatusArray[ x ].ulRunTimeCounter ); + } + #else + { + /* sizeof( int ) == sizeof( long ) so a smaller + * printf() library can be used. */ + sprintf( pcWriteBuffer, "\t%u\t\t<1%%\r\n", ( unsigned int ) pxTaskStatusArray[ x ].ulRunTimeCounter ); /*lint !e586 sprintf() allowed as this is compiled with many compilers and this is a utility function only - not part of the core kernel implementation. */ + } + #endif + } + + pcWriteBuffer += strlen( pcWriteBuffer ); /*lint !e9016 Pointer arithmetic ok on char pointers especially as in this case where it best denotes the intent of the code. */ + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Free the array again. NOTE! If configSUPPORT_DYNAMIC_ALLOCATION + * is 0 then vPortFree() will be #defined to nothing. */ + vPortFree( pxTaskStatusArray ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + +#endif /* ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) ) */ +/*-----------------------------------------------------------*/ + +TickType_t uxTaskResetEventItemValue( void ) +{ + TickType_t uxReturn; + + uxReturn = listGET_LIST_ITEM_VALUE( &( pxCurrentTCB->xEventListItem ) ); + + /* Reset the event list item to its normal value - so it can be used with + * queues and semaphores. */ + listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xEventListItem ), ( ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxCurrentTCB->uxPriority ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + + return uxReturn; +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_MUTEXES == 1 ) + + TaskHandle_t pvTaskIncrementMutexHeldCount( void ) + { + /* If xSemaphoreCreateMutex() is called before any tasks have been created + * then pxCurrentTCB will be NULL. */ + if( pxCurrentTCB != NULL ) + { + ( pxCurrentTCB->uxMutexesHeld )++; + } + + return pxCurrentTCB; + } + +#endif /* configUSE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TASK_NOTIFICATIONS == 1 ) + + uint32_t ulTaskGenericNotifyTake( UBaseType_t uxIndexToWait, + BaseType_t xClearCountOnExit, + TickType_t xTicksToWait ) + { + uint32_t ulReturn; + + configASSERT( uxIndexToWait < configTASK_NOTIFICATION_ARRAY_ENTRIES ); + + taskENTER_CRITICAL(); + { + /* Only block if the notification count is not already non-zero. */ + if( pxCurrentTCB->ulNotifiedValue[ uxIndexToWait ] == 0UL ) + { + /* Mark this task as waiting for a notification. */ + pxCurrentTCB->ucNotifyState[ uxIndexToWait ] = taskWAITING_NOTIFICATION; + + if( xTicksToWait > ( TickType_t ) 0 ) + { + prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE ); + traceTASK_NOTIFY_TAKE_BLOCK( uxIndexToWait ); + + /* All ports are written to allow a yield in a critical + * section (some will yield immediately, others wait until the + * critical section exits) - but it is not something that + * application code should ever do. */ + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); + + taskENTER_CRITICAL(); + { + traceTASK_NOTIFY_TAKE( uxIndexToWait ); + ulReturn = pxCurrentTCB->ulNotifiedValue[ uxIndexToWait ]; + + if( ulReturn != 0UL ) + { + if( xClearCountOnExit != pdFALSE ) + { + pxCurrentTCB->ulNotifiedValue[ uxIndexToWait ] = 0UL; + } + else + { + pxCurrentTCB->ulNotifiedValue[ uxIndexToWait ] = ulReturn - ( uint32_t ) 1; + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + pxCurrentTCB->ucNotifyState[ uxIndexToWait ] = taskNOT_WAITING_NOTIFICATION; + } + taskEXIT_CRITICAL(); + + return ulReturn; + } + +#endif /* configUSE_TASK_NOTIFICATIONS */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TASK_NOTIFICATIONS == 1 ) + + BaseType_t xTaskGenericNotifyWait( UBaseType_t uxIndexToWait, + uint32_t ulBitsToClearOnEntry, + uint32_t ulBitsToClearOnExit, + uint32_t * pulNotificationValue, + TickType_t xTicksToWait ) + { + BaseType_t xReturn; + + configASSERT( uxIndexToWait < configTASK_NOTIFICATION_ARRAY_ENTRIES ); + + taskENTER_CRITICAL(); + { + /* Only block if a notification is not already pending. */ + if( pxCurrentTCB->ucNotifyState[ uxIndexToWait ] != taskNOTIFICATION_RECEIVED ) + { + /* Clear bits in the task's notification value as bits may get + * set by the notifying task or interrupt. This can be used to + * clear the value to zero. */ + pxCurrentTCB->ulNotifiedValue[ uxIndexToWait ] &= ~ulBitsToClearOnEntry; + + /* Mark this task as waiting for a notification. */ + pxCurrentTCB->ucNotifyState[ uxIndexToWait ] = taskWAITING_NOTIFICATION; + + if( xTicksToWait > ( TickType_t ) 0 ) + { + prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE ); + traceTASK_NOTIFY_WAIT_BLOCK( uxIndexToWait ); + + /* All ports are written to allow a yield in a critical + * section (some will yield immediately, others wait until the + * critical section exits) - but it is not something that + * application code should ever do. */ + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); + + taskENTER_CRITICAL(); + { + traceTASK_NOTIFY_WAIT( uxIndexToWait ); + + if( pulNotificationValue != NULL ) + { + /* Output the current notification value, which may or may not + * have changed. */ + *pulNotificationValue = pxCurrentTCB->ulNotifiedValue[ uxIndexToWait ]; + } + + /* If ucNotifyValue is set then either the task never entered the + * blocked state (because a notification was already pending) or the + * task unblocked because of a notification. Otherwise the task + * unblocked because of a timeout. */ + if( pxCurrentTCB->ucNotifyState[ uxIndexToWait ] != taskNOTIFICATION_RECEIVED ) + { + /* A notification was not received. */ + xReturn = pdFALSE; + } + else + { + /* A notification was already pending or a notification was + * received while the task was waiting. */ + pxCurrentTCB->ulNotifiedValue[ uxIndexToWait ] &= ~ulBitsToClearOnExit; + xReturn = pdTRUE; + } + + pxCurrentTCB->ucNotifyState[ uxIndexToWait ] = taskNOT_WAITING_NOTIFICATION; + } + taskEXIT_CRITICAL(); + + return xReturn; + } + +#endif /* configUSE_TASK_NOTIFICATIONS */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TASK_NOTIFICATIONS == 1 ) + + BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify, + UBaseType_t uxIndexToNotify, + uint32_t ulValue, + eNotifyAction eAction, + uint32_t * pulPreviousNotificationValue ) + { + TCB_t * pxTCB; + BaseType_t xReturn = pdPASS; + uint8_t ucOriginalNotifyState; + + configASSERT( uxIndexToNotify < configTASK_NOTIFICATION_ARRAY_ENTRIES ); + configASSERT( xTaskToNotify ); + pxTCB = xTaskToNotify; + + taskENTER_CRITICAL(); + { + if( pulPreviousNotificationValue != NULL ) + { + *pulPreviousNotificationValue = pxTCB->ulNotifiedValue[ uxIndexToNotify ]; + } + + ucOriginalNotifyState = pxTCB->ucNotifyState[ uxIndexToNotify ]; + + pxTCB->ucNotifyState[ uxIndexToNotify ] = taskNOTIFICATION_RECEIVED; + + switch( eAction ) + { + case eSetBits: + pxTCB->ulNotifiedValue[ uxIndexToNotify ] |= ulValue; + break; + + case eIncrement: + ( pxTCB->ulNotifiedValue[ uxIndexToNotify ] )++; + break; + + case eSetValueWithOverwrite: + pxTCB->ulNotifiedValue[ uxIndexToNotify ] = ulValue; + break; + + case eSetValueWithoutOverwrite: + + if( ucOriginalNotifyState != taskNOTIFICATION_RECEIVED ) + { + pxTCB->ulNotifiedValue[ uxIndexToNotify ] = ulValue; + } + else + { + /* The value could not be written to the task. */ + xReturn = pdFAIL; + } + + break; + + case eNoAction: + + /* The task is being notified without its notify value being + * updated. */ + break; + + default: + + /* Should not get here if all enums are handled. + * Artificially force an assert by testing a value the + * compiler can't assume is const. */ + configASSERT( xTickCount == ( TickType_t ) 0 ); + + break; + } + + traceTASK_NOTIFY( uxIndexToNotify ); + + /* If the task is in the blocked state specifically to wait for a + * notification then unblock it now. */ + if( ucOriginalNotifyState == taskWAITING_NOTIFICATION ) + { + listREMOVE_ITEM( &( pxTCB->xStateListItem ) ); + prvAddTaskToReadyList( pxTCB ); + + /* The task should not have been on an event list. */ + configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL ); + + #if ( configUSE_TICKLESS_IDLE != 0 ) + { + /* If a task is blocked waiting for a notification then + * xNextTaskUnblockTime might be set to the blocked task's time + * out time. If the task is unblocked for a reason other than + * a timeout xNextTaskUnblockTime is normally left unchanged, + * because it will automatically get reset to a new value when + * the tick count equals xNextTaskUnblockTime. However if + * tickless idling is used it might be more important to enter + * sleep mode at the earliest possible time - so reset + * xNextTaskUnblockTime here to ensure it is updated at the + * earliest possible time. */ + prvResetNextTaskUnblockTime(); + } + #endif + + if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ) + { + /* The notified task has a priority above the currently + * executing task so a yield is required. */ + taskYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); + + return xReturn; + } + +#endif /* configUSE_TASK_NOTIFICATIONS */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TASK_NOTIFICATIONS == 1 ) + + BaseType_t xTaskGenericNotifyFromISR( TaskHandle_t xTaskToNotify, + UBaseType_t uxIndexToNotify, + uint32_t ulValue, + eNotifyAction eAction, + uint32_t * pulPreviousNotificationValue, + BaseType_t * pxHigherPriorityTaskWoken ) + { + TCB_t * pxTCB; + uint8_t ucOriginalNotifyState; + BaseType_t xReturn = pdPASS; + UBaseType_t uxSavedInterruptStatus; + + configASSERT( xTaskToNotify ); + configASSERT( uxIndexToNotify < configTASK_NOTIFICATION_ARRAY_ENTRIES ); + + /* RTOS ports that support interrupt nesting have the concept of a + * maximum system call (or maximum API call) interrupt priority. + * Interrupts that are above the maximum system call priority are keep + * permanently enabled, even when the RTOS kernel is in a critical section, + * but cannot make any calls to FreeRTOS API functions. If configASSERT() + * is defined in FreeRTOSConfig.h then + * portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + * failure if a FreeRTOS API function is called from an interrupt that has + * been assigned a priority above the configured maximum system call + * priority. Only FreeRTOS functions that end in FromISR can be called + * from interrupts that have been assigned a priority at or (logically) + * below the maximum system call interrupt priority. FreeRTOS maintains a + * separate interrupt safe API to ensure interrupt entry is as fast and as + * simple as possible. More information (albeit Cortex-M specific) is + * provided on the following link: + * https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + pxTCB = xTaskToNotify; + + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + { + if( pulPreviousNotificationValue != NULL ) + { + *pulPreviousNotificationValue = pxTCB->ulNotifiedValue[ uxIndexToNotify ]; + } + + ucOriginalNotifyState = pxTCB->ucNotifyState[ uxIndexToNotify ]; + pxTCB->ucNotifyState[ uxIndexToNotify ] = taskNOTIFICATION_RECEIVED; + + switch( eAction ) + { + case eSetBits: + pxTCB->ulNotifiedValue[ uxIndexToNotify ] |= ulValue; + break; + + case eIncrement: + ( pxTCB->ulNotifiedValue[ uxIndexToNotify ] )++; + break; + + case eSetValueWithOverwrite: + pxTCB->ulNotifiedValue[ uxIndexToNotify ] = ulValue; + break; + + case eSetValueWithoutOverwrite: + + if( ucOriginalNotifyState != taskNOTIFICATION_RECEIVED ) + { + pxTCB->ulNotifiedValue[ uxIndexToNotify ] = ulValue; + } + else + { + /* The value could not be written to the task. */ + xReturn = pdFAIL; + } + + break; + + case eNoAction: + + /* The task is being notified without its notify value being + * updated. */ + break; + + default: + + /* Should not get here if all enums are handled. + * Artificially force an assert by testing a value the + * compiler can't assume is const. */ + configASSERT( xTickCount == ( TickType_t ) 0 ); + break; + } + + traceTASK_NOTIFY_FROM_ISR( uxIndexToNotify ); + + /* If the task is in the blocked state specifically to wait for a + * notification then unblock it now. */ + if( ucOriginalNotifyState == taskWAITING_NOTIFICATION ) + { + /* The task should not have been on an event list. */ + configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL ); + + if( uxSchedulerSuspended == ( UBaseType_t ) 0U ) + { + listREMOVE_ITEM( &( pxTCB->xStateListItem ) ); + prvAddTaskToReadyList( pxTCB ); + } + else + { + /* The delayed and ready lists cannot be accessed, so hold + * this task pending until the scheduler is resumed. */ + listINSERT_END( &( xPendingReadyList ), &( pxTCB->xEventListItem ) ); + } + + if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ) + { + /* The notified task has a priority above the currently + * executing task so a yield is required. */ + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + + /* Mark that a yield is pending in case the user is not + * using the "xHigherPriorityTaskWoken" parameter to an ISR + * safe FreeRTOS function. */ + xYieldPending = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + return xReturn; + } + +#endif /* configUSE_TASK_NOTIFICATIONS */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TASK_NOTIFICATIONS == 1 ) + + void vTaskGenericNotifyGiveFromISR( TaskHandle_t xTaskToNotify, + UBaseType_t uxIndexToNotify, + BaseType_t * pxHigherPriorityTaskWoken ) + { + TCB_t * pxTCB; + uint8_t ucOriginalNotifyState; + UBaseType_t uxSavedInterruptStatus; + + configASSERT( xTaskToNotify ); + configASSERT( uxIndexToNotify < configTASK_NOTIFICATION_ARRAY_ENTRIES ); + + /* RTOS ports that support interrupt nesting have the concept of a + * maximum system call (or maximum API call) interrupt priority. + * Interrupts that are above the maximum system call priority are keep + * permanently enabled, even when the RTOS kernel is in a critical section, + * but cannot make any calls to FreeRTOS API functions. If configASSERT() + * is defined in FreeRTOSConfig.h then + * portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + * failure if a FreeRTOS API function is called from an interrupt that has + * been assigned a priority above the configured maximum system call + * priority. Only FreeRTOS functions that end in FromISR can be called + * from interrupts that have been assigned a priority at or (logically) + * below the maximum system call interrupt priority. FreeRTOS maintains a + * separate interrupt safe API to ensure interrupt entry is as fast and as + * simple as possible. More information (albeit Cortex-M specific) is + * provided on the following link: + * https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + pxTCB = xTaskToNotify; + + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + { + ucOriginalNotifyState = pxTCB->ucNotifyState[ uxIndexToNotify ]; + pxTCB->ucNotifyState[ uxIndexToNotify ] = taskNOTIFICATION_RECEIVED; + + /* 'Giving' is equivalent to incrementing a count in a counting + * semaphore. */ + ( pxTCB->ulNotifiedValue[ uxIndexToNotify ] )++; + + traceTASK_NOTIFY_GIVE_FROM_ISR( uxIndexToNotify ); + + /* If the task is in the blocked state specifically to wait for a + * notification then unblock it now. */ + if( ucOriginalNotifyState == taskWAITING_NOTIFICATION ) + { + /* The task should not have been on an event list. */ + configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL ); + + if( uxSchedulerSuspended == ( UBaseType_t ) 0U ) + { + listREMOVE_ITEM( &( pxTCB->xStateListItem ) ); + prvAddTaskToReadyList( pxTCB ); + } + else + { + /* The delayed and ready lists cannot be accessed, so hold + * this task pending until the scheduler is resumed. */ + listINSERT_END( &( xPendingReadyList ), &( pxTCB->xEventListItem ) ); + } + + if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ) + { + /* The notified task has a priority above the currently + * executing task so a yield is required. */ + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + + /* Mark that a yield is pending in case the user is not + * using the "xHigherPriorityTaskWoken" parameter in an ISR + * safe FreeRTOS function. */ + xYieldPending = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + } + +#endif /* configUSE_TASK_NOTIFICATIONS */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TASK_NOTIFICATIONS == 1 ) + + BaseType_t xTaskGenericNotifyStateClear( TaskHandle_t xTask, + UBaseType_t uxIndexToClear ) + { + TCB_t * pxTCB; + BaseType_t xReturn; + + configASSERT( uxIndexToClear < configTASK_NOTIFICATION_ARRAY_ENTRIES ); + + /* If null is passed in here then it is the calling task that is having + * its notification state cleared. */ + pxTCB = prvGetTCBFromHandle( xTask ); + + taskENTER_CRITICAL(); + { + if( pxTCB->ucNotifyState[ uxIndexToClear ] == taskNOTIFICATION_RECEIVED ) + { + pxTCB->ucNotifyState[ uxIndexToClear ] = taskNOT_WAITING_NOTIFICATION; + xReturn = pdPASS; + } + else + { + xReturn = pdFAIL; + } + } + taskEXIT_CRITICAL(); + + return xReturn; + } + +#endif /* configUSE_TASK_NOTIFICATIONS */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TASK_NOTIFICATIONS == 1 ) + + uint32_t ulTaskGenericNotifyValueClear( TaskHandle_t xTask, + UBaseType_t uxIndexToClear, + uint32_t ulBitsToClear ) + { + TCB_t * pxTCB; + uint32_t ulReturn; + + /* If null is passed in here then it is the calling task that is having + * its notification state cleared. */ + pxTCB = prvGetTCBFromHandle( xTask ); + + taskENTER_CRITICAL(); + { + /* Return the notification as it was before the bits were cleared, + * then clear the bit mask. */ + ulReturn = pxTCB->ulNotifiedValue[ uxIndexToClear ]; + pxTCB->ulNotifiedValue[ uxIndexToClear ] &= ~ulBitsToClear; + } + taskEXIT_CRITICAL(); + + return ulReturn; + } + +#endif /* configUSE_TASK_NOTIFICATIONS */ +/*-----------------------------------------------------------*/ + +#if ( configGENERATE_RUN_TIME_STATS == 1 ) + + configRUN_TIME_COUNTER_TYPE ulTaskGetRunTimeCounter( const TaskHandle_t xTask ) + { + return xTask->ulRunTimeCounter; + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( configGENERATE_RUN_TIME_STATS == 1 ) + + configRUN_TIME_COUNTER_TYPE ulTaskGetRunTimePercent( const TaskHandle_t xTask ) + { + configRUN_TIME_COUNTER_TYPE ulTotalTime, ulReturn; + + ulTotalTime = ( configRUN_TIME_COUNTER_TYPE ) portGET_RUN_TIME_COUNTER_VALUE(); + + /* For percentage calculations. */ + ulTotalTime /= ( configRUN_TIME_COUNTER_TYPE ) 100; + + /* Avoid divide by zero errors. */ + if( ulTotalTime > ( configRUN_TIME_COUNTER_TYPE ) 0 ) + { + ulReturn = xTask->ulRunTimeCounter / ulTotalTime; + } + else + { + ulReturn = 0; + } + + return ulReturn; + } + +#endif /* if ( configGENERATE_RUN_TIME_STATS == 1 ) */ +/*-----------------------------------------------------------*/ + +#if ( configGENERATE_RUN_TIME_STATS == 1 ) + + configRUN_TIME_COUNTER_TYPE ulTaskGetIdleRunTimeCounter( void ) + { + return ulTaskGetRunTimeCounter( xIdleTaskHandle ); + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( configGENERATE_RUN_TIME_STATS == 1 ) + + configRUN_TIME_COUNTER_TYPE ulTaskGetIdleRunTimePercent( void ) + { + return ulTaskGetRunTimePercent( xIdleTaskHandle ); + } + +#endif +/*-----------------------------------------------------------*/ + +static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait, + const BaseType_t xCanBlockIndefinitely ) +{ + TickType_t xTimeToWake; + const TickType_t xConstTickCount = xTickCount; + + #if ( INCLUDE_xTaskAbortDelay == 1 ) + { + /* About to enter a delayed list, so ensure the ucDelayAborted flag is + * reset to pdFALSE so it can be detected as having been set to pdTRUE + * when the task leaves the Blocked state. */ + pxCurrentTCB->ucDelayAborted = pdFALSE; + } + #endif + + /* Remove the task from the ready list before adding it to the blocked list + * as the same list item is used for both lists. */ + if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) + { + /* The current task must be in a ready list, so there is no need to + * check, and the port reset macro can be called directly. */ + portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority ); /*lint !e931 pxCurrentTCB cannot change as it is the calling task. pxCurrentTCB->uxPriority and uxTopReadyPriority cannot change as called with scheduler suspended or in a critical section. */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + if( ( xTicksToWait == portMAX_DELAY ) && ( xCanBlockIndefinitely != pdFALSE ) ) + { + /* Add the task to the suspended task list instead of a delayed task + * list to ensure it is not woken by a timing event. It will block + * indefinitely. */ + listINSERT_END( &xSuspendedTaskList, &( pxCurrentTCB->xStateListItem ) ); + } + else + { + /* Calculate the time at which the task should be woken if the event + * does not occur. This may overflow but this doesn't matter, the + * kernel will manage it correctly. */ + xTimeToWake = xConstTickCount + xTicksToWait; + + /* The list item will be inserted in wake time order. */ + listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake ); + + if( xTimeToWake < xConstTickCount ) + { + /* Wake time has overflowed. Place this item in the overflow + * list. */ + vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) ); + } + else + { + /* The wake time has not overflowed, so the current block list + * is used. */ + vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) ); + + /* If the task entering the blocked state was placed at the + * head of the list of blocked tasks then xNextTaskUnblockTime + * needs to be updated too. */ + if( xTimeToWake < xNextTaskUnblockTime ) + { + xNextTaskUnblockTime = xTimeToWake; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + } + #else /* INCLUDE_vTaskSuspend */ + { + /* Calculate the time at which the task should be woken if the event + * does not occur. This may overflow but this doesn't matter, the kernel + * will manage it correctly. */ + xTimeToWake = xConstTickCount + xTicksToWait; + + /* The list item will be inserted in wake time order. */ + listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake ); + + if( xTimeToWake < xConstTickCount ) + { + /* Wake time has overflowed. Place this item in the overflow list. */ + vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) ); + } + else + { + /* The wake time has not overflowed, so the current block list is used. */ + vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) ); + + /* If the task entering the blocked state was placed at the head of the + * list of blocked tasks then xNextTaskUnblockTime needs to be updated + * too. */ + if( xTimeToWake < xNextTaskUnblockTime ) + { + xNextTaskUnblockTime = xTimeToWake; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + /* Avoid compiler warning when INCLUDE_vTaskSuspend is not 1. */ + ( void ) xCanBlockIndefinitely; + } + #endif /* INCLUDE_vTaskSuspend */ +} +/*-----------------------------------------------------------*/ + +#if ( portUSING_MPU_WRAPPERS == 1 ) + + xMPU_SETTINGS * xTaskGetMPUSettings( TaskHandle_t xTask ) + { + TCB_t * pxTCB; + + pxTCB = prvGetTCBFromHandle( xTask ); + + return &( pxTCB->xMPUSettings ); + } + +#endif /* portUSING_MPU_WRAPPERS */ +/*-----------------------------------------------------------*/ + +/* Code below here allows additional code to be inserted into this source file, + * especially where access to file scope functions and data is needed (for example + * when performing module tests). */ + +#ifdef FREERTOS_MODULE_TEST + #include "tasks_test_access_functions.h" +#endif + + +#if ( configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H == 1 ) + + #include "freertos_tasks_c_additions.h" + + #ifdef FREERTOS_TASKS_C_ADDITIONS_INIT + static void freertos_tasks_c_additions_init( void ) + { + FREERTOS_TASKS_C_ADDITIONS_INIT(); + } + #endif + +#endif /* if ( configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H == 1 ) */