From b7756da26298f2a859faf7ab4e670d404467509b Mon Sep 17 00:00:00 2001 From: Soren Ptak Date: Sat, 5 Aug 2023 14:59:20 -0700 Subject: [PATCH 001/131] * CI-CD Overhaul * Swap to using clang-format instead of uncrustify * Use a rust spell checker instead of the current one * Use echo groups to make logs easier to parse * Add a PR Check that runs against all repos * Idea of common config templates for repos that use this --------- --- .gitattributes | 2 + .github/workflows/pr_checks.yml | 242 + .github/workflows/test.yml | 1269 +++- .gitignore | 4 + README.md | 2 +- complexity/action.yml | 4 +- coverage-cop/action.yml | 2 +- doxygen-generation/action.yml | 6 +- doxygen/action.yml | 2 +- executable-monitor/README.md | 34 +- executable-monitor/action.yml | 69 +- executable-monitor/executable-monitor.py | 88 +- executable-monitor/test.c | 99 +- formatting/.clang-format | 104 + formatting/action.yml | 161 +- formatting/filesWithCRLFEndings/cgc_error.h | 47 + formatting/filesWithCRLFEndings/main-blinky.c | 193 + formatting/filesWithCRLFEndings/out.txt | 47 + .../fileFormattedWithClangFormatV16.c | 6151 ++++++++++++++++ .../goodFileInHere.c | 6151 ++++++++++++++++ formatting/filesWithFormattingErrors/test.c | 95 + formatting/filesWithTrailingWhitespace/test.c | 94 + formatting/getFiles | 118 + formatting/goodFiles/include/task.h | 3761 ++++++++++ formatting/goodFiles/source/README.md | 6153 +++++++++++++++++ formatting/goodFiles/source/badFile.c | 193 + formatting/goodFiles/source/core_mqtt.c | 3515 ++++++++++ formatting/goodFiles/source/tasks.c | 6151 ++++++++++++++++ formatting/goodFiles/source/test.c | 100 + formatting/uncrustify.cfg | 160 - link-verifier/action.yml | 53 +- localhost-echo-server/action.yml | 2 +- localhost-http-1.1-server/action.yml | 2 +- .../localhost_http_1.1_server.py | 4 +- localhost-mqtt-broker/action.yml | 2 +- manifest-verifier/action.yml | 43 +- manifest-verifier/verify_manifest.py | 57 +- memory_statistics/memory_statistics.py | 8 +- rust-spell-check/Cargo.lock | 535 ++ rust-spell-check/Cargo.toml | 20 + rust-spell-check/action.yml | 119 + .../crates/libaspell-sys/Cargo.toml | 9 + .../crates/libaspell-sys/build.rs | 18 + .../crates/libaspell-sys/src/lib.rs | 5 + .../crates/libaspell-sys/wrapper.h | 1 + rust-spell-check/getFiles | 82 + rust-spell-check/queries/c.scm | 2 + rust-spell-check/queries/python.scm | 2 + rust-spell-check/spell-checker | Bin 0 -> 6295800 bytes rust-spell-check/src/main.rs | 266 + sbom-generator/action.yml | 2 +- sbom-generator/sbom_utils.py | 6 +- sbom-generator/scan_dir.py | 12 +- spellings/action.yml | 114 +- spellings/cspell.config.yaml | 22 + spellings/getFiles | 81 + spellings/tools/README.md | 30 - spellings/tools/ablexicon | 88 - spellings/tools/extract-comments | 41 - spellings/tools/find-unknown-comment-words | 152 - ssl-credential-creator/action.yml | 31 +- .../ssl_credential_creator.py | 2 +- uncrustify-formatting/action.yml | 169 + .../filesWithCRLFEndings/cgc_error.h | 47 + .../filesWithCRLFEndings/main-blinky.c | 193 + .../goodFileInHere.c | 5532 +++++++++++++++ .../filesWithFormattingErrors/test.c | 95 + .../filesWithTrailingWhitespace/test.c | 94 + .../goodFiles/include/task.h | 3477 ++++++++++ .../goodFiles/source/badFile.c | 193 + .../goodFiles/source/core_mqtt.c | 3388 +++++++++ .../goodFiles/source/tasks.c | 5532 +++++++++++++++ uncrustify-formatting/goodFiles/source/test.c | 94 + uncrustify-formatting/uncrustify.cfg | 673 ++ 74 files changed, 55412 insertions(+), 903 deletions(-) create mode 100644 .gitattributes create mode 100644 .github/workflows/pr_checks.yml create mode 100644 formatting/.clang-format create mode 100644 formatting/filesWithCRLFEndings/cgc_error.h create mode 100644 formatting/filesWithCRLFEndings/main-blinky.c create mode 100644 formatting/filesWithCRLFEndings/out.txt create mode 100644 formatting/filesWithFormattingErrors/fileFormattedWithClangFormatV16.c create mode 100644 formatting/filesWithFormattingErrors/goodFileInHere.c create mode 100644 formatting/filesWithFormattingErrors/test.c create mode 100644 formatting/filesWithTrailingWhitespace/test.c create mode 100755 formatting/getFiles create mode 100644 formatting/goodFiles/include/task.h create mode 100644 formatting/goodFiles/source/README.md create mode 100644 formatting/goodFiles/source/badFile.c create mode 100644 formatting/goodFiles/source/core_mqtt.c create mode 100644 formatting/goodFiles/source/tasks.c create mode 100644 formatting/goodFiles/source/test.c delete mode 100644 formatting/uncrustify.cfg create mode 100644 rust-spell-check/Cargo.lock create mode 100644 rust-spell-check/Cargo.toml create mode 100644 rust-spell-check/action.yml create mode 100644 rust-spell-check/crates/libaspell-sys/Cargo.toml create mode 100644 rust-spell-check/crates/libaspell-sys/build.rs create mode 100644 rust-spell-check/crates/libaspell-sys/src/lib.rs create mode 100644 rust-spell-check/crates/libaspell-sys/wrapper.h create mode 100755 rust-spell-check/getFiles create mode 100644 rust-spell-check/queries/c.scm create mode 100644 rust-spell-check/queries/python.scm create mode 100755 rust-spell-check/spell-checker create mode 100644 rust-spell-check/src/main.rs create mode 100644 spellings/cspell.config.yaml create mode 100755 spellings/getFiles delete mode 100644 spellings/tools/README.md delete mode 100755 spellings/tools/ablexicon delete mode 100755 spellings/tools/extract-comments delete mode 100755 spellings/tools/find-unknown-comment-words create mode 100644 uncrustify-formatting/action.yml create mode 100644 uncrustify-formatting/filesWithCRLFEndings/cgc_error.h create mode 100644 uncrustify-formatting/filesWithCRLFEndings/main-blinky.c create mode 100644 uncrustify-formatting/filesWithFormattingErrors/goodFileInHere.c create mode 100644 uncrustify-formatting/filesWithFormattingErrors/test.c create mode 100644 uncrustify-formatting/filesWithTrailingWhitespace/test.c create mode 100644 uncrustify-formatting/goodFiles/include/task.h create mode 100644 uncrustify-formatting/goodFiles/source/badFile.c create mode 100644 uncrustify-formatting/goodFiles/source/core_mqtt.c create mode 100644 uncrustify-formatting/goodFiles/source/tasks.c create mode 100644 uncrustify-formatting/goodFiles/source/test.c create mode 100644 uncrustify-formatting/uncrustify.cfg diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..9a463050 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +formatting/filesWithCRLFEndings/* eol=crlf +formatting/goodFiles/source/badFile.c eol=crlf diff --git a/.github/workflows/pr_checks.yml b/.github/workflows/pr_checks.yml new file mode 100644 index 00000000..81838305 --- /dev/null +++ b/.github/workflows/pr_checks.yml @@ -0,0 +1,242 @@ +name: Repository Pull Request Checks + +on: + pull_request: + branches: [main] + workflow_dispatch: + +env: + # The bash escape character is \033 + bashPass: \033[32;1mPASSED - + bashInfo: \033[33;1mINFO - + bashFail: \033[31;1mFAILED - + bashEnd: \033[0m + + +jobs: + + PR-repo-checks: + runs-on: ubuntu-latest + if: ${{ github.event.pull_request }} || ${{ github.event.workflow }} + strategy: + fail-fast: false + matrix: + inputs: + [ + { + repository: coreHTTP, + exclude-files: lexicon.txt, + exclude-dirs: "build,docs", + #org: FreeRTOS, + org: skptak, + }, + { + repository: coreJSON, + exclude-files: lexicon.txt, + exclude-dirs: "build,docs", + #org: FreeRTOS, + org: skptak, + }, + { + repository: coreMQTT, + exclude-files: lexicon.txt, + exclude-dirs: "build,docs", + #org: FreeRTOS, + org: skptak, + }, + { + repository: corePKCS11, + exclude-files: lexicon.txt, + exclude-dirs: "build,docs,portable", + #org: FreeRTOS, + org: skptak, + }, + { + repository: coreSNTP, + exclude-files: lexicon.txt, + exclude-dirs: "build,docs", + #org: FreeRTOS, + org: skptak, + }, + { + repository: FreeRTOS-Cellular-Interface, + exclude-files: lexicon.txt, + exclude-dirs: "build,docs", + #org: FreeRTOS, + org: skptak, + }, + { + repository: FreeRTOS, + exclude-files: lexicon.txt, + exclude-dirs: "build,docs,portable", + #org: FreeRTOS, + org: skptak, + }, + { + repository: Device-Defender-for-AWS-IoT-embedded-sdk, + exclude-files: lexicon.txt, + exclude-dirs: "build,docs", + #org: aws, + org: skptak, + }, + { + repository: Device-Shadow-for-AWS-IoT-embedded-sdk, + exclude-files: lexicon.txt, + exclude-dirs: "build,docs", + #org: aws, + org: skptak, + }, + { + repository: Fleet-Provisioning-for-AWS-IoT-embedded-sdk, + exclude-files: lexicon.txt, + exclude-dirs: "build,docs", + #org: aws, + org: skptak, + }, + { + repository: Jobs-for-AWS-IoT-embedded-sdk, + exclude-files: "lexicon.txt", + exclude-dirs: "build,docs", + #org: aws, + org: skptak, + }, + { + repository: ota-for-AWS-IoT-embedded-sdk, + exclude-files: "lexicon.txt", + exclude-dirs: "build,docs", + #org: aws, + org: skptak, + }, + { + repository: SigV4-for-AWS-IoT-embedded-sdk, + exclude-files: lexicon.txt, + exclude-dirs: "build,docs", + #org: aws, + org: skptak, + }, + { + repository: FreeRTOS-Kernel, + exclude-files: lexicon.txt, + exclude-dirs: "build,docs,portable", + #org: aws, + org: skptak, + }, + ] + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 + with: + python-version: '3.11.0' + + - name: "Clone: ${{ matrix.inputs.repository }}" + uses: actions/checkout@v3 + with: + repository: ${{ matrix.inputs.org }}/${{ matrix.inputs.repository }} + ref: main + path: ${{ matrix.inputs.repository }} + + - name: "Formatting Check : ${{ matrix.inputs.repository }}" + if: success() || failure() + uses: ./formatting + with: + path: ${{ matrix.inputs.repository }} + exclude-files: ${{ matrix.inputs.exclude-fles}} + exclude-dirs: ${{matrix.inputs.exclude-dirs}} + + - name: "Complexity Check: ${{ matrix.inputs.repository }}" + if: success() || failure() + uses: ./complexity + with: + path: ${{ matrix.inputs.repository }} + # For coreMQTT the code complexity threshold is 10. + horrid_threshold: 10 + + - name: "Doxygen Build Check ${{ matrix.inputs.repository }}" + if: success() || failure() + uses: ./doxygen + with: + path: ${{ matrix.inputs.repository }} + + - name: "Doxygen Zip Check: ${{ matrix.inputs.repository }}" + if: success() || failure() + uses: ./doxygen + with: + path: ${{ matrix.inputs.repository }} + generate_zip: true + + + - name: "Spelling Check: ${{ matrix.inputs.repository }} " + if: success() || failure() + uses: ./spellings + with: + path: ${{ matrix.inputs.repository }} + +# test-coverage-cop: +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v3 +# - uses: actions/checkout@v3 +# with: +# repository: ${{ matrix.inputs.org }}/${{ matrix.inputs.repository }} +# ref: main +# path: ${{ matrix.inputs.repository }} +# - name: Build +# run: | +# sudo apt-get install -y lcov +# cmake -S ./coreMQTT/test -B build/ \ +# -G "Unix Makefiles" \ +# -DCMAKE_BUILD_TYPE=Debug \ +# -DBUILD_CLONE_SUBMODULES=ON \ +# -DCMAKE_C_FLAGS='--coverage -Wall -Wextra -Werror -DNDEBUG -DLIBRARY_LOG_LEVEL=LOG_DEBUG' +# make -C build/ all +# - name: Test +# run: | +# cd build/ +# ctest -E system --output-on-failure +# cd .. +# - name: Run Coverage +# run: | +# make -C build/ coverage +# declare -a EXCLUDE=("\*test/\*" "\*CMakeCCompilerId\*" "\*mocks\*") +# echo ${EXCLUDE[@]} | xargs lcov --rc lcov_branch_coverage=1 -r build/coverage.info -o build/coverage.info +# lcov --rc lcov_branch_coverage=1 --list build/coverage.info +# - name: Test coverage cop action +# uses: ./coverage-cop +# with: +# path: ./build/coverage.info +# branch-coverage-min: 70 +# line-coverage-min: 100 + + - name: "Memory Statistics Check: ${{ matrix.inputs.repository }}" + if: success() || failure() + uses: ./memory_statistics + with: + path: memory_statistics/test + config: ./memory_statistics_config.json + output: ./size_table_new.html + check_against: ./size_table_expected.html + + - name: "Link Verifier Check: ${{ matrix.inputs.repository }}" + uses: ./link-verifier + if: success() || failure() + with: + path: ${{ matrix.inputs.repository }} + exclude-dirs: complexity,formatting + include-file-types: .c,.html + + - name: "Clone: ${{ matrix.inputs.repository }}" + if: success() || failure() + uses: actions/checkout@v3 + with: + repository: ${{ matrix.inputs.org }}/${{ matrix.inputs.repository }} + ref: main + path: ${{ matrix.inputs.repository }}_Recursive + submodules: recursive + + - name: "Manifest Verifier: ${{ matrix.inputs.repository }}" + if: success() || failure() + uses: ./manifest-verifier + with: + path: ${{ matrix.inputs.repository }}_Recursive + exclude-submodules: FreeRTOS-Plus/Test/CMock,FreeRTOS/Test/CMock/CMock,FreeRTOS/Test/litani + fail-on-incorrect-version: true diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 28b749b1..5aa496da 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,223 +7,1072 @@ on: branches: [main] workflow_dispatch: +env: + # The bash escape character is \033 + bashPass: \033[32;1mPASSED - + bashInfo: \033[33;1mINFO - + bashFail: \033[31;1mFAILED - + bashEnd: \033[0m + jobs: - test-format-check: + + get-files-test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - env: + stepName: "Install Dependencies" + name: ${{ env.stepName }} + shell: bash + working-directory: formatting + run: | + # ${{ env.stepName }} + echo "::group::${{ env.stepName }}" + echo "$(pwd)" >> $GITHUB_PATH + + sudo apt install fd-find + fdfind --version + + exitStatus=$? + echo "::endgroup::" + if [ $exitStatus -eq 0 ]; then + echo -e "${{ env.bashPass }} ${{ env.stepName }} ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} ${{ env.stepName }} ${{ env.bashEnd }}" + exit 1 + fi + + - env: + stepName: "Functional | Success | getFiles() No Arguments" + name: ${{ env.stepName }} + shell: bash + working-directory: formatting + run: | + # ${{ env.stepName }} + echo "::group::${{ env.stepName }}" + allFiles=$(grep -sril "copyright.*amazon" --include="*.c" --include="*.h") + files=$(getFiles) + echo -e "${{ env.bashInfo }} allFiles:\n$allFiles ${{ env.bashEnd }}" + echo -e "${{ env.bashInfo }} files:\n$files ${{ env.bashEnd }}" + exitStatus=0 + # Loop through all the files we found using grep + for file in ${allFiles[@]} ; do + echo -e "${{ env.bashInfo }} Checking File: $file ${{ env.bashEnd }}" + # Check if they're in the flattened version of getFiles() search + if ! [[ $files == *"$file"* ]]; then + echo -e "${{ env.bashFail }} Get Files did not find file: $file ${{ env.bashEnd }}" + exitStatus=1 + fi + done + echo "::endgroup::" + if [ $exitStatus -eq 0 ]; then + echo -e "${{ env.bashPass }} ${{ env.stepName }} ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} ${{ env.stepName }} ${{ env.bashEnd }}" + exit 1 + fi + + - env: + stepName: "Functional | Success | getFiles() Exclude Single File" + name: ${{ env.stepName }} + shell: bash + working-directory: formatting + run: | + # ${{ env.stepName }} + echo "::group::${{ env.stepName }}" + files=$(getFiles --exclude-files="test.c") + echo -e "${{ env.bashInfo }} files:\n$files ${{ env.bashEnd }}" + exitStatus=0 + if [[ "$files" == *"test.c"* ]]; then + echo -e "${{ env.bashFail }} Get Files Found a file it should have skipped: test.c ${{ env.bashEnd }}" + exitStatus=1 + elif [[ "$files" != *"tasks.c"* ]]; then + echo -e "${{ env.bashFail }} Get Files Did not find a file it should have: tasks.c ${{ env.bashEnd }}" + exitStatus=1 + fi + echo "::endgroup::" + if [ $exitStatus -eq 0 ]; then + echo -e "${{ env.bashPass }} ${{ env.stepName }} ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} ${{ env.stepName }} ${{ env.bashEnd }}" + exit 1 + fi + + - env: + stepName: "Functional | Success | getFiles() Exclude Single Directory" + name: ${{ env.stepName }} + shell: bash + working-directory: formatting + run: | + # ${{ env.stepName }} + echo "::group::${{ env.stepName }}" + files=$(getFiles --exclude-dirs="goodFiles") + echo -e "${{ env.bashInfo }} files:\n$files ${{ env.bashEnd }}" + exitStatus=0 + if [[ "$files" == *"goodFiles"* ]]; then + echo -e "${{ env.bashFail }} Get Files Found a directory it should have skipped: goodFiles ${{ env.bashEnd }}" + exitStatus=1 + fi + echo "::endgroup::" + if [ $exitStatus -eq 0 ]; then + echo -e "${{ env.bashPass }} ${{ env.stepName }} ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} ${{ env.stepName }} ${{ env.bashEnd }}" + exit 1 + fi + + - env: + stepName: "Functional | Success | getFiles() Exclude Single Directory and File" + name: ${{ env.stepName }} + shell: bash + working-directory: formatting + run: | + # ${{ env.stepName }} + echo "::group::${{ env.stepName }}" + files=$(getFiles --exclude-dirs="goodFiles" --exclude-files="test.c") + echo -e "${{ env.bashInfo }} files:\n$files ${{ env.bashInfo }}" + exitStatus=0 + if [[ "$files" == *"test.c"* ]]; then + echo -e "${{ env.bashFail }} Get Files Found a directory it should have skipped: goodFiles ${{ env.bashEnd }}" + exitStatus=1 + fi + echo "::endgroup::" + if [ $exitStatus -eq 0 ]; then + echo -e "${{ env.bashPass }} ${{ env.stepName }} ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} ${{ env.stepName }} ${{ env.bashEnd }}" + exit 1 + fi + + - env: + stepName: "Functional | Success | getFiles() Include Extra Type" + name: ${{ env.stepName }} + shell: bash + working-directory: formatting + run: | + # ${{ env.stepName }} + echo "::group::${{ env.stepName }}" + + files=$(getFiles --include-extensions="md") + echo -e "${{ env.bashInfo }} files:\n$files ${{ env.bashEnd }}" + + exitStatus=0 + if [[ "$files" != *"README.md"* ]]; then + echo -e "${{ env.bashFail }} Get Files Found a directory it should have skipped: goodFiles ${{ env.bashEnd }}" + exitStatus=1 + fi + + echo "::endgroup::" + if [ $exitStatus -eq 0 ]; then + echo -e "${{ env.bashPass }} ${{ env.stepName }} ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} ${{ env.stepName }} ${{ env.bashEnd }}" + exit 1 + fi + + - env: + stepName: "Functional | Success | getFiles() Exclude Two Directories and Two Files" + name: ${{ env.stepName }} + shell: bash + working-directory: formatting + run: | + # ${{ env.stepName }} + echo "::group::${{ env.stepName }}" + files="" + files=$(getFiles --exclude-dirs="filesWithCRLFEndings,include" --exclude-files="fileFormattedWithClangFormatV16.c,badFile.c") + + echo -e "${{ env.bashInfo }} files:\n"$files" ${{ env.bashEnd }}" + exitStatus=0 + if [[ "$files" == *"fileFormattedWithClangFormatV16.c"* ]]; then + echo -e "${{ env.bashFail }} Get Files Found a File it should have skipped: fileFormattedWithClangFormatV16.c ${{ env.bashEnd }}" + exitStatus=1 + fi + + if [[ "$files" == *"badFile.c"* ]]; then + echo -e "${{ env.bashFail }} Get Files Found a File it should have skipped: badFile.c ${{ env.bashEnd }}" + exitStatus=1 + fi + + if [[ "$files" == *"filesWithCRLFEndings"* ]]; then + echo -e "${{ env.bashFail }} Get Files Found a directory it should have skipped: filesWithCRLFEndings ${{ env.bashEnd }}" + exitStatus=1 + fi + + if [[ "$files" == *"include"* ]]; then + echo -e "${{ env.bashFail }} Get Files Found a directory it should have skipped: include ${{ env.bashEnd }}" + exitStatus=1 + fi + + echo "::endgroup::" + if [ $exitStatus -eq 0 ]; then + echo -e "${{ env.bashPass }} ${{ env.stepName }} ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} ${{ env.stepName }} ${{ env.bashEnd }}" + exit 1 + fi + + uncrustify-formatting-success-cases: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v3 + + - env: + stepName: "Functional | Success | Exclude All Error Dirs" + name: ${{ env.stepName }} + id: exclude-dirs-with-errors + uses: ./uncrustify-formatting + with: + path: uncrustify-formatting + exclude-dirs: "filesWithFormattingErrors,filesWithCRLFEndings,filesWithTrailingWhitespace" + exclude-files: "badFile.c" + + - env: + stepName: "Functional | Success | Exclude Files" + name: ${{ env.stepName }} + id: exclude-files-with-errors + uses: ./uncrustify-formatting + with: + path: uncrustify-formatting + exclude-dirs: ",,," + exclude-files: "badFile.c,cgc_error.h,main-blinky.c,test.c" + + - env: + stepName: "Functional | Success | Exclude File and Exclude Dirs" + name: ${{ env.stepName }} + id: exclude-single-dir-multiple-files + uses: ./uncrustify-formatting + with: + path: uncrustify-formatting + exclude-dirs: "goodFiles" + exclude-files: "cgc_error.h,main-blinky.c,test.c" + + - env: + stepName: "Functional | Success | Exclude Files and Exclude Dirs" + name: ${{ env.stepName }} + id: exclude-two-files-two-dirs + uses: ./uncrustify-formatting + with: + path: uncrustify-formatting + exclude-dirs: "filesWithFormattingErrors,filesWithTrailingWhitespace" + exclude-files: "test.c,cgc_error.h,main-blinky.c,badFile.c" + + uncrustify-formatting-error-cases: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 + - env: + stepName: "Functional | Failure | Whitespace, CRLF, and Format Failure" + name: ${{ env.stepName }} + id: all-format-errors + continue-on-error: true + uses: ./uncrustify-formatting with: - repository: FreeRTOS/coreMQTT - ref: main - path: coreMQTT - - name: Test formatting check action + path: uncrustify-formatting + + - env: + stepName: "Functional | Failure | CRLF and Formatting Error" + name: ${{ env.stepName }} + id: crlf-and-format-error + continue-on-error: true + uses: ./uncrustify-formatting + with: + path: uncrustify-formatting + exclude-dirs: "filesWithTrailingWhitespace" + + - env: + stepName: "Functional | Failure | CRLF and Whitespace Error" + name: ${{ env.stepName }} + id: crlf-and-whitespace-error + continue-on-error: true + uses: ./uncrustify-formatting + with: + path: uncrustify-formatting + exclude-dirs: "filesWithFormattingErrors" + + - env: + stepName: "Functional | Failure | CRLF Error" + name: ${{ env.stepName }} + id: crlf-error + continue-on-error: true + uses: ./uncrustify-formatting + with: + path: uncrustify-formatting + exclude-dirs: "filesWithFormattingErrors,filesWithTrailingWhitespace" + exclude-files: "badFile.c" + + - env: + stepName: "Functional | Failure | Formatting and Whitespace Errror" + name: ${{ env.stepName }} + id: formatting-and-whitespace-error + continue-on-error: true + uses: ./uncrustify-formatting + with: + path: uncrustify-formatting + exclude-dirs: "filesWithCRLFEndings" + + - env: + stepName: "Functional | Failure | Formatting Errror" + name: ${{ env.stepName }} + id: formatting-error + continue-on-error: true + uses: ./uncrustify-formatting + with: + path: uncrustify-formatting + exclude-dirs: "filesWithTrailingWhitespace,filesWithCRLFEndings" + + - env: + stepName: "Functional | Failure | Whitespace Errror" + name: ${{ env.stepName }} + id: whitespace-error + continue-on-error: true + uses: ./uncrustify-formatting + with: + path: uncrustify-formatting + exclude-dirs: "filesWithFormattingErrrors,filesWithCRLFEndings" + + - env: + stepName: "API | Failure | Exclude Dirs Error" + name: ${{ env.stepName }} + id: error-in-exclude-dirs + continue-on-error: true + uses: ./uncrustify-formatting + with: + path: uncrustify-formatting + exclude-dirs: "filesWithFormattingErrrors, filesWithCRLFEndings" + + - env: + stepName: "API | Failure | Exclude Files Error" + name: ${{ env.stepName }} + id: error-in-exclude-files + continue-on-error: true + uses: ./uncrustify-formatting + with: + path: uncrustify-formatting + exclude-files: "filesWithFormattingErrrors, filesWithCRLFEndings" + + - env: + stepName: "API | Failure | Exclude Option Errors" + name: ${{ env.stepName }} + id: error-in-both + continue-on-error: true + uses: ./uncrustify-formatting + with: + path: uncrustify-formatting + exclude-files: "filesWithFormattingErrrors, filesWithCRLFEndings" + exclude-dirs: "filesWithFormattingErrrors, + filesWithCRLFEndings" + + - env: + stepName: Check Failure Test Cases + name: ${{ env.stepName }} + id: check-failure-test-cases + shell: bash + run: | + # ${{ env.stepName }} + exitStatus=0 + if [ "${{ steps.all-format-errors.outcome}}" = "failure" ]; then + echo -e "${{ env.bashPass }} Functional | Failure | Whitespace, CRLF, and Format Failure | Had Expected "failure" ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} Functional | Failure | Whitespace, CRLF, and Format Failure | Had Unexpected "success" ${{ env.bashEnd }}" + exitStatus=1 + fi + if [ "${{ steps.crlf-and-format-error.outcome}}" = "failure" ]; then + echo -e "${{ env.bashPass }} Functional | Failure | CRLF and Formatting Error | Had Expected "failure" ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} Functional | Failure | CRLF and Formatting Error | Had Unexpected "success" ${{ env.bashEnd }}" + exitStatus=1 + fi + if [ "${{ steps.crlf-and-whitespace-error.outcome}}" = "failure" ]; then + echo -e "${{ env.bashPass }} Functional | Failure | CRLF and Whitespace Error | Had Expected "failure" ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} Functional | Failure | CRLF and Whitespace Error | Had Unexpected "success" ${{ env.bashEnd }}" + exitStatus=1 + fi + if [ "${{ steps.crlf-error.outcome}}" = "failure" ]; then + echo -e "${{ env.bashPass }} Functional | Failure | CRLF Error | Had Expected "failure" ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} Functional | Failure | CRLF Error | Had Unexpected "success" ${{ env.bashEnd }}" + exitStatus=1 + fi + if [ "${{ steps.formatting-and-whitespace-error.outcome}}" = "failure" ]; then + echo -e "${{ env.bashPass }} Functional | Failure | Formatting and Whitespace Errror | Had Expected "failure" ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} Functional | Failure | Formatting and Whitespace Errror | Had Unexpected "success" ${{ env.bashEnd }}" + exitStatus=1 + fi + if [ "${{ steps.formatting-error.outcome}}" = "failure" ]; then + echo -e "${{ env.bashPass }} Functional | Failure | Formatting Errror | Had Expected "failure" ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} Functional | Failure | Formatting Errror | Had Unexpected "success" ${{ env.bashEnd }}" + exitStatus=1 + fi + if [ "${{ steps.whitespace-error.outcome}}" = "failure" ]; then + echo -e "${{ env.bashPass }} Functional | Failure | Whitespace Errror | Had Expected "failure" ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} Functional | Failure | Whitespace Errror | Had Unexpected "success" ${{ env.bashEnd }}" + exitStatus=1 + fi + if [ "${{ steps.error-in-exclude-dirs.outcome}}" = "failure" ]; then + echo -e "${{ env.bashPass }} API | Failure | Exclude Dirs Error | Had Expected "failure" ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} API | Failure | Exclude Dirs Error | Had Unexpected "success" ${{ env.bashEnd }}" + exitStatus=1 + fi + if [ "${{ steps.error-in-exclude-files.outcome}}" = "failure" ]; then + echo -e "${{ env.bashPass }} API | Failure | Exclude Files Error | Had Expected "failure" ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} API | Failure | Exclude Files Error | Had Unexpected "success" ${{ env.bashEnd }}" + exitStatus=1 + fi + if [ "${{ steps.error-in-both.outcome}}" = "failure" ]; then + echo -e "${{ env.bashPass }} API | Failure | Exclude Option Errors | Had Expected "failure" ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} API | Failure | Exclude Option Errors | Had Unexpected "success" ${{ env.bashEnd }}" + exitStatus=1 + fi + exit $exitStatus + + clang-formatting-success-cases: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - env: + stepName: "Functional | Success | Exclude All Error Dirs" + name: ${{ env.stepName }} + id: exclude-dirs-with-errors uses: ./formatting with: - path: coreMQTT - exclude-files: lexicon.txt - exclude-dirs: build,docs + path: formatting + exclude-dirs: "filesWithFormattingErrors,filesWithCRLFEndings,filesWithTrailingWhitespace" + exclude-files: "badFile.c" + + - env: + stepName: "Functional | Success | Exclude Files" + name: ${{ env.stepName }} + id: exclude-files-with-errors + uses: ./formatting + with: + path: formatting + exclude-dirs: ",,," + exclude-files: "badFile.c,cgc_error.h,main-blinky.c,test.c" + + - env: + stepName: "Functional | Success | Exclude File and Exclude Dirs" + name: ${{ env.stepName }} + id: exclude-single-dir-multiple-files + uses: ./formatting + with: + path: formatting + exclude-dirs: "goodFiles" + exclude-files: "cgc_error.h,main-blinky.c,test.c" - test-executable-monitor: + - env: + stepName: "Functional | Success | Exclude Files and Exclude Dirs" + name: ${{ env.stepName }} + id: exclude-two-files-two-dirs + uses: ./formatting + with: + path: formatting + exclude-dirs: "filesWithFormattingErrors,filesWithTrailingWhitespace" + exclude-files: "test.c,cgc_error.h,main-blinky.c,badFile.c" + + clang-formatting-error-cases: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Compile executable monitor test - id: compile-executable-monitor-test + - env: + stepName: "Functional | Failure | Whitespace, CRLF, and Format Failure" + name: ${{ env.stepName }} + id: all-format-errors + continue-on-error: true + uses: ./formatting + with: + path: formatting + + - name: Reset Files + shell: bash + run: git reset --hard + + - env: + stepName: "Functional | Failure | CRLF and Formatting Error" + name: ${{ env.stepName }} + id: crlf-and-format-error + continue-on-error: true + uses: ./formatting + with: + path: formatting + exclude-dirs: "filesWithTrailingWhitespace" + + - name: Reset Files + shell: bash + run: git reset --hard + + - env: + stepName: "Functional | Failure | CRLF and Whitespace Error" + name: ${{ env.stepName }} + id: crlf-and-whitespace-error + continue-on-error: true + uses: ./formatting + with: + path: formatting + exclude-dirs: "filesWithFormattingErrors" + + - name: Reset Files + shell: bash + run: git reset --hard + + - env: + stepName: "Functional | Failure | CRLF Error" + name: ${{ env.stepName }} + id: crlf-error + continue-on-error: true + uses: ./formatting + with: + path: formatting + exclude-dirs: "filesWithFormattingErrors,filesWithTrailingWhitespace" + exclude-files: "badFile.c" + + - name: Reset Files + shell: bash + run: git reset --hard + + - env: + stepName: "Functional | Failure | Formatting and Whitespace Errror" + name: ${{ env.stepName }} + id: formatting-and-whitespace-error + continue-on-error: true + uses: ./formatting + with: + path: formatting + exclude-dirs: "filesWithCRLFEndings" + + - name: Reset Files + shell: bash + run: git reset --hard + + - env: + stepName: "Functional | Failure | Formatting Errror" + name: ${{ env.stepName }} + id: formatting-error + continue-on-error: true + uses: ./formatting + with: + path: formatting + exclude-dirs: "filesWithTrailingWhitespace,filesWithCRLFEndings" + + - name: Reset Files + shell: bash + run: git reset --hard + + - env: + stepName: "Functional | Failure | Whitespace Errror" + name: ${{ env.stepName }} + id: whitespace-error + continue-on-error: true + uses: ./formatting + with: + path: formatting + exclude-dirs: "filesWithFormattingErrrors,filesWithCRLFEndings" + + - name: Reset Files + shell: bash + run: git reset --hard + + - env: + stepName: "API | Failure | Exclude Dirs Error" + name: ${{ env.stepName }} + id: error-in-exclude-dirs + continue-on-error: true + uses: ./formatting + with: + path: formatting + exclude-dirs: "filesWithFormattingErrrors, filesWithCRLFEndings" + + - name: Reset Files + shell: bash + run: git reset --hard + + - env: + stepName: "API | Failure | Exclude Files Error" + name: ${{ env.stepName }} + id: error-in-exclude-files + continue-on-error: true + uses: ./formatting + with: + path: formatting + exclude-files: "filesWithFormattingErrrors, filesWithCRLFEndings" + + - name: Reset Files shell: bash + run: git reset --hard + + - env: + stepName: "API | Failure | Exclude Option Errors" + name: ${{ env.stepName }} + id: error-in-both + continue-on-error: true + uses: ./formatting + with: + path: formatting + exclude-files: "filesWithFormattingErrrors, filesWithCRLFEndings" + exclude-dirs: "filesWithFormattingErrrors, + filesWithCRLFEndings" + + - name: Reset Files + shell: bash + run: git reset --hard + + - env: + stepName: Check Failure Test Cases + name: ${{ env.stepName }} + id: check-failure-test-cases + shell: bash + run: | + # ${{ env.stepName }} + exitStatus=0 + if [ "${{ steps.all-format-errors.outcome}}" = "failure" ]; then + echo -e "${{ env.bashPass }} Functional | Failure | Whitespace, CRLF, and Format Failure | Had Expected "failure" ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} Functional | Failure | Whitespace, CRLF, and Format Failure | Had Unexpected "success" ${{ env.bashEnd }}" + exitStatus=1 + fi + if [ "${{ steps.crlf-and-format-error.outcome}}" = "failure" ]; then + echo -e "${{ env.bashPass }} Functional | Failure | CRLF and Formatting Error | Had Expected "failure" ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} Functional | Failure | CRLF and Formatting Error | Had Unexpected "success" ${{ env.bashEnd }}" + exitStatus=1 + fi + if [ "${{ steps.crlf-and-whitespace-error.outcome}}" = "failure" ]; then + echo -e "${{ env.bashPass }} Functional | Failure | CRLF and Whitespace Error | Had Expected "failure" ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} Functional | Failure | CRLF and Whitespace Error | Had Unexpected "success" ${{ env.bashEnd }}" + exitStatus=1 + fi + if [ "${{ steps.crlf-error.outcome}}" = "failure" ]; then + echo -e "${{ env.bashPass }} Functional | Failure | CRLF Error | Had Expected "failure" ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} Functional | Failure | CRLF Error | Had Unexpected "success" ${{ env.bashEnd }}" + exitStatus=1 + fi + if [ "${{ steps.formatting-and-whitespace-error.outcome}}" = "failure" ]; then + echo -e "${{ env.bashPass }} Functional | Failure | Formatting and Whitespace Errror | Had Expected "failure" ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} Functional | Failure | Formatting and Whitespace Errror | Had Unexpected "success" ${{ env.bashEnd }}" + exitStatus=1 + fi + if [ "${{ steps.formatting-error.outcome}}" = "failure" ]; then + echo -e "${{ env.bashPass }} Functional | Failure | Formatting Errror | Had Expected "failure" ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} Functional | Failure | Formatting Errror | Had Unexpected "success" ${{ env.bashEnd }}" + exitStatus=1 + fi + if [ "${{ steps.whitespace-error.outcome}}" = "failure" ]; then + echo -e "${{ env.bashPass }} Functional | Failure | Whitespace Errror | Had Expected "failure" ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} Functional | Failure | Whitespace Errror | Had Unexpected "success" ${{ env.bashEnd }}" + exitStatus=1 + fi + if [ "${{ steps.error-in-exclude-dirs.outcome}}" = "failure" ]; then + echo -e "${{ env.bashPass }} API | Failure | Exclude Dirs Error | Had Expected "failure" ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} API | Failure | Exclude Dirs Error | Had Unexpected "success" ${{ env.bashEnd }}" + exitStatus=1 + fi + if [ "${{ steps.error-in-exclude-files.outcome}}" = "failure" ]; then + echo -e "${{ env.bashPass }} API | Failure | Exclude Files Error | Had Expected "failure" ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} API | Failure | Exclude Files Error | Had Unexpected "success" ${{ env.bashEnd }}" + exitStatus=1 + fi + if [ "${{ steps.error-in-both.outcome}}" = "failure" ]; then + echo -e "${{ env.bashPass }} API | Failure | Exclude Option Errors | Had Expected "failure" ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} API | Failure | Exclude Option Errors | Had Unexpected "success" ${{ env.bashEnd }}" + exitStatus=1 + fi + exit $exitStatus + + test-exe-monitor-success-cases: + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + 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 + + - env: + stepName: Install Ubuntu Build Tools + name: ${{ env.stepName }} + if: runner.os == 'Linux' + id: install-ubuntu-build--tools run: | - # Compile executable monitor test - echo "::group::Compile executable monitor test" + # ${{ env.stepName }} + echo "::group::${{ env.stepName }}" sudo apt install build-essential - if [ "$?" = "0" ]; then - echo -e "\033[32;3mInstalled build-essential\033[0m" + exitStatus=$? + echo "::endgroup::" + if [ $exitStatus -eq 0 ]; then + echo -e "${{ env.bashPass }} ${{ env.stepName }} ${{ env.bashEnd }}" else - echo "::endgroup::" - echo -e "\033[32;31mInstall build-essential failed...\033[0m" + echo -e "${{ env.bashFail }} ${{ env.stepName }} ${{ env.bashEnd }}" exit 1 fi + + - env: + stepName: Compile Executable Monitor Test Files + name: ${{ env.stepName }} + id: compile-executable-monitor-test-files + shell: bash + run: | + # ${{ env.stepName }} + echo "::group::${{ env.stepName }}" gcc executable-monitor/test.c -o executable-monitor/test.out gcc -DEXIT_WITH_MINUTES executable-monitor/test.c -o executable-monitor/test_exit_current_minutes.out + readlink -f executable-monitor/test.out + readlink -f executable-monitor/test_exit_current_minutes.out + exitStatus=$? echo "::endgroup::" - if [ "$?" = "0" ]; then - readlink -f executable-monitor/test.out - echo -e "\033[32;3mCompiled executable-monitor/test.c\033[0m" + if [ $exitStatus -eq 0 ]; then + echo -e "${{ env.bashPass }} ${{ env.stepName }} ${{ env.bashEnd }}" else - echo -e "\033[32;31mCompilation of executable-monitor/test.c failed...\033[0m" + echo -e "${{ env.bashFail }} ${{ env.stepName }} ${{ env.bashEnd }}" exit 1 fi + # Get future times from now, then look for that in the executable + - env: + stepName: Get Future Times + name: ${{ env.stepName }} + id: future-time + shell: bash + run: | + # {{ env.stepName }} + set -x + echo -e "${{ env.bashInfo }} Getting Future Times to Compare Against ${{ env.bashEnd }}" + echo "exitCodeTwoMinutes=$(date --date='2 minutes' +%M)" >> "$GITHUB_ENV" + echo "successLineThreeMinutes=$(date --date='3 minutes' +%H:%M)" >> "$GITHUB_ENV" + echo "successLineFiveMinutes=$(date --date='5 minutes' +%H:%M)" >> "$GITHUB_ENV" + echo "exitCodeFourMinutes=$(date --date='4 minutes' +%M)" >> "$GITHUB_ENV" - - name: Functional Test | Success Case | Retries, Success Line, and Exit Code | Pass on Success Line - id: test-executable-monitor-action-success-line - uses: ./executable-monitor - with: - exe-path: executable-monitor/test.out - timeout-seconds: 20 - success-line: "SLEEPING FOR 6 SECONDS" - success-exit-code: 0 - retry-attempts: 2 - - - name: Functional Test | Success Case | Retries, Success Line, and Exit Code | Pass on Exit Code - id: test-executable-monitor-action-exit-code - uses: ./executable-monitor - with: - exe-path: executable-monitor/test.out - timeout-seconds: 75 - success-line: "LINE_THAT_WILL_NOT_PRINT_BUT_EXISTS" - success-exit-code: 0 - retry-attempts: 2 - - - name: Functional Test | Success Case | Retries, Success Line, and Exit Code, With log file - id: test-executable-monitor-action-log-file + - env: + stepName: "Functional | Exit Code | Exit Code Found" + name: ${{ env.stepName }} + id: exe-monitor-exit-code uses: ./executable-monitor with: - log-dir: demo_run_logs exe-path: executable-monitor/test.out - timeout-seconds: 20 - success-line: "SLEEPING FOR 6 SECONDS" success-exit-code: 0 - retry-attempts: 2 + log-dir: logDirectory + timeout-seconds: 60 - - name: Functional Test | Success Case | Retries, Success Line, No Exit Code - id: test-executable-monitor-action-no-exit-code + - env: + stepName: "Functional | Success Line | Success Line Found" + name: ${{ env.stepName }} + id: exe-monitor-success-line uses: ./executable-monitor with: exe-path: executable-monitor/test.out - timeout-seconds: 20 success-line: "SLEEPING FOR 6 SECONDS" - retry-attempts: 2 + log-dir: logDirectory + timeout-seconds: 30 - - name: Functional Test | Success Case | Retries, No Success Line, Exit Code - id: test-executable-monitor-action-no-success-line + - env: + stepName: "Functional | Exit Code and Success Line | Exit Code Found" + name: ${{ env.stepName }} + id: exe-monitor-find-exit-code uses: ./executable-monitor with: exe-path: executable-monitor/test.out - timeout-seconds: 75 + success-line: "LINE_THAT_WILL_NOT_PRINT" success-exit-code: 0 - retry-attempts: 2 + timeout-seconds: 60 - - name: Functional Test | Success Case | No Retries, Success Line, No Exit Code - id: test-executable-monitor-action-no-retry-attempts-success-line + - env: + stepName: "Functional | Exit Code and Success Line | Success Line Found" + name: ${{ env.stepName }} + id: exe-monitor-find-success-line uses: ./executable-monitor with: exe-path: executable-monitor/test.out success-line: "SLEEPING FOR 6 SECONDS" - timeout-seconds: 20 - - - name: Functional Test | Success Case | No Retries, No Success Line, Exit Code - id: test-executable-monitor-action-no-retry-attempts-exit-code - uses: ./executable-monitor - with: - exe-path: executable-monitor/test.out success-exit-code: 0 - timeout-seconds: 75 + timeout-seconds: 30 - - name: API Test | Success Case | Retries, Success Line, No Exit Code, Use Default Timeout - id: test-executable-monitor-action-no-exit-code-no-timeout - uses: ./executable-monitor - with: - exe-path: executable-monitor/test.out - success-line: "SLEEPING FOR 6 SECONDS" - retry-attempts: 2 - - - name: API Test | Success Case | Retries, No Success Line, Exit Code, Use Default Timeout - id: test-executable-monitor-action-no-success-line-no-timeout + - env: + stepName: "Functional | Retry Needed | Exit Code Found" + name: ${{ env.stepName }} + id: exe-monitor-retry-find-exit-code uses: ./executable-monitor with: - exe-path: executable-monitor/test.out - success-exit-code: 0 - retry-attempts: 2 - - # Get future times from now, then look for that in the executable - - name: Get Future Times - id: future-time - run: | - # Get times for future demos - echo "time_success_line=$(date --date='2 minutes' +%H:%M)" >> "$GITHUB_ENV" - echo "time_exit_code=$(date --date='5 minutes' +%M)" >> "$GITHUB_ENV" + exe-path: executable-monitor/test_exit_current_minutes.out + success-exit-code: ${{ env.exitCodeTwoMinutes }} + retry-attempts: 10 + timeout-seconds: 60 - - name: Functional Test | Sucess Case | Test Retry Run occurs on Wrong exit code - id: test-executable-monitor-action-retry-success-line + - env: + stepName: "Functional | Retry Needed | Success Line Found" + name: ${{ env.stepName }} + id: exe-monitor-retry-find-success-line uses: ./executable-monitor with: - exe-path: executable-monitor/test.out - timeout-seconds: 20 + exe-path: executable-monitor/test_exit_current_minutes.out + success-line: ${{ env.successLineThreeMinutes }} retry-attempts: 10 - success-line: ${{ env.time_success_line }} + timeout-seconds: 60 - - name: Functional Test | Success Case | Retry On Wrong Exit Code - id: test-executable-monitor-action-retry-exit-code + - env: + stepName: "Functional | Retry Needed | Exit Code and Success Line | Exit Code Found" + name: ${{ env.stepName }} + id: exe-monitor-retry-both-inputs-find-exit uses: ./executable-monitor with: exe-path: executable-monitor/test_exit_current_minutes.out - success-exit-code: ${{ env.time_exit_code }} - timeout-seconds: 60 + success-line: "LINE_THAT_WILL_NOT_PRINT" + success-exit-code: ${{ env.exitCodeFourMinutes }} retry-attempts: 10 + timeout-seconds: 60 - - name: Functional Test | Failure Case | Fail On Timeout - id: test-executable-monitor-action-fail-on-timeout - continue-on-error: true + - env: + stepName: "Functional | Retry Needed | Exit Code and Success Line | Success Line Found" + name: ${{ env.stepName }} + id: exe-monitor-retry-both-inputs-find-success-line uses: ./executable-monitor with: + # Use the EXE that doesn't exit with the current minutes exe-path: executable-monitor/test.out - timeout-seconds: 1 - success-line: "SLEEPING FOR 12 SECONDS" - success-exit-code: 0 - retry-attempts: 2 + success-line: ${{ env.successLineFiveMinutes }} + success-exit-code: 1 + retry-attempts: 10 + timeout-seconds: 60 - - name: API Test | Failure Case | Retries, No Success Metric - continue-on-error: true - id: test-executable-monitor-API-no-success-metric - uses: ./executable-monitor - with: - exe-path: executable-monitor/test.out - timeout-seconds: 20 - retry-attempts: 2 + test-exe-monitor-failure-cases: + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] - - name: API Test | Failure Case | No Exe Path - continue-on-error: true - id: test-executable-monitor-API-no-exe-path - uses: ./executable-monitor - with: - timeout-seconds: 20 - success-line: "SLEEPING FOR 6 SECONDS" - success-exit-code: 0 - 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 - - name: Check Failure Test Cases - id: check-failure-test-cases - run: | - # Check Last Step Failed - echo "::group::Check Failure Test Cases" - if [ "${{ steps.test-executable-monitor-action-fail-on-timeout.outcome}}" = "failure" ]; then - echo -e "\033[32;3mFunctional Test | Failure Case | Fail On Timeout had outcome '${{ steps.test-executable-monitor-action-fail-on-timeout.outcome}}' as intended\033[0m" - else - echo "::endgroup::" - echo -e "\033[32;31mFunctional Test | Failure Case | Fail On Timeout had unexpected '${{ steps.test-executable-monitor-action-fail-on-timeout.outcome}}' exit condition\033[0m" - exit 1 - fi - if [ "${{ steps.test-executable-monitor-API-no-success-metric.outcome}}" = "failure" ]; then - echo -e "\033[32;3mAPI Test | Failure Case | Retries, No Success Metric had outcome '${{ steps.test-executable-monitor-API-no-success-metric.outcome}}' as intended\033[0m" - else - echo "::endgroup::" - echo -e "\033[32;31mAPI Test | Failure Case | Retries, No Success Metric had unexpected '${{ steps.test-executable-monitor-API-no-success-metric.outcome}}' exit condition\033[0m" - exit 1 - fi - if [ "${{ steps.test-executable-monitor-API-no-exe-path.outcome}}" = "failure" ]; then - echo -e "\033[32;3mAPI Test | Failure Case | No Exe Path had outcome '${{ steps.test-executable-monitor-API-no-exe-path.outcome}}' as intended\033[0m" - else - echo "::endgroup::" - echo -e "\033[32;31mAPI Test | Failure Case | No Exe Path had unexpected '${{ steps.test-executable-monitor-API-no-exe-path.outcome}}' exit condition\033[0m" - exit 1 - fi - echo "::endgroup::" - echo -e "All failure tests failed correctly" + - env: + stepName: Install Ubuntu Build Tools + name: ${{ env.stepName }} + if: runner.os == 'Linux' + id: install-ubuntu-build--tools + run: | + # Install Ubuntu Build Tools + echo "::group::${{ env.stepName }}" + sudo apt install build-essential + exitStatus=$? + echo "::endgroup::" + if [ $exitStatus -eq 0 ]; then + echo -e "${{ env.bashPass }} ${{ env.stepName }} ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} ${{ env.stepName }} ${{ env.bashEnd }}" + exit 1 + fi + + - env: + stepName: Compile Executable Monitor Test Files + name: ${{ env.stepName }} + id: compile-executable-monitor-test-files + shell: bash + run: | + # ${{ env.stepName }} + echo "::group::${{ env.stepName }}" + gcc executable-monitor/test.c -o executable-monitor/test.out + gcc -DEXIT_WITH_MINUTES executable-monitor/test.c -o executable-monitor/test_exit_current_minutes.out + readlink -f executable-monitor/test.out + readlink -f executable-monitor/test_exit_current_minutes.out + exitStatus=$? + echo "::endgroup::" + if [ $exitStatus -eq 0 ]; then + echo -e "${{ env.bashPass }} ${{ env.stepName }} ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} ${{ env.stepName }} ${{ env.bashEnd }}" + exit 1 + fi + + - env: + stepName: "API | Failure | No Executable Provided" + id: exe-monitor-fail-no-exe + uses: ./executable-monitor + continue-on-error: true + with: + timeout-seconds: 30 + + - env: + stepName: "API | Failure | No Success Condition Provided" + name: ${{ env.stepName }} + id: exe-monitor-fail-no-success-condition + uses: ./executable-monitor + continue-on-error: true + with: + exe-path: executable-monitor/test.out + log-dir: logDirectory + timeout-seconds: 30 + + - env: + stepName: "Functional | Failure | Timeout Cause No Success Line To Print" + name: ${{ env.stepName }} + id: exe-monitor-fail-timeout-no-success-line + uses: ./executable-monitor + continue-on-error: true + with: + exe-path: executable-monitor/test.out + # This is a line that would print if not for timeout + success-line: "SLEEPING FOR 9 SECONDS" + timeout-seconds: 2 + + - env: + stepName: "Functional | Failure | Timeout Cause No Exit Code" + name: ${{ env.stepName }} + id: exe-monitor-fail-timeout-no-exit-code + uses: ./executable-monitor + continue-on-error: true + with: + exe-path: executable-monitor/test.out + # This is an exit status that should be met if not for timeou + success-exit-code: 0 + timeout-seconds: 2 + + - env: + stepName: "Functional | Failure | Timeout Cause Neither Condition" + name: ${{ env.stepName }} + id: exe-monitor-fail-timeout-no-condition + uses: ./executable-monitor + continue-on-error: true + with: + exe-path: executable-monitor/test.out + # These are exit conditions that should be met if not for timeout + success-line: "SLEEPING FOR 9 SECONDS" + success-exit-code: 0 + timeout-seconds: 2 + + - env: + stepName: "Functional | Failure | Retries Timeout Cause No Success Line To Print" + name: ${{ env.stepName }} + id: exe-monitor-fail-retries-timeout-no-success-line + uses: ./executable-monitor + continue-on-error: true + with: + exe-path: executable-monitor/test.out + # This is a line that would print if not for timeout + success-line: "SLEEPING FOR 9 SECONDS" + timeout-seconds: 2 + retry-attempts: 2 + + - env: + stepName: "Functional | Failure | Retries Timeout Cause No Exit Code" + name: ${{ env.stepName }} + id: exe-monitor-fail-retries-no-exit-code + uses: ./executable-monitor + continue-on-error: true + with: + exe-path: executable-monitor/test.out + # This is an exit status that should be met if not for timeout + success-exit-code: 0 + timeout-seconds: 2 + retry-attempts: 2 + + - env: + stepName: "Functional | Failure | Retries Timeout Cause Neither Condition " + name: ${{ env.stepName }} + id: exe-monitor-fail-retries-no-success-condition + uses: ./executable-monitor + continue-on-error: true + with: + exe-path: executable-monitor/test.out + # These are exit conditions that should be met if not for timeout + success-line: "SLEEPING FOR 9 SECONDS" + success-exit-code: 0 + timeout-seconds: 2 + retry-attempts: 2 + + - env: + stepName: Check Failure Test Cases + name: ${{ env.stepName }} + id: check-failure-test-cases + shell: bash + run: | + # ${{ env.stepName }} + if [ "${{ steps.exe-monitor-fail-no-exe.outcome}}" = "failure" ]; then + echo -e "${{ env.bashPass }} | No Executable Provided | Failed As Intended ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} | No Executable Provided | Had Unexpected Pass ${{ env.bashEnd }}" + exit 1 + fi + + if [ "${{ steps.exe-monitor-fail-no-success-condition.outcome}}" = "failure" ]; then + echo -e "${{ env.bashPass }} | No Success Condition Provided | Failed As Intended ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} | No Success Condition Provided | Had Unexpected Pass ${{ env.bashEnd }}" + exit 1 + fi + + if [ "${{ steps.exe-monitor-fail-timeout-no-success-line.outcome}}" = "failure" ]; then + echo -e "${{ env.bashPass }} | Timeout Cause No Success Line To Print | Failed As Intended ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} | Timeout Cause No Success Line To Print | Had Unexpected Pass ${{ env.bashEnd }}" + exit 1 + fi + + if [ "${{ steps.exe-monitor-fail-timeout-no-exit-code.outcome}}" = "failure" ]; then + echo -e "${{ env.bashPass }} | Timeout Cause No Exit Code | Failed As Intended ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} | Timeout Cause No Exit Code | Had Unexpected Pass ${{ env.bashEnd }}" + exit 1 + fi + + if [ "${{ steps.exe-monitor-fail-timeout-no-condition.outcome}}" = "failure" ]; then + echo -e "${{ env.bashPass }} | Timeout Cause Neither Condition | Failed As Intended ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} | Timeout Cause Neither Condition | Had Unexpected Pass ${{ env.bashEnd }}" + exit 1 + fi + + if [ "${{ steps.exe-monitor-fail-retries-timeout-no-success-line.outcome}}" = "failure" ]; then + echo -e "${{ env.bashPass }} | Retries Timeout Cause No Success Line To Print | Failed As Intended ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} | Retries Timeout Cause No Success Line To Print | Had Unexpected Pass ${{ env.bashEnd }}" + exit 1 + fi + + if [ "${{ steps.exe-monitor-fail-retries-no-exit-code.outcome}}" = "failure" ]; then + echo -e "${{ env.bashPass }} | Retries Timeout Cause No Exit Code | Failed As Intended ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} | Retries Timeout Cause No Exit Code | Had Unexpected Pass ${{ env.bashEnd }}" + exit 1 + fi + + if [ "${{ steps.exe-monitor-fail-retries-no-success-condition.outcome}}" = "failure" ]; then + echo -e "${{ env.bashPass }} | Retries Timeout Cause Neither Condition | Failed As Intended ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} | Retries Timeout Cause Neither Condition | Had Unexpected Pass ${{ env.bashEnd }}" + exit 1 + fi test-complexity-check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 + - uses: actions/checkout@v3 with: repository: FreeRTOS/coreMQTT ref: main @@ -234,14 +1083,15 @@ jobs: path: coreMQTT # For coreMQTT the code complexity threshold is 10. horrid_threshold: 10 + test-doxygen-zip-check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 with: python-version: '3.11.0' - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: repository: aws/aws-iot-device-sdk-embedded-C submodules: recursive @@ -253,11 +1103,12 @@ jobs: path: ./aws-iot-device-sdk-embedded-C libs_parent_dir_path: libraries/standard,libraries/aws generate_zip: true + test-doxygen-non-zip-check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 + - uses: actions/checkout@v3 with: repository: FreeRTOS/coreMQTT ref: main @@ -266,24 +1117,102 @@ jobs: uses: ./doxygen with: path: coreMQTT + test-spell-check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 + + - uses: actions/checkout@v3 with: - repository: FreeRTOS/coreMQTT + repository: skptak/coreMQTT ref: main path: coreMQTT + - name: Test spell check action uses: ./spellings with: path: coreMQTT + + test-rust-spell-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - uses: actions/checkout@v3 + with: + repository: skptak/coreMQTT + ref: main + path: coreMQTT + + - name: Test spell check action + uses: ./rust-spell-check + with: + path: coreMQTT + + test-rust-spell-checker-needs-to-build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - uses: actions/checkout@v3 + with: + repository: skptak/coreMQTT + ref: main + path: coreMQTT + + - name: Delete the pre-built spell-checker + shell: bash + run: file=$(find . -name spell-checker); readlink -f "$file" ; rm "$file" + + - name: Test Spell Check Build and Run + uses: ./rust-spell-check + with: + path: coreMQTT + + test-rust-spell-checker-find-mistake: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - uses: actions/checkout@v3 + with: + repository: skptak/coreMQTT + ref: main + path: coreMQTT + + - name: Empty the lexicon + shell: bash + working-directory: coreMQTT + run: file=$(find . -name .cSpellWords.txt); readlink -f "$file" ; > "$file" + + - name: Test Spell Check Fails on Misspelled Word + id: test-spell-check-find-mistake + continue-on-error: true + uses: ./rust-spell-check + with: + path: coreMQTT + + - env: + stepName: Check Failure Test Case + name: ${{ env.stepName }} + id: check-failure-test-cases + shell: bash + run: | + # ${{ env.stepName }} + exitStatus=0 + if [ "${{ steps.test-spell-check-find-mistake.outcome}}" = "failure" ]; then + echo -e "${{ env.bashPass }} Functional | Failure | Fail on Misspelled Word | Had Expected "failure" ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} Functional | Failure | Fail on Misspelled Word | Had Unexpected "success" ${{ env.bashEnd }}" + exitStatus=1 + fi + test-coverage-cop: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 + - uses: actions/checkout@v3 with: repository: FreeRTOS/coreMQTT ref: main @@ -314,10 +1243,11 @@ jobs: path: ./build/coverage.info branch-coverage-min: 70 line-coverage-min: 100 + test-memory-statistics: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Test memory statistics action uses: ./memory_statistics with: @@ -325,12 +1255,13 @@ jobs: config: ./memory_statistics_config.json output: ./size_table_new.html check_against: ./size_table_expected.html + test-link-verifier: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Setup python environment - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: '3.11.0' - name: Test link verifier action @@ -339,25 +1270,25 @@ jobs: path: ./ exclude-dirs: complexity,formatting include-file-types: .c,.html + test-manifest-verifier: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Setup python environment - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: '3.x' - name: Checkout the FreeRTOS/FreeRTOS repository for testing action on. - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: FreeRTOS/FreeRTOS ref: '202107.00' path: FreeRTOS - submodules: recursive + submodules: recursive - name: Test manifest verifier uses: ./manifest-verifier with: path: ./FreeRTOS exclude-submodules: FreeRTOS-Plus/Test/CMock,FreeRTOS/Test/CMock/CMock,FreeRTOS/Test/litani fail-on-incorrect-version: true - diff --git a/.gitignore b/.gitignore index 08e6ed60..8dd38c4e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ *.out logDir/ demo_run_logs/ +rust-spell-check/target/ +rust-spell-check/debug/ +# Ignore any helper scripts people are writing while working +*.sh \ No newline at end of file diff --git a/README.md b/README.md index 11ed14cb..e2705b42 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ FreeRTOS libraries. * **Complexity** - Uses [GNU Complexity](https://www.gnu.org/software/complexity/manual/complexity.html) to verify that the complexity score of library functions is less than 9. * **Formatting** - Validates all C files of a FreeRTOS library repository comply to the formatting - standard defined in [uncrustify.cfg](formatting/uncrustify.cfg). + standard defined in [clang-format](formatting/.clang-format). * **Doxygen** - Validates that the doxygen manual of the FreeRTOS library can be built without warnings. * **Spellings** - Checks spellings across all files of the FreeRTOS library repository. Each diff --git a/complexity/action.yml b/complexity/action.yml index 1721df94..94b5ba47 100644 --- a/complexity/action.yml +++ b/complexity/action.yml @@ -12,10 +12,10 @@ inputs: horrid_threshold: description: 'The horrid threshold parameter passed to the invocation of complexity' required: false - default: 8 + default: 18 runs: using: "composite" - steps: + steps: - name: Setup run: sudo apt-get install complexity shell: bash diff --git a/coverage-cop/action.yml b/coverage-cop/action.yml index 1cdb584a..95ceec55 100644 --- a/coverage-cop/action.yml +++ b/coverage-cop/action.yml @@ -14,7 +14,7 @@ inputs: default: 100 runs: using: "composite" - steps: + steps: - name: Print coverage data run: lcov --list --rc lcov_branch_coverage=1 ${{ inputs.path }} shell: bash diff --git a/doxygen-generation/action.yml b/doxygen-generation/action.yml index 2d080043..418e6c55 100644 --- a/doxygen-generation/action.yml +++ b/doxygen-generation/action.yml @@ -32,7 +32,7 @@ runs: using: "composite" steps: - name: Checkout the repo for generating doxygen - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: path: doxygen_source ref: ${{ inputs.ref }} @@ -40,7 +40,7 @@ runs: - name: Install Doxygen shell: bash run: | - wget -O doxygen.tgz "${{ inputs.doxygen_link }}" + wget -O doxygen.tgz "${{ inputs.doxygen_link }}" if [ $? -ne 0 ]; then exit 1; fi sudo tar --strip-components=1 -xzf doxygen.tgz -C /usr/local sudo apt-get install -y ${{ inputs.doxygen_dependencies }} @@ -51,7 +51,7 @@ runs: run: ${{ inputs.generate_command }} - name: Checkout the repo for storing doxygen - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: path: doxygen_store diff --git a/doxygen/action.yml b/doxygen/action.yml index 8bf98b8a..d29ce214 100644 --- a/doxygen/action.yml +++ b/doxygen/action.yml @@ -21,7 +21,7 @@ inputs: description: 'Flag to indicate whether a ZIP output should be created.' required: false default: 'false' - + runs: using: "composite" steps: diff --git a/executable-monitor/README.md b/executable-monitor/README.md index 81ee26ad..c68c8bce 100644 --- a/executable-monitor/README.md +++ b/executable-monitor/README.md @@ -1,8 +1,30 @@ -Test the case where exit status code and success line are given but hit timeout -python3 executable-monitor.py --log-dir . --success-exit-status 0 --success-line "Sleeping for 27 seconds" --retry-attempts 2 --timeout-seconds 10 --exe-path test.out ; echo $? +# Executable Monitor Summary: +This is a python program designed to run an executable and monitor it. +It can either look for a string being printed to the program's standard out, or check for an exit condition. +It supports retry logic, as well as deadlocking programs. +The tests for the executable monitor mostly live inside of the CI-CD-Github-Actions/.github/workflows/test.yml file. +A few tests have been added below for ease of copying and pasting it though. +These tests are meant to be run using the test.c file that lives inside of this directory. +test.c prints out the system time and then sleeps for a set duration +# Provided Test.c: + test.c is a simple C program that prints the current minutes, hours, and seconds. + As well as how long the program is going to sleep for. + These values can then be used as a test for the executable-monitor. +```gcc test.c -o test.out``` -# Run a test where we find the success line but the exe does not exit, ensure exit status is 0 -#python3 executable-monitor.py --log-dir . --success-line "Sleeping for 27 seconds" --retry-attempts 2 --timeout-seconds 10 --exe-path test.out; echo $? +Test.c can also be set to exit with the current time in minutes by using the command. +By setting the exit code or success line to be a time in the future a test can be written that requires retry logic. +By setting the success-line to be a time in the future the program will need to "retry" until this time occurs +By setting the exit code of the program to be the current minute, the program will need to "retry" until this exit code is seen. +```gcc test.c -DEXIT_WITH_MINUTES -o test.out``` -Test a run where the thread will timeout while waiting for the success message, ensure exit status is 1 -# python3 executable-monitor.py --log-dir . --success-line "Sleeping for 27 seconds" --retry-attempts 2 --timeout-seconds 2 --exe-path test.out; echo $? \ No newline at end of file + +# How to call the program locally with some basic tests. + Success Test | Test the case where the success line is found but the exe does not exit, ensure exit status is 0 +```python3 executable-monitor.py --success-line "SLEEPING FOR 6 SECONDS" --retry-attempts 2 --timeout-seconds 10 --exe-path test.out; echo $?``` + +Success Test | Test the case where the success line is not found, but the exe exits, ensure exit status is 0 +```python3 executable-monitor.py --success-line "THIS WILL NEVER PRINT" --success-exit-code 0 --retry-attempts 2 --timeout-seconds 60 --exe-path test.out; echo $?``` + +Failure Test | Test the case where exit status code and success line are given but hit timeout, ensure exit status is 1 +```python3 executable-monitor.py --success-exit-code 0 --success-line "SLEEPING FOR 12 SECONDS" --retry-attempts 2 --timeout-seconds 10 --exe-path test.out ; echo $?``` diff --git a/executable-monitor/action.yml b/executable-monitor/action.yml index 59cedba4..df4cd2a7 100644 --- a/executable-monitor/action.yml +++ b/executable-monitor/action.yml @@ -13,9 +13,9 @@ inputs: required: false default: "" timeout-seconds: - description: 'Maximum amount of time to run the executable. Default is 600.' + description: 'Maximum amount of time to run the executable. Default is 180.' required: false - default: 300 + default: 180 retry-attempts: description: 'Number of times to re-launch the binary to check for success.' required: false @@ -27,22 +27,33 @@ inputs: runs: using: "composite" - steps: - - name: Install Dependencies + steps: + - env: + # The bash escape character is \033 + # At time of writing, you can't add a global environment to an + # action so stuck with this. If this gets changed please move this + bashPass: \033[32;1mPASSED - + bashInfo: \033[33;1mINFO - + bashFail: \033[31;1mFAILED - + bashEnd: \033[0m + stepName: Install Dependencies + name: ${{ env.stepName }} + shell: bash run: | - # Install Dependencies + # ${{ env.stepName }} echo "::group::Install Dependencies" pip install -r $GITHUB_ACTION_PATH/requirements.txt + exitStatus=$? echo "::endgroup::" - if [ "$?" = "0" ]; then - echo -e "\033[32;3mInstalled all dependencies\033[0m" - exit 0 - else - echo -e "\033[32;31mDependencies Install failed...\033[0m" - exit 1 - fi + echo -e "${{ env.bashPass }} ${{ env.stepName }} ${{ env.bashEnd }}" + + - env: + bashPass: \033[32;1mPASSED - + bashInfo: \033[33;1mINFO - + bashFail: \033[31;1mFAILED - + bashEnd: \033[0m + stepName: ${{ env.stepName }} shell: bash - - name: Run Executable with Monitoring run: | # Run Executable with Monitoring echo "::group::Argument Parsing" @@ -50,55 +61,57 @@ runs: # Make sure we have an exit condition to look for if [ "${{ inputs.success-exit-code }}" = "" ] && [ "${{ inputs.success-line }}" = "" ]; then echo "::endgroup::" - echo -e "\033[32;31mDid not supply an input of success-line or success-exit-code to search for\033[0m" + echo -e "${{ env.bashFail}}Did not supply an input of success-line or success-exit-code to search for ${{ env.bashEnd }}" exit 1 fi + echo "Adding --exe-path=${{ inputs.exe-path }}" + echo "Adding --timeout-seconds=${{ inputs.timeout-seconds }}" + # Check if an exit code was provided if [ "${{ inputs.success-exit-code }}" != "" ]; then optArgs="--success-exit-code=${{ inputs.success-exit-code }} $optArgs" - echo "Adding --success-exit-code=${{ inputs.success-exit-code }} to the call" + echo "Adding --success-exit-code=${{ inputs.success-exit-code }}" fi # Check for log directory/if a log file should be created if [ "${{ inputs.log-dir }}" != "" ]; then optArgs="--log-dir=${{ inputs.log-dir}} $optArgs" - echo "Adding --log-dir=${{ inputs.log-dir}} to the call" + echo "Adding --log-dir=${{ inputs.log-dir}}" fi # Check for retry attempts if [ "${{ inputs.retry-attempts }}" != "" ]; then optArgs="--retry-attempts=${{ inputs.retry-attempts }} $optArgs" - echo "Adding --retry-attempts=${{ inputs.retry-attempts }} to the call" + echo "Adding --retry-attempts=${{ inputs.retry-attempts }}" fi + # set +e so if the run fails we can capture that and print custom error message + set +e + # Check if a success line was provided. Converting the github input to a bash variable # Will cause it to tokenize on spaces when passed into python # So just only add the success line if it exits. - # set +e so if the run fails we can capture that and print custom error message - set +e if [ "${{ inputs.success-line }}" != "" ]; then successLineArgs="--success-line=\"${{ inputs.success-line}}\" $optArgs" - echo "Adding --success-line=${{ inputs.success-line}} to the call" + echo "Adding --success-line=${{ inputs.success-line}}" echo "::endgroup::" - echo -e "\033[1;33mRunning Command: python3 $GITHUB_ACTION_PATH/executable-monitor.py --exe-path=${{ inputs.exe-path }} --timeout-seconds=${{ inputs.timeout-seconds }} --success-line="${{ inputs.success-line }}" $optArgs \033[0m" + echo -e " ${{ env.bashInfo }}Running Command: python3 executable-monitor.py --exe-path=${{ inputs.exe-path }} --timeout-seconds=${{ inputs.timeout-seconds }} --success-line="${{ inputs.success-line }}" $optArgs ${{ env.bashEnd }}" echo "::group::Executable Output" python3 $GITHUB_ACTION_PATH/executable-monitor.py --exe-path=${{ inputs.exe-path }} --timeout-seconds=${{ inputs.timeout-seconds }} $optArgs --success-line="${{ inputs.success-line}}" else echo "::endgroup::" - echo -e "\033[1;33mRunning Command: python3 $GITHUB_ACTION_PATH/executable-monitor.py --exe-path=${{ inputs.exe-path }} --timeout-seconds=${{ inputs.timeout-seconds }} $optArgs \033[0m" + echo -e " ${{ env.bashInfo }}Running Command: python3 executable-monitor.py --exe-path=${{ inputs.exe-path }} --timeout-seconds=${{ inputs.timeout-seconds }} $optArgs ${{ env.bashEnd }}" echo "::group::Executable Output" python3 $GITHUB_ACTION_PATH/executable-monitor.py --exe-path=${{ inputs.exe-path }} --timeout-seconds=${{ inputs.timeout-seconds }} $optArgs fi # Store status as the echo group will overwrite it exitStatus=$? echo "::endgroup::" - if [ "$exitStatus" = "0" ]; then - echo -e "\033[32;3mValid exit status found\033[0m" - exit 0 + if [ exitStatus -eq 0 ]; then + echo -e "${{ env.bashPass }} ${{ env.stepName }} ${{ env.bashEnd }}" else - echo -e "\033[32;31mDid not find a valid exit condition\033[0m" + echo -e "${{ env.bashFail }} ${{ env.stepName }} ${{ env.bashEnd }}" set -e - exit 1 fi - shell: bash + exit $exitStatus diff --git a/executable-monitor/executable-monitor.py b/executable-monitor/executable-monitor.py index e3a85f6a..eac8c695 100755 --- a/executable-monitor/executable-monitor.py +++ b/executable-monitor/executable-monitor.py @@ -7,16 +7,34 @@ import logging from multiprocessing import Process +# Set up logging +logging.getLogger().setLevel(logging.NOTSET) + +# This script is meant for FreeRTOS PR checks, which all run using bash shells +# So wrap the important lines in bash escaped colours. +bashPass="\033[32;1m" +bashWarn="\033[33;1m" +bashFail="\033[31;1m" +bashEnd="\033[0m" +# Add stdout handler to logging +stdout_logging_handler = logging.StreamHandler(sys.stdout) +stdout_logging_handler.setLevel(logging.DEBUG) +stdout_logging_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') +stdout_logging_handler.setFormatter(stdout_logging_formatter) +logging.getLogger().addHandler(stdout_logging_handler) + def runAndMonitor(args): # Set up logging - logging.getLogger().setLevel(logging.NOTSET) + #logging.getLogger().setLevel(logging.NOTSET) # Add stdout handler to logging - stdout_logging_handler = logging.StreamHandler(sys.stdout) - stdout_logging_handler.setLevel(logging.DEBUG) - stdout_logging_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') - stdout_logging_handler.setFormatter(stdout_logging_formatter) - logging.getLogger().addHandler(stdout_logging_handler) + #stdout_logging_handler = logging.StreamHandler(sys.stdout) + #stdout_logging_handler.setLevel(logging.DEBUG) + #stdout_logging_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') + #stdout_logging_handler.setFormatter(stdout_logging_formatter) + + if os.name == 'nt': + logging.getLogger().addHandler(stdout_logging_handler) exe_abs_path = os.path.abspath(args.exe_path) if args.log_dir is not None: @@ -69,14 +87,15 @@ def runAndMonitor(args): exe.kill() time.sleep(.05) else: - logging.info(exe_stdout_line) + # Making an assumption that device output is adding a \n already + logging.info(exe_stdout_line.rstrip("\r\n")) # Check for timeout cur_time_seconds = time.time() if cur_time_seconds >= timeout_time_seconds: logging.info(f"TIMEOUT OF {args.timeout_seconds} SECONDS HIT") exit_condition_met = True - + # Sleep for a short duration between loops to not steal all system resources # time.sleep(.05) @@ -86,10 +105,13 @@ def runAndMonitor(args): logging.info(f"PARSING REST OF LOG") # Capture remaining output and check for the successful line for exe_stdout_line in exe.stdout.readlines(): - logging.info(exe_stdout_line) + # Making an assumption that device output is adding a \n already + logging.info(exe_stdout_line.rstrip("\r\n")) if args.success_line is not None and args.success_line in exe_stdout_line: success_line_found = True - logging.info(f"SUCCESS_LINE_FOUND: {exe_stdout_line}") + # Insert a blank line to make the log look better + print() + logging.info(f"{bashPass}SUCCESS_LINE_FOUND: {exe_stdout_line}{bashEnd}") logging.info("END OF DEVICE OUTPUT") logging.info("EXECUTABLE RUN SUMMARY:") @@ -97,41 +119,31 @@ def runAndMonitor(args): exit_status = 1 # Check if a success line was found if that is an option if ( args.success_line is not None) and (not success_line_found ): - logging.error("Success Line: Success line not output.") + logging.error(f"{bashFail}Success Line: Success line not output.{bashEnd}") exit_status = 1 elif( args.success_line is not None) and ( success_line_found ): exit_status = 0 logging.info(f"Success Line: Success line was output") - + # Check if a exit code was found if that was an option if ( ( exit_status != 0 ) and ( args.success_exit_code is not None) ): # If the executable had to be force killed mark it as a failure if( not exe_exitted): - logging.error("Exit Code: Executable did not exit by itself.\n") + logging.error(f"{bashFail}Exit Code: Executable did not exit by itself.{bashEnd}\n") exit_status = 1 # If the executable exited with a different status mark it as a failure elif ( ( exe_exitted ) and ( exe_exit_status != args.success_exit_code ) ): - logging.error(f"Exit Code: {exe_exit_status} is not equal to requested exit code of {args.success_exit_code}\n") + logging.error(f"{bashFail}Exit Code: {exe_exit_status} is not equal to requested exit code of {args.success_exit_code}{bashEnd}\n") exit_status = 1 # If the executable exited with the same code as requested mark a success elif ( ( exe_exitted ) and ( exe_exit_status == args.success_exit_code ) ): - logging.info(f"Exit Code: Executable exited with requested exit code") + logging.info(f"{bashPass}Exit Code: Executable exited with requested exit code{bashEnd}") exit_status = 0 - logging.info(f"Runner thread exiting with status {exit_status}\n") + logging.info(f"Runner thread exiting with status {exit_status}") exit(exit_status) if __name__ == '__main__': - # Set up logging - logging.getLogger().setLevel(logging.NOTSET) - - # Add stdout handler to logging - stdout_logging_handler = logging.StreamHandler(sys.stdout) - stdout_logging_handler.setLevel(logging.DEBUG) - stdout_logging_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') - stdout_logging_handler.setFormatter(stdout_logging_formatter) - logging.getLogger().addHandler(stdout_logging_handler) - # Parse arguments parser = ArgumentParser(description='Executable monitor.') parser.add_argument('--exe-path', @@ -162,15 +174,15 @@ def runAndMonitor(args): args = parser.parse_args() if args.success_exit_code is None and args.success_line is None: - logging.error("Must specify at least one of the following: --success-line, --success-exit-code.") + logging.error(f"{bashFail}Must specify at least one of the following: --success-line, --success-exit-code.{bashEnd}") sys.exit(1) elif args.success_exit_code is not None and args.success_line is not None: - logging.warning("Received an option for success-line and success-exit-code.") - logging.warning("Be aware: This program will report SUCCESS on either of these conditions being met") + logging.warning(f"{bashWarn}Received an option for success-line and success-exit-code.{bashEnd}") + logging.warning(f"{bashWarn}Be aware: This program will report SUCCESS on either of these conditions being met{bashEnd}") if not os.path.exists(args.exe_path): - logging.error(f'Input executable path \"{args.exe_path}\" does not exist.') + logging.error(f"{bashFail}Input executable path \"{args.exe_path}\" does not exist.{bashEnd}") sys.exit(1) # Convert any relative path (like './') in passed argument to absolute path. @@ -194,31 +206,31 @@ def runAndMonitor(args): logging.info(f"Running executable: {exe_abs_path} ") logging.info(f"Timeout (seconds) per run: {args.timeout_seconds}") - + if not args.retry_attempts: args.retry_attempts = 0 else: logging.info(f"Will relaunch the executable {args.retry_attempts} times to look for a valid success metric") - + if args.success_line is not None: logging.info(f"Searching for success line: {args.success_line}") if args.success_exit_code is not None: logging.info(f"Searching for exit code: {args.success_exit_code}") - + # Small increase on the timeout to allow the thread to try and timeout threadTimeout = ( args.timeout_seconds + 3 ) for attempts in range(0,args.retry_attempts + 1): exit_status = 1 - # Set the timeout for the thread + # Set the timeout for the thread thread = Process(target=runAndMonitor, args=(args,)) thread.start() - # Wait for the thread to join, or hit a timeout. + # Wait for the thread to join, or hit a timeout. thread.join(timeout=threadTimeout) # As join() always returns None, you must call is_alive() after join() to decide whether a timeout happened - # If the thread is still alive, the join() call timed out. + # If the thread is still alive, the join() call timed out. if ( ( thread.exitcode is None ) and ( thread.is_alive() ) ): # Print the thread timeout they passed in to the log - logging.warning(f"EXECUTABLE HAS HIT TIMEOUT OF {threadTimeout - 3} SECONDS: FORCE KILLING THREAD") + logging.warning(f"{bashWarn}EXECUTABLE HAS HIT TIMEOUT OF {threadTimeout - 3} SECONDS: FORCE KILLING THREAD") thread.kill() exit_status = 1 else: @@ -226,7 +238,7 @@ def runAndMonitor(args): logging.info(f"THREAD EXITED WITH EXITCODE {exit_status}") if( ( attempts < args.retry_attempts ) and exit_status != 0 ): - logging.warning(f"DID NOT RECEIVE SUCCESSFUL EXIT STATUS, TRYING RE-ATTEMPT {attempts+1} OF {args.retry_attempts}\n") + logging.warning(f"{bashWarn}DID NOT RECEIVE SUCCESSFUL EXIT STATUS, TRYING RE-ATTEMPT {attempts+1} OF {args.retry_attempts}\n") else: break diff --git a/executable-monitor/test.c b/executable-monitor/test.c index 564e17f0..6f80b9d5 100644 --- a/executable-monitor/test.c +++ b/executable-monitor/test.c @@ -1,30 +1,55 @@ -#include #include #include #include -#include - +#include typedef struct DateAndTime { - int year; - int month; - int day; - int hour; - int minutes; - int seconds; - int msec; + uint64_t hour; + uint64_t minutes; + uint64_t seconds; + uint64_t msec; } DateAndTime; + +#if defined( WIN32 ) || defined( _WIN32 ) || defined( __WIN32__ ) || defined( __NT__ ) || defined( WIN64 ) || defined( __WIN64 ) + #include + /* Remove the warning about implicit sleep even with windows.h included */ + extern void sleep( int miliseconds ); + void getTime( struct DateAndTime * currentTime ) + { + SYSTEMTIME st, lt; + + GetLocalTime( < ); + currentTime->hour = lt.wHour; + currentTime->minutes = lt.wMinute; + currentTime->seconds = lt.wSecond; + currentTime->msec = lt.wMilliseconds; + } +#else /* if defined( WIN32 ) || defined( _WIN32 ) || defined( __WIN32__ ) || defined( __NT__ ) || defined( WIN64 ) || defined( __WIN64 ) */ + #include + #include + void getTime( struct DateAndTime * currentTime ) + { + struct timeval tv; + struct tm * tm; + + gettimeofday( &tv, NULL ); + tm = localtime( &tv.tv_sec ); + currentTime->hour = tm->tm_hour; + currentTime->minutes = tm->tm_min; + currentTime->seconds = tm->tm_sec; + currentTime->msec = ( int ) ( tv.tv_usec / 1000 ); + } +#endif /* if defined( WIN32 ) || defined( _WIN32 ) || defined( __WIN32__ ) || defined( __NT__ ) || defined( WIN64 ) || defined( __WIN64 ) */ + int main( int argc, char ** argv ) { - DateAndTime date_and_time; - struct timeval tv; - struct tm * tm; + DateAndTime currentTime = { 0 }; int32_t loop = 0; - int32_t totalLoops = 5; + int32_t totalLoops = 5U; int32_t exitCode = 0; if( argc == 1 ) @@ -37,49 +62,33 @@ int main( int argc, if( argc == 2 ) { - totalLoops = ( int32_t ) atoi( argv[ 2 ] ); + totalLoops = atoi( argv[ 1 ] ); printf( "Will run for requested %d loops\n", totalLoops ); } if( argc == 3 ) { - exitCode = atoi( argv[ 3 ] ); + exitCode = atoi( argv[ 2 ] ); printf( "Will exit with supplied exit code %d\n", exitCode ); } setvbuf( stdout, NULL, _IONBF, 0 ); - for(int i = 1; i < totalLoops; i++) + for( int i = 1U; i < totalLoops; i++ ) { - gettimeofday( &tv, NULL ); - tm = localtime( &tv.tv_sec ); - /* Add 1900 to get the right year value */ - /* read the manual page for localtime() */ - /* date_and_time.year = tm->tm_year + 1900; */ - /* Months are 0 based in struct tm */ - date_and_time.year = tm->tm_year + 1900; - date_and_time.month = tm->tm_mon + 1; - date_and_time.day = tm->tm_mday; - date_and_time.hour = tm->tm_hour; - date_and_time.minutes = tm->tm_min; - date_and_time.seconds = tm->tm_sec; - date_and_time.msec = ( int ) ( tv.tv_usec / 1000 ); - - fprintf( stdout, "%02d:%02d:%02d.%03d %02d-%02d-%04d TEST APPLICIATION SLEEPING FOR %d SECONDS\n", - date_and_time.hour, - date_and_time.minutes, - date_and_time.seconds, - date_and_time.msec, - date_and_time.day, - date_and_time.month, - date_and_time.year, - i * 3U - ); + getTime( ¤tTime ); + printf( "%02llu:%02llu:%02llu.%03llu TEST APPLICATION SLEEPING FOR %d SECONDS\n", + currentTime.hour, + currentTime.minutes, + currentTime.seconds, + currentTime.msec, + i * 3U ); sleep( i * 3U ); } -#ifdef EXIT_WITH_MINUTES - exitCode = date_and_time.minutes; -#endif - printf( "EXITING TEST APPLICICATION WITH EXIT CODE = %d\n",exitCode ); + + #ifdef EXIT_WITH_MINUTES + exitCode = currentTime.minutes; + #endif + printf( "EXITING TEST APPLICATION WITH EXIT CODE = %d\n", exitCode ); return exitCode; } diff --git a/formatting/.clang-format b/formatting/.clang-format new file mode 100644 index 00000000..84b6bc79 --- /dev/null +++ b/formatting/.clang-format @@ -0,0 +1,104 @@ +--- +Language: Cpp +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: None +AlignConsecutiveBitFields: AcrossEmptyLinesAndComments +AlignConsecutiveDeclarations: None +AlignConsecutiveMacros: AcrossEmptyLinesAndComments +AlignEscapedNewlines: Left +AlignOperands: Align +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: false +BinPackParameters: false +BitFieldColonSpacing: Both +BraceWrapping: + AfterCaseLabel: true + AfterClass: true + AfterControlStatement: Always + AfterEnum: true + AfterExternBlock: false + AfterFunction: true + AfterNamespace: true + AfterStruct: true + AfterUnion: true + BeforeCatch: true + BeforeElse: true + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeConceptDeclarations: true +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +BreakStringLiterals: true +ColumnLimit: 80 +CompactNamespaces: false +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: false +DeriveLineEnding: false +DerivePointerAlignment: false +EmptyLineBeforeAccessModifier: Always +FixNamespaceComments: true +IncludeBlocks: Preserve +IndentCaseBlocks: false +IndentCaseLabels: true +IndentExternBlock: NoIndent +IndentGotoLabels: true +IndentPPDirectives: BeforeHash +IndentWidth: 4 +KeepEmptyLinesAtTheStartOfBlocks: false +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +PenaltyBreakAssignment: 1000 +PenaltyBreakBeforeFirstCallParameter: 200 +PenaltyBreakComment: 50 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 100 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 100 +PenaltyIndentedWhitespace: 0 +PenaltyReturnTypeOnItsOwnLine: 10000 +PointerAlignment: Middle +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: true +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceBeforeCpp11BracedList: true +SpaceBeforeCtorInitializerColon: false +SpaceBeforeInheritanceColon: false +SpaceBeforeParens: Never +SpaceBeforeRangeBasedForLoopColon: false +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInConditionalStatement: true +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: true +SpacesInParentheses: true +SpacesInSquareBrackets: true +TabWidth: 4 +UseCRLF: false +UseTab: Never +... + diff --git a/formatting/action.yml b/formatting/action.yml index 8ac71180..3d07199a 100644 --- a/formatting/action.yml +++ b/formatting/action.yml @@ -2,78 +2,133 @@ name: 'formatting' description: 'CI formatting check' inputs: path: - description: 'Path to repository folder to run formatting check for.' + description: 'Path to repository folder to run formatting check for. ' required: false default: ./ exclude-files: - description: 'List of comma-separated files to exclude from trailing whitespace check. Eg file1,file2' + description: 'List of comma-separated files to exclude from the formatting check. Eg file1,file2' required: false default: '' exclude-dirs: - description: 'List of comma-separated directories to exclude from trailing whitespace formatting check. Eg docs,build' + description: 'List of comma-separated directories to exclude from the formatting check. Eg docs,build' required: false default: '' + include-extensions: + description: 'List of comma-separated extensions to add to the formatting check . Eg md,dox' + required: false + default: '' + runs: using: "composite" - steps: - - name: Install Uncrustify - run: | - : # Install Uncrustify - echo "::group::Install Uncrustify" - sudo apt-get install uncrustify - echo "::endgroup::" + steps: + - env: + bashPass: \033[32;1mPASSED - + bashInfo: \033[33;1mINFO - + bashFail: \033[31;1mFAILED - + bashEnd: \033[0m + stepName: Install Clang Format + name: ${{ env.stepName }} shell: bash - - name: Run Uncrustify - working-directory: ${{ inputs.path }} run: | - : # Uncrustify on C files while ignoring symlinks. - : # Make a collapsible section in the log to run uncrustify - echo "::group::Uncrustify Check" - : # GitHub automtically use "set -e" which causes scripts to fail on the first exit code - : # This would mean the first time a file fails the check that we exit without formatting all files. - set +e - : # Format all the files using the common config file. - find . -iname "*.[ch]" | xargs uncrustify --no-backup --replace --if-changed -c $GITHUB_ACTION_PATH/uncrustify.cfg -l C + # ${{ env.stepName }} + echo "::group::${{ env.stepName }}" + sudo apt-get install clang-format fd-find + echo "$GITHUB_ACTION_PATH" >> $GITHUB_PATH + export PATH="$PATH:$GITHUB_ACTION_PATH" + + fdfind --version + fdInstalled=$? + + clang-format --version + clangFormatInstalled=$? + + getFiles --help + getFilesAdded=$? + echo "::endgroup::" - : # Run a git diff to print the differences if any exist, return an error code if there are any - git diff --exit-code - if [ "$?" = "0" ]; then - echo -e "\033[32;3mUncrustify check passed\033[0m" - exit 0 - else - echo -e "\033[32;31mFormatting check (using Uncrustify) failed...\033[0m" - : # If there is an error, set this flag high again so the exit 1 fails the run - set -e - exit 1 - fi - shell: bash - - name: Check For Trailing Whitespace - working-directory: ${{ inputs.path }} - run: | - : # Trailing Whitespace Check - set +e - grep --exclude={README.md,${{ inputs.exclude-files }}} --exclude-dir={${{ inputs.exclude-dirs }},'.git'} -rnI -e "[[:blank:]]$" . - if [ "$?" = "0" ]; then - set -e - echo -e "\033[32;31mFiles have trailing whitespace.\033[0m" + if [ $getFilesAdded -eq 1 ] || [ $clangFormatInstalled -eq 1] || [ $fdInstalled -eq 1 ]; then + echo -e "${{ env.bashFail }} ${{ env.stepName }} ${{ env.bashEnd }}" exit 1 else - echo -e "\033[32;3mTrailing whitespace check passed\033[0m" + echo -e "${{ env.bashPass }} ${{ env.stepName }} ${{ env.bashEnd }}" exit 0 fi + + - env: + stepName: Validate Inputs + # The bash escape character is \033 + # At time of writing, you can't add a global environment + # to an action file so stuck with this. If this gets changed + # Please move this + bashPass: \033[32;1mPASSED - + bashInfo: \033[33;1mINFO - + bashFail: \033[31;1mFAILED - + bashEnd: \033[0m + name: ${{ env.stepName }} + id: validate-inputs shell: bash - - name: Check for CRLF + run: | + # ${{ env.stepName }} + # Check exclude dirs for possible input issues + if [[ "${{ inputs.exclude-dirs }}" = *"\n"* ]]; then + echo -e "${{ env.bashFail }} YOU HAVE NEWLINE IN YOUR EXCLUDE-DIRS. ${{ env.bashEnd }}" + exitStatus=1 + fi + + - env: + stepName: Run Clang Format + name: ${{ env.stepName }} + if: ${{ steps.validate-inputs.outcome}} = "success" working-directory: ${{ inputs.path }} + shell: bash run: | - : # Check for CRLF Line Ending - set +e - find . -path ./.git -prune -o -exec file {} + | grep "CRLF" - if [ "$?" = "0" ]; then - set -e - echo -e "\033[32;31mFiles have CRLF line endings.\033[0m" - exit 1 + # ${{ env.stepName }} + echo -e "${{ env.bashInfo }} Using clang-format version "$(clang-format --version)" ${{ env.bashEnd }}" + echo "::group::${{ env.stepName }}" + exitCode=0 + clangError=0 + export PATH="$PATH:$GITHUB_ACTION_PATH" + files=$(getFiles --exclude-files="${{inputs.exclude-files}}" --exclude-dir="${{inputs.exclude-dirs}}" ) + if [ ! $? -eq 0 ]; then + echo -e "${{ env.bashFail }} Error calling getFiles() ${{ env.bashEnd }}" + echo -e "$fileOut" + exit 1 + fi + + # set +e + # Loop over the files that matched the above conditions + for file in ${files[@]}; do + # Print them out for future debugging + echo -e "${{ env.bashInfo }} Parsing File: "$file" ${{ env.bashEnd }}" + + # Pipe the output of the command to a variable + # This way we can print the command only if it fails + fileOut=$(clang-format -i --verbose --style=file $file) + # If the previous command failed then print out an error message as well as the output + if [ ! $? -eq 0 ]; then + echo -e "${{ env.bashFail }} Error parsing "$file" ${{ env.bashEnd }}" + echo -e "$fileOut" + clangError=1 + fi + done + + echo "::endgroup::" + # Run a git diff to print the differences if any exist, return an error code if there are any + + echo "::group::File Difference" + git diff --exit-code + exitCode=$? + echo "::endgroup::" + # set -e + if [ $clangError -eq 1 ]; then + echo -e "${{ env.bashFail }} Clang Encountered an Error ${{ env.bashEnd }}" + fi + + if [ $exitCode -eq 0 ]; then + echo -e "${{ env.bashPass }} ${{env.stepName }} ${{ env.bashEnd }}" else - echo -e "\033[32;3mLine ending check passed\033[0m" - exit 0 + echo -e "${{ env.bashFail }} List of files with formatting errors: ${{ env.bashEnd }}" + echo -e "${{ env.bashFail }} "$(git diff --name-only)" ${{ env.bashEnd }} " + echo -e "${{ env.bashFail }} ${{env.stepName }} ${{ env.bashEnd }}" fi - shell: bash + exit $exitCode || $clangError diff --git a/formatting/filesWithCRLFEndings/cgc_error.h b/formatting/filesWithCRLFEndings/cgc_error.h new file mode 100644 index 00000000..eb2f3481 --- /dev/null +++ b/formatting/filesWithCRLFEndings/cgc_error.h @@ -0,0 +1,47 @@ +#ifndef CGC_ERROR_H_ +#define CGC_ERROR_H_ + +/* Error checking macros for the clock selction and clock enable defines */ + +#if ( (CLK_SOURCE != CLK_SOURCE_LOCO) && \ + (CLK_SOURCE != CLK_SOURCE_HOCO) && \ + (CLK_SOURCE != CLK_SOURCE_MAIN) && \ + (CLK_SOURCE != CLK_SOURCE_SUB) && \ + (CLK_SOURCE != CLK_SOURCE_PLL) ) + #error "No CLK_SOURCE specified. Please specify a valid CLK_SOURCE"; +#endif + + +#if (CLK_SOURCE == CLK_SOURCE_HOCO) && (ENABLE_HOCO == 0) + #error "HOCO has been specified as the CLK_SOURCE but ENABLE_HOCO is (0). Please set to (1) in file cgc.h" +#endif + +#if (CLK_SOURCE == CLK_SOURCE_MAIN) && (ENABLE_MAIN == 0) + #error "HOCO has been specified as the CLK_SOURCE but ENABLE_HOCO is (0). Please set to (1) in file cgc.h" +#endif + +#if (CLK_SOURCE == CLK_SOURCE_SUB) && (ENABLE_SUB == 0) + #error "HOCO has been specified as the CLK_SOURCE but ENABLE_HOCO is (0). Please set to (1) in file cgc.h" +#endif + +#if (CLK_SOURCE == CLK_SOURCE_PLL) && (ENABLE_PLL == 0) + #error "PLL has been specified as the CLK_SOURCE but ENABLE_PLL is (0). Please set to (1) in file cgc.h" +#endif + +#if ( FCLK_FREQUENCY > 50000000L ) + #error "FCLK_FREQUENCY Error: Please enter a valid divider value" +#endif + +#if ( ICLK_FREQUENCY > 100000000L ) + #error "ICLK_FREQUENCY Error: Please enter a valid divider value" +#endif + +#if ( BCLK_FREQUENCY > 100000000L ) + #error "BCLK_FREQUENCY Error: Please enter a valid divider value" +#endif + +#if ( PCLKB_FREQUENCY > 50000000L ) + #error "PCLKB_FREQUENCY Error: Please enter a valid divider value" +#endif + +#endif \ No newline at end of file diff --git a/formatting/filesWithCRLFEndings/main-blinky.c b/formatting/filesWithCRLFEndings/main-blinky.c new file mode 100644 index 00000000..0eaca8b0 --- /dev/null +++ b/formatting/filesWithCRLFEndings/main-blinky.c @@ -0,0 +1,193 @@ +/* + * FreeRTOS V202212.00 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 + * + */ + +/****************************************************************************** + * NOTE 1: This project provides two demo applications. A simple blinky style + * project, and a more comprehensive test and demo application. The + * mainCREATE_SIMPLE_BLINKY_DEMO_ONLY setting in main.c is used to select + * between the two. See the notes on using mainCREATE_SIMPLE_BLINKY_DEMO_ONLY + * in main.c. This file implements the simply blinky style version. + * + * NOTE 2: This file only contains the source code that is specific to the + * basic demo. Generic functions, such FreeRTOS hook functions, and functions + * required to configure the hardware, are defined in main.c. + ****************************************************************************** + * + * main_blinky() creates one queue, and two tasks. It then starts the + * scheduler. + * + * The Queue Send Task: + * The queue send task is implemented by the prvQueueSendTask() function in + * this file. prvQueueSendTask() sits in a loop that causes it to repeatedly + * block for 200 milliseconds before sending the value 100 to the queue that + * was created within main_blinky(). Once the value is sent, the task loops + * back around to block for another 200 milliseconds. + * + * The Queue Receive Task: + * The queue receive task is implemented by the prvQueueReceiveTask() function + * in this file. prvQueueReceiveTask() sits in a loop where it repeatedly + * blocks on attempts to read data from the queue that was created within + * main_blinky(). When data is received, the task checks the value of the + * data, and if the value equals the expected 100, toggles the LED. The 'block + * time' parameter passed to the queue receive function specifies that the + * task should be held in the Blocked state indefinitely to wait for data to + * be available on the queue. The queue receive task will only leave the + * Blocked state when the queue send task writes to the queue. As the queue + * send task writes to the queue every 200 milliseconds, the queue receive + * task leaves the Blocked state every 200 milliseconds, and therefore toggles + * the LED every 200 milliseconds. + */ + +/* Kernel includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +/* Priorities at which the tasks are created. */ +#define mainQUEUE_RECEIVE_TASK_PRIORITY ( tskIDLE_PRIORITY + 2 ) +#define mainQUEUE_SEND_TASK_PRIORITY ( tskIDLE_PRIORITY + 1 ) + +/* The rate at which data is sent to the queue. The 200ms value is converted +to ticks using the portTICK_PERIOD_MS constant. */ +#define mainQUEUE_SEND_FREQUENCY_MS ( 200 / portTICK_PERIOD_MS ) + +/* The number of items the queue can hold. This is 1 as the receive task +will remove items as they are added, meaning the send task should always find +the queue empty. */ +#define mainQUEUE_LENGTH ( 1 ) + +/* Values passed to the two tasks just to check the task parameter +functionality. */ +#define mainQUEUE_SEND_PARAMETER ( 0x1111UL ) +#define mainQUEUE_RECEIVE_PARAMETER ( 0x22UL ) + + +/*-----------------------------------------------------------*/ + +/* + * The tasks as described in the comments at the top of this file. + */ +static void prvQueueReceiveTask( void *pvParameters ); +static void prvQueueSendTask( void *pvParameters ); + +/* + * Called by main() to create the simply blinky style application if + * mainCREATE_SIMPLE_BLINKY_DEMO_ONLY is set to 1. + */ +void main_blinky( void ); + +/*-----------------------------------------------------------*/ + +/* The queue used by both tasks. */ +static QueueHandle_t xQueue = NULL; + +/*-----------------------------------------------------------*/ + +void main_blinky( void ) +{ + /* Create the queue. */ + xQueue = xQueueCreate( mainQUEUE_LENGTH, sizeof( unsigned long ) ); + + if( xQueue != NULL ) + { + /* Start the two tasks as described in the comments at the top of this + file. */ + xTaskCreate( prvQueueReceiveTask, /* The function that implements the task. */ + "Rx", /* The text name assigned to the task - for debug only as it is not used by the kernel. */ + configMINIMAL_STACK_SIZE, /* The size of the stack to allocate to the task. */ + ( void * ) mainQUEUE_RECEIVE_PARAMETER, /* The parameter passed to the task - just to check the functionality. */ + mainQUEUE_RECEIVE_TASK_PRIORITY, /* The priority assigned to the task. */ + NULL ); /* The task handle is not required, so NULL is passed. */ + + xTaskCreate( prvQueueSendTask, "TX", configMINIMAL_STACK_SIZE, ( void * ) mainQUEUE_SEND_PARAMETER, mainQUEUE_SEND_TASK_PRIORITY, NULL ); + + /* Start the tasks and timer running. */ + vTaskStartScheduler(); + } + + /* If all is well, the scheduler will now be running, and the following + line will never be reached. If the following line does execute, then + there was insufficient FreeRTOS heap memory available for the idle and/or + timer tasks to be created. See the memory management section on the + FreeRTOS web site for more details. */ + for( ;; ); +} +/*-----------------------------------------------------------*/ + +static void prvQueueSendTask( void *pvParameters ) +{ +TickType_t xNextWakeTime; +const unsigned long ulValueToSend = 100UL; + + /* Check the task parameter is as expected. */ + configASSERT( ( ( unsigned long ) pvParameters ) == mainQUEUE_SEND_PARAMETER ); + + /* Initialise xNextWakeTime - this only needs to be done once. */ + xNextWakeTime = xTaskGetTickCount(); + + for( ;; ) + { + /* Place this task in the blocked state until it is time to run again. + The block time is specified in ticks, the constant used converts ticks + to ms. While in the Blocked state this task will not consume any CPU + time. */ + vTaskDelayUntil( &xNextWakeTime, mainQUEUE_SEND_FREQUENCY_MS ); + + /* Send to the queue - causing the queue receive task to unblock and + toggle the LED. 0 is used as the block time so the sending operation + will not block - it shouldn't need to block as the queue should always + be empty at this point in the code. */ + xQueueSend( xQueue, &ulValueToSend, 0U ); + } +} +/*-----------------------------------------------------------*/ + +static void prvQueueReceiveTask( void *pvParameters ) +{ +unsigned long ulReceivedValue; + + /* Check the task parameter is as expected. */ + configASSERT( ( ( unsigned long ) pvParameters ) == mainQUEUE_RECEIVE_PARAMETER ); + + for( ;; ) + { + /* Wait until something arrives in the queue - this task will block + indefinitely provided INCLUDE_vTaskSuspend is set to 1 in + FreeRTOSConfig.h. */ + xQueueReceive( xQueue, &ulReceivedValue, portMAX_DELAY ); + + /* To get here something must have been received from the queue, but + is it the expected value? If it is, toggle the LED. */ + if( ulReceivedValue == 100UL ) + { + /* Toggle the LED. */ + port_pin_toggle_output_level( LED_0_PIN ); + ulReceivedValue = 0U; + } + } +} +/*-----------------------------------------------------------*/ + diff --git a/formatting/filesWithCRLFEndings/out.txt b/formatting/filesWithCRLFEndings/out.txt new file mode 100644 index 00000000..42336909 --- /dev/null +++ b/formatting/filesWithCRLFEndings/out.txt @@ -0,0 +1,47 @@ +#ifndef CGC_ERROR_H_ +#define CGC_ERROR_H_ + +/* Error checking macros for the clock selction and clock enable defines */ + +#if ( (CLK_SOURCE != CLK_SOURCE_LOCO) && \ + (CLK_SOURCE != CLK_SOURCE_HOCO) && \ + (CLK_SOURCE != CLK_SOURCE_MAIN) && \ + (CLK_SOURCE != CLK_SOURCE_SUB) && \ + (CLK_SOURCE != CLK_SOURCE_PLL) ) + #error "No CLK_SOURCE specified. Please specify a valid CLK_SOURCE"; +#endif + + +#if (CLK_SOURCE == CLK_SOURCE_HOCO) && (ENABLE_HOCO == 0) + #error "HOCO has been specified as the CLK_SOURCE but ENABLE_HOCO is (0). Please set to (1) in file cgc.h" +#endif + +#if (CLK_SOURCE == CLK_SOURCE_MAIN) && (ENABLE_MAIN == 0) + #error "HOCO has been specified as the CLK_SOURCE but ENABLE_HOCO is (0). Please set to (1) in file cgc.h" +#endif + +#if (CLK_SOURCE == CLK_SOURCE_SUB) && (ENABLE_SUB == 0) + #error "HOCO has been specified as the CLK_SOURCE but ENABLE_HOCO is (0). Please set to (1) in file cgc.h" +#endif + +#if (CLK_SOURCE == CLK_SOURCE_PLL) && (ENABLE_PLL == 0) + #error "PLL has been specified as the CLK_SOURCE but ENABLE_PLL is (0). Please set to (1) in file cgc.h" +#endif + +#if ( FCLK_FREQUENCY > 50000000L ) + #error "FCLK_FREQUENCY Error: Please enter a valid divider value" +#endif + +#if ( ICLK_FREQUENCY > 100000000L ) + #error "ICLK_FREQUENCY Error: Please enter a valid divider value" +#endif + +#if ( BCLK_FREQUENCY > 100000000L ) + #error "BCLK_FREQUENCY Error: Please enter a valid divider value" +#endif + +#if ( PCLKB_FREQUENCY > 50000000L ) + #error "PCLKB_FREQUENCY Error: Please enter a valid divider value" +#endif + +#endif \ No newline at end of file diff --git a/formatting/filesWithFormattingErrors/fileFormattedWithClangFormatV16.c b/formatting/filesWithFormattingErrors/fileFormattedWithClangFormatV16.c new file mode 100644 index 00000000..3a642c3e --- /dev/null +++ b/formatting/filesWithFormattingErrors/fileFormattedWithClangFormatV16.c @@ -0,0 +1,6151 @@ +/* + * 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 "stack_macros.h" +#include "task.h" +#include "timers.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 ) */ diff --git a/formatting/filesWithFormattingErrors/goodFileInHere.c b/formatting/filesWithFormattingErrors/goodFileInHere.c new file mode 100644 index 00000000..3a642c3e --- /dev/null +++ b/formatting/filesWithFormattingErrors/goodFileInHere.c @@ -0,0 +1,6151 @@ +/* + * 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 "stack_macros.h" +#include "task.h" +#include "timers.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 ) */ diff --git a/formatting/filesWithFormattingErrors/test.c b/formatting/filesWithFormattingErrors/test.c new file mode 100644 index 00000000..f600ff52 --- /dev/null +++ b/formatting/filesWithFormattingErrors/test.c @@ -0,0 +1,95 @@ +#include +#include +#include +#include + + +typedef struct DateAndTime +{ + uint64_t hour; + uint64_t minutes; + uint64_t seconds; + uint64_t msec; +} DateAndTime; + + +#if defined( WIN32 ) || defined( _WIN32 ) || defined( __WIN32__ ) || defined( __NT__ ) || defined( WIN64 ) || defined( __WIN64 ) + #include + /* Remove the warning about implicit sleep even with windows.h included */ + extern void sleep( int miliseconds ); + void getTime( struct DateAndTime * currentTime ) + { + SYSTEMTIME st, lt; + + GetLocalTime( < ); + currentTime->hour = lt.wHour; + currentTime->minutes = lt.wMinute; + currentTime->seconds = lt.wSecond; + currentTime->msec = lt.wMilliseconds; + } +#else /* if defined( WIN32 ) || defined ( _WIN32 ) || defined( __WIN32__ ) || defined( __NT__ ) || defined( WIN64 ) || defined( __WIN64 ) */ + #include + #include + void getTime( struct DateAndTime * currentTime ) + { + struct timeval tv; + struct tm * tm; + + gettimeofday( &tv, NULL ); + tm = localtime( &tv.tv_sec ); + currentTime->hour = tm->tm_hour; + currentTime->minutes = tm->tm_min; + currentTime->seconds = tm->tm_sec; + currentTime->msec = ( int ) ( tv.tv_usec / 1000 ); + } +#endif /* if defined( WIN32 ) || defined( _WIN32 ) || defined( __WIN32__ ) || defined( __NT__ ) || defined( WIN64 ) || defined( __WIN64 ) */ + +int main( int argc, + char ** argv ) +{ + DateAndTime currentTime = { 0 }; + int32_t loop = 0; + int32_t totalLoops = 5U; + int32_t exitCode = 0; + + if( argc == 1 ) + { + printf( "This is a basic test application .\n"); + printf( "It prints the date and time and then sleeps for loopCount * 3\n" ); + printf( "This program takes in two inputs, a loop count and an exit code\n" ); + printf( "By default it will run %d loops and exit with exit status %d\n", totalLoops, exitCode ); + } + + if( argc == 2 ) + { + totalLoops = atoi( argv[ 1 ] ); + printf( "Will run for requested %d loops\n", totalLoops ); + } + + if( argc == 3 ) + { + exitCode = atoi( argv[ 2 ] ); + printf( "Will exit with supplied exit code %d\n", exitCode ); + } + + setvbuf( stdout, NULL, _IONBF, 0 ); + + for(int i = 1U; i < totalLoops; i++ ) + { + getTime( ¤tTime ); + printf( "%02llu:%02llu:%02llu.%03llu TEST APPLICATION SLEEPING FOR %d SECONDS\n", + currentTime.hour, + currentTime.minutes , + currentTime.seconds, + currentTime.msec, + i * 3U ); + sleep( i * 3U ); + } + + #ifdef EXIT_WITH_MINUTES + exitCode = currentTime.minutes; + #endif + printf( "EXITING TEST APPLICATION WITH EXIT CODE = %d\n" +, exitCode ); + return exitCode; +} diff --git a/formatting/filesWithTrailingWhitespace/test.c b/formatting/filesWithTrailingWhitespace/test.c new file mode 100644 index 00000000..bbf1062f --- /dev/null +++ b/formatting/filesWithTrailingWhitespace/test.c @@ -0,0 +1,94 @@ +#include +#include +#include +#include + + +typedef struct DateAndTime +{ + uint64_t hour; + uint64_t minutes; + uint64_t seconds; + uint64_t msec; +} DateAndTime; + + +#if defined( WIN32 ) || defined( _WIN32 ) || defined( __WIN32__ ) || defined( __NT__ ) || defined( WIN64 ) || defined( __WIN64 ) + #include + /* Remove the warning about implicit sleep even with windows.h included */ + extern void sleep( int miliseconds ); + void getTime( struct DateAndTime * currentTime ) + { + SYSTEMTIME st, lt; + + GetLocalTime( < ); + currentTime->hour = lt.wHour; + currentTime->minutes = lt.wMinute; + currentTime->seconds = lt.wSecond; + currentTime->msec = lt.wMilliseconds; + } +#else /* if defined( WIN32 ) || defined( _WIN32 ) || defined( __WIN32__ ) || defined( __NT__ ) || defined( WIN64 ) || defined( __WIN64 ) */ + #include + #include + void getTime( struct DateAndTime * currentTime ) + { + struct timeval tv; + struct tm * tm; + + gettimeofday( &tv, NULL ); + tm = localtime( &tv.tv_sec ); + currentTime->hour = tm->tm_hour; + currentTime->minutes = tm->tm_min; + currentTime->seconds = tm->tm_sec; + currentTime->msec = ( int ) ( tv.tv_usec / 1000 ); + } +#endif /* if defined( WIN32 ) || defined( _WIN32 ) || defined( __WIN32__ ) || defined( __NT__ ) || defined( WIN64 ) || defined( __WIN64 ) */ + +int main( int argc, + char ** argv ) +{ + DateAndTime currentTime = { 0 }; + int32_t loop = 0; + int32_t totalLoops = 5U; + int32_t exitCode = 0; + + if( argc == 1 ) + { + printf( "This is a basic test application.\n" ); + printf( "It prints the date and time and then sleeps for loopCount * 3\n" ); + printf( "This program takes in two inputs, a loop count and an exit code\n" ); + printf( "By default it will run %d loops and exit with exit status %d\n", totalLoops, exitCode ); + } + + if( argc == 2 ) + { + totalLoops = atoi( argv[ 1 ] ); + printf( "Will run for requested %d loops\n", totalLoops ); + } + + if( argc == 3 ) + { + exitCode = atoi( argv[ 2 ] ); + printf( "Will exit with supplied exit code %d\n", exitCode ); + } + + setvbuf( stdout, NULL, _IONBF, 0 ); + + for( int i = 1U; i < totalLoops; i++ ) + { + getTime( ¤tTime ); + printf( "%02llu:%02llu:%02llu.%03llu TEST APPLICATION SLEEPING FOR %d SECONDS\n", + currentTime.hour, + currentTime.minutes, + currentTime.seconds, + currentTime.msec, + i * 3U ); + sleep( i * 3U ); + } + + #ifdef EXIT_WITH_MINUTES + exitCode = currentTime.minutes; + #endif + printf( "EXITING TEST APPLICATION WITH EXIT CODE = %d\n", exitCode ); + return exitCode; +} diff --git a/formatting/getFiles b/formatting/getFiles new file mode 100755 index 00000000..0f8f6527 --- /dev/null +++ b/formatting/getFiles @@ -0,0 +1,118 @@ +#!/bin/bash +bashPass="\033[32;1mPASSED -" +bashInfo="\033[33;1mINFO -" +bashFail="\033[31;1mFAILED -" +bashEnd="\033[0m" + +output=$(getopt -V) +if [[ *"getopt"* == "$output" ]] && [ $# != 0 ]; then + echo -e "$bashInfo This script cannot parse options from Mac command line's default 'getopt' $bashEnd" + echo -e "$bashInfo Run \"brew install gnu-getopt\" and then add it to your path using $bashEnd" + echo -e "$bashInfo export PATH=\"/opt/homebrew/opt/gnu-getopt/bin:\$PATH\" $bashEnd" + echo -e "$bashInfo At which point you can then use this script with options $bashEnd" + exit 1; +fi +# Check number of arguments +files="" +file="" +excludeDirs="" +excludeFiles="" +includeExtensions="" +if [[ "$OSTYPE" == "linux-gnu"* ]]; then + if [ $# != 0 ]; then + VALID_ARGS=$(getopt -o h,ed:,ef:,if: --long help,exclude-dirs:,exclude-files:,include-extensions: -- "$@") + eval set -- "$VALID_ARGS" + while [ : ]; do + case "$1" in + ed | --exclude-dirs ) + # $2 Holds the argument passed after --exclude-files or --ed + # Use sed to replace the commas with the exclude flag + if ! [ -z "$2" ]; then + excludeDirs="-E $(echo "$2" | sed -r 's/,/ -E /g' )" + fi + shift 2 + ;; + + ef | --exclude-files ) + # $2 Holds the argument passed after --exclude-files or --ed + # Use sed to replace the commas with the exclude flag + if ! [ -z "$2" ]; then + excludeFiles="-E $( echo "$2" | sed -r 's/,/ -E /g' )" + fi + shift 2 + ;; + + if | --include-extensions ) + # $2 Holds the argument passed after --exclude-files or --ed + # Use sed to replace the commas with the exclude flag + if ! [ -z "$2" ]; then + includeExtensions="-e $( echo "$2" | sed -r 's/,/ -E /g' )" + fi + shift 2 + ;; + + h | --help ) + echo -e "$bashInfo Find all .c and .h files with the Amazon copyright in them $bashEnd" + echo -e "$bashInfo It exports this to a bash array variable called \"files\" $bashEnd" + echo -e "$bashInfo This script can take in two optional arguments $bashEnd" + echo -e "$bashInfo --exclude-files: A comma seperated list of files to exclude $bashEnd" + echo -e "$bashInfo --exclude-dir: A comma seperated list of directories to exclude $bashEnd" + echo -e "$bashInfo --include-extensions: Any additional exstensions to search for $bashEnd" + exit 0 + ;; + -- ) + shift + break + ;; + esac + done + fi + +elif [[ "$OSTYPE" == "darwin"* ]]; then + while [ $# -gt 0 ]; do + case "$1" in + --exclude-dirs=* ) + excludeDirs="-E $(echo "${1#*=}" | sed -E 's/,/ -E /g')" + shift + ;; + --exclude-files=* ) + excludeFiles="-E $(echo "${1#*=}" | sed -E 's/,/ -E /g')" + shift + ;; + --include-extensions=* ) + includeExtensions="-e $(echo "${1#*=}" | sed -E 's/,/ -e /g')" + shift + ;; + -h | --help ) + echo "Usage: $0 [options]" + echo "Options:" + echo " --exclude-dirs= Comma separated list of directories to exclude" + echo " --exclude-files= Comma separated list of files to exclude" + echo " --include-extensions= Comma separated list of additional extensions" + echo " -h, --help Display this help message" + exit 0 + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac + done +fi +# We know we just want the .c and .h files +# It's just way easier to do this then it is to write +# A complicated thing to handle parsing the arguments +#echo "Hello" +#echo "fdfind -e c -e h "$excludeDirs" "$excludeFiles" "$includeExtensions" --exec grep -lriE \"copyright (.*) [0-9]{4} amazon.com\" " +if [[ "$OSTYPE" == "linux-gnu"* ]]; then + files=$(fdfind -e c -e h $excludeDirs $excludeFiles $includeExtensions --exec grep -lriE "copyright (.*) [0-9]{4} amazon.com" ) +else + files=$(fd -e c -e h $excludeDirs $excludeFiles $includeExtensions --exec grep -lrie "copyright (.*) 20\d\d amazon.com" ) +fi + +finalFiles=("") +for file in ${files[@]}; do + if ! [ -z "$file" ]; then + echo "$file" + fi +done diff --git a/formatting/goodFiles/include/task.h b/formatting/goodFiles/include/task.h new file mode 100644 index 00000000..3edd544f --- /dev/null +++ b/formatting/goodFiles/include/task.h @@ -0,0 +1,3761 @@ +/* + * 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 + * + */ + +#ifndef INC_TASK_H +#define INC_TASK_H + +#ifndef INC_FREERTOS_H + #error \ + "include FreeRTOS.h must appear in source files before include task.h" +#endif + +#include "list.h" + +/* *INDENT-OFF* */ +#ifdef __cplusplus +extern "C" { +#endif +/* *INDENT-ON* */ + +/*----------------------------------------------------------- + * MACROS AND DEFINITIONS + *----------------------------------------------------------*/ + +/* + * If tskKERNEL_VERSION_NUMBER ends with + it represents the version in + * development after the numbered release. + * + * The tskKERNEL_VERSION_MAJOR, tskKERNEL_VERSION_MINOR, tskKERNEL_VERSION_BUILD + * values will reflect the last released version number. + */ +#define tskKERNEL_VERSION_NUMBER "V10.4.4+" +#define tskKERNEL_VERSION_MAJOR 10 +#define tskKERNEL_VERSION_MINOR 4 +#define tskKERNEL_VERSION_BUILD 4 + +/* MPU region parameters passed in ulParameters + * of MemoryRegion_t struct. */ +#define tskMPU_REGION_READ_ONLY ( 1UL << 0UL ) +#define tskMPU_REGION_READ_WRITE ( 1UL << 1UL ) +#define tskMPU_REGION_EXECUTE_NEVER ( 1UL << 2UL ) +#define tskMPU_REGION_NORMAL_MEMORY ( 1UL << 3UL ) +#define tskMPU_REGION_DEVICE_MEMORY ( 1UL << 4UL ) + +/* MPU region permissions stored in MPU settings to + * authorize access requests. */ +#define tskMPU_READ_PERMISSION ( 1UL << 0UL ) +#define tskMPU_WRITE_PERMISSION ( 1UL << 1UL ) + +/* The direct to task notification feature used to have only a single + * notification per task. Now there is an array of notifications per task that + * is dimensioned by configTASK_NOTIFICATION_ARRAY_ENTRIES. For backward + * compatibility, any use of the original direct to task notification defaults + * to using the first index in the array. */ +#define tskDEFAULT_INDEX_TO_NOTIFY ( 0 ) + +/** + * task. h + * + * Type by which tasks are referenced. For example, a call to xTaskCreate + * returns (via a pointer parameter) an TaskHandle_t variable that can then + * be used as a parameter to vTaskDelete to delete the task. + * + * \defgroup TaskHandle_t TaskHandle_t + * \ingroup Tasks + */ +struct tskTaskControlBlock; /* The old naming convention is used to prevent + breaking kernel aware debuggers. */ +typedef struct tskTaskControlBlock * TaskHandle_t; +typedef const struct tskTaskControlBlock * ConstTaskHandle_t; + +/* + * Defines the prototype to which the application task hook function must + * conform. + */ +typedef BaseType_t ( *TaskHookFunction_t )( void * ); + +/* Task states returned by eTaskGetState. */ +typedef enum +{ + eRunning = 0, /* A task is querying the state of itself, so must be running. + */ + eReady, /* The task being queried is in a ready or pending ready list. */ + eBlocked, /* The task being queried is in the Blocked state. */ + eSuspended, /* The task being queried is in the Suspended state, or is in + the Blocked state with an infinite time out. */ + eDeleted, /* The task being queried has been deleted, but its TCB has not + yet been freed. */ + eInvalid /* Used as an 'invalid state' value. */ +} eTaskState; + +/* Actions that can be performed when vTaskNotify() is called. */ +typedef enum +{ + eNoAction = 0, /* Notify the task without updating its notify value. */ + eSetBits, /* Set bits in the task's notification value. */ + eIncrement, /* Increment the task's notification value. */ + eSetValueWithOverwrite, /* Set the task's notification value to a specific + value even if the previous value has not yet been + read by the task. */ + eSetValueWithoutOverwrite /* Set the task's notification value if the + previous value has been read by the task. */ +} eNotifyAction; + +/* + * Used internally only. + */ +typedef struct xTIME_OUT +{ + BaseType_t xOverflowCount; + TickType_t xTimeOnEntering; +} TimeOut_t; + +/* + * Defines the memory ranges allocated to the task when an MPU is used. + */ +typedef struct xMEMORY_REGION +{ + void * pvBaseAddress; + uint32_t ulLengthInBytes; + uint32_t ulParameters; +} MemoryRegion_t; + +/* + * Parameters required to create an MPU protected task. + */ +typedef struct xTASK_PARAMETERS +{ + TaskFunction_t pvTaskCode; + const char * pcName; /*lint !e971 Unqualified char types are allowed for + strings and single characters only. */ + configSTACK_DEPTH_TYPE usStackDepth; + void * pvParameters; + UBaseType_t uxPriority; + StackType_t * puxStackBuffer; + MemoryRegion_t xRegions[ portNUM_CONFIGURABLE_REGIONS ]; +#if( ( portUSING_MPU_WRAPPERS == 1 ) && \ + ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + StaticTask_t * const pxTaskBuffer; +#endif +} TaskParameters_t; + +/* Used with the uxTaskGetSystemState() function to return the state of each + * task in the system. */ +typedef struct xTASK_STATUS +{ + TaskHandle_t xHandle; /* The handle of the task to which the rest of the + information in the structure relates. */ + const char * pcTaskName; /* A pointer to the task's name. This value will be invalid if the task was deleted since the structure was populated! */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + UBaseType_t xTaskNumber; /* A number unique to the task. */ + eTaskState eCurrentState; /* The state in which the task existed when the + structure was populated. */ + UBaseType_t uxCurrentPriority; /* The priority at which the task was running + (may be inherited) when the structure was + populated. */ + UBaseType_t uxBasePriority; /* The priority to which the task will return if + the task's current priority has been + inherited to avoid unbounded priority + inversion when obtaining a mutex. Only valid + if configUSE_MUTEXES is defined as 1 in + FreeRTOSConfig.h. */ + configRUN_TIME_COUNTER_TYPE + ulRunTimeCounter; /* The total run time + allocated to the task so + far, as defined by the run + time stats clock. See + https://www.FreeRTOS.org/rtos-run-time-stats.html. + Only valid when + configGENERATE_RUN_TIME_STATS + is defined as 1 in + FreeRTOSConfig.h. */ + StackType_t * pxStackBase; /* Points to the lowest address of the task's + stack area. */ +#if( ( portSTACK_GROWTH > 0 ) && ( configRECORD_STACK_HIGH_ADDRESS == 1 ) ) + StackType_t * pxTopOfStack; /* Points to the top address of the task's stack + area. */ + StackType_t * pxEndOfStack; /* Points to the end address of the task's stack + area. */ +#endif + configSTACK_DEPTH_TYPE usStackHighWaterMark; /* The minimum amount of stack + space that has remained for + the task since the task was + created. The closer this + value is to zero the closer + the task has come to + overflowing its stack. */ +#if( ( configUSE_CORE_AFFINITY == 1 ) && ( configNUMBER_OF_CORES > 1 ) ) + UBaseType_t uxCoreAffinityMask; /* The core affinity mask for the task */ +#endif +} TaskStatus_t; + +/* Possible return values for eTaskConfirmSleepModeStatus(). */ +typedef enum +{ + eAbortSleep = 0, /* A task has been made ready or a context switch pended + since portSUPPRESS_TICKS_AND_SLEEP() was called - abort + entering a sleep mode. */ + eStandardSleep, /* Enter a sleep mode that will not last any longer than the + expected idle time. */ +#if( INCLUDE_vTaskSuspend == 1 ) + eNoTasksWaitingTimeout /* No tasks are waiting for a timeout so it is safe + to enter a sleep mode that can only be exited by + an external interrupt. */ +#endif /* INCLUDE_vTaskSuspend */ +} eSleepModeStatus; + +/** + * Defines the priority used by the idle task. This must not be modified. + * + * \ingroup TaskUtils + */ +#define tskIDLE_PRIORITY ( ( UBaseType_t ) 0U ) + +/** + * Defines affinity to all available cores. + * + * \ingroup TaskUtils + */ +#define tskNO_AFFINITY ( ( UBaseType_t ) -1 ) + +/** + * task. h + * + * Macro for forcing a context switch. + * + * \defgroup taskYIELD taskYIELD + * \ingroup SchedulerControl + */ +#define taskYIELD() portYIELD() + +/** + * task. h + * + * Macro to mark the start of a critical code region. Preemptive context + * switches cannot occur when in a critical region. + * + * NOTE: This may alter the stack (depending on the portable implementation) + * so must be used with care! + * + * \defgroup taskENTER_CRITICAL taskENTER_CRITICAL + * \ingroup SchedulerControl + */ +#define taskENTER_CRITICAL() portENTER_CRITICAL() +#if( configNUMBER_OF_CORES == 1 ) + #define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR() +#else + #define taskENTER_CRITICAL_FROM_ISR() portENTER_CRITICAL_FROM_ISR() +#endif + +/** + * task. h + * + * Macro to mark the end of a critical code region. Preemptive context + * switches cannot occur when in a critical region. + * + * NOTE: This may alter the stack (depending on the portable implementation) + * so must be used with care! + * + * \defgroup taskEXIT_CRITICAL taskEXIT_CRITICAL + * \ingroup SchedulerControl + */ +#define taskEXIT_CRITICAL() portEXIT_CRITICAL() +#if( configNUMBER_OF_CORES == 1 ) + #define taskEXIT_CRITICAL_FROM_ISR( x ) \ + portCLEAR_INTERRUPT_MASK_FROM_ISR( x ) +#else + #define taskEXIT_CRITICAL_FROM_ISR( x ) portEXIT_CRITICAL_FROM_ISR( x ) +#endif + +/** + * task. h + * + * Macro to disable all maskable interrupts. + * + * \defgroup taskDISABLE_INTERRUPTS taskDISABLE_INTERRUPTS + * \ingroup SchedulerControl + */ +#define taskDISABLE_INTERRUPTS() portDISABLE_INTERRUPTS() + +/** + * task. h + * + * Macro to enable microcontroller interrupts. + * + * \defgroup taskENABLE_INTERRUPTS taskENABLE_INTERRUPTS + * \ingroup SchedulerControl + */ +#define taskENABLE_INTERRUPTS() portENABLE_INTERRUPTS() + +/* Definitions returned by xTaskGetSchedulerState(). taskSCHEDULER_SUSPENDED is + * 0 to generate more optimal code when configASSERT() is defined as the + * constant is used in assert() statements. */ +#define taskSCHEDULER_SUSPENDED ( ( BaseType_t ) 0 ) +#define taskSCHEDULER_NOT_STARTED ( ( BaseType_t ) 1 ) +#define taskSCHEDULER_RUNNING ( ( BaseType_t ) 2 ) + +/* Checks if core ID is valid. */ +#define taskVALID_CORE_ID( xCoreID ) \ + ( ( ( ( ( BaseType_t ) 0 <= ( xCoreID ) ) && \ + ( ( xCoreID ) < ( BaseType_t ) configNUMBER_OF_CORES ) ) ) \ + ? ( pdTRUE ) \ + : ( pdFALSE ) ) + +/*----------------------------------------------------------- + * TASK CREATION API + *----------------------------------------------------------*/ + +/** + * task. h + * @code{c} + * BaseType_t xTaskCreate( + * TaskFunction_t pxTaskCode, + * const char *pcName, + * configSTACK_DEPTH_TYPE usStackDepth, + * void *pvParameters, + * UBaseType_t uxPriority, + * TaskHandle_t *pxCreatedTask + * ); + * @endcode + * + * Create a new task and add it to the list of tasks that are ready to run. + * + * Internally, within the FreeRTOS implementation, tasks use two blocks of + * memory. The first block is used to hold the task's data structures. The + * second block is used by the task as its stack. If a task is created using + * xTaskCreate() then both blocks of memory are automatically dynamically + * allocated inside the xTaskCreate() function. (see + * https://www.FreeRTOS.org/a00111.html). If a task is created using + * xTaskCreateStatic() then the application writer must provide the required + * memory. xTaskCreateStatic() therefore allows a task to be created without + * using any dynamic memory allocation. + * + * See xTaskCreateStatic() for a version that does not use any dynamic memory + * allocation. + * + * xTaskCreate() can only be used to create a task that has unrestricted + * access to the entire microcontroller memory map. Systems that include MPU + * support can alternatively create an MPU constrained task using + * xTaskCreateRestricted(). + * + * @param pxTaskCode Pointer to the task entry function. Tasks + * must be implemented to never return (i.e. continuous loop). + * + * @param pcName A descriptive name for the task. This is mainly used to + * facilitate debugging. Max length defined by configMAX_TASK_NAME_LEN - + * default is 16. + * + * @param usStackDepth The size of the task stack specified as the number of + * variables the stack can hold - not the number of bytes. For example, if + * the stack is 16 bits wide and usStackDepth is defined as 100, 200 bytes + * will be allocated for stack storage. + * + * @param pvParameters Pointer that will be used as the parameter for the task + * being created. + * + * @param uxPriority The priority at which the task should run. Systems that + * include MPU support can optionally create tasks in a privileged (system) + * mode by setting bit portPRIVILEGE_BIT of the priority parameter. For + * example, to create a privileged task at priority 2 the uxPriority parameter + * should be set to ( 2 | portPRIVILEGE_BIT ). + * + * @param pxCreatedTask Used to pass back a handle by which the created task + * can be referenced. + * + * @return pdPASS if the task was successfully created and added to a ready + * list, otherwise an error code defined in the file projdefs.h + * + * Example usage: + * @code{c} + * // Task to be created. + * void vTaskCode( void * pvParameters ) + * { + * for( ;; ) + * { + * // Task code goes here. + * } + * } + * + * // Function that creates a task. + * void vOtherFunction( void ) + * { + * static uint8_t ucParameterToPass; + * TaskHandle_t xHandle = NULL; + * + * // Create the task, storing the handle. Note that the passed parameter + * ucParameterToPass + * // must exist for the lifetime of the task, so in this case is declared + * static. If it was just an + * // an automatic stack variable it might no longer exist, or at least have + * been corrupted, by the time + * // the new task attempts to access it. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, &ucParameterToPass, + * tskIDLE_PRIORITY, &xHandle ); configASSERT( xHandle ); + * + * // Use the handle to delete the task. + * if( xHandle != NULL ) + * { + * vTaskDelete( xHandle ); + * } + * } + * @endcode + * \defgroup xTaskCreate xTaskCreate + * \ingroup Tasks + */ +#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 ) PRIVILEGED_FUNCTION; +#endif + +#if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && \ + ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) +BaseType_t xTaskCreateAffinitySet( + 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, + UBaseType_t uxCoreAffinityMask, + TaskHandle_t * const pxCreatedTask ) PRIVILEGED_FUNCTION; +#endif + +/** + * task. h + * @code{c} + * TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode, + * const char *pcName, + * uint32_t ulStackDepth, + * void *pvParameters, + * UBaseType_t uxPriority, + * StackType_t *puxStackBuffer, + * StaticTask_t *pxTaskBuffer ); + * @endcode + * + * Create a new task and add it to the list of tasks that are ready to run. + * + * Internally, within the FreeRTOS implementation, tasks use two blocks of + * memory. The first block is used to hold the task's data structures. The + * second block is used by the task as its stack. If a task is created using + * xTaskCreate() then both blocks of memory are automatically dynamically + * allocated inside the xTaskCreate() function. (see + * https://www.FreeRTOS.org/a00111.html). If a task is created using + * xTaskCreateStatic() then the application writer must provide the required + * memory. xTaskCreateStatic() therefore allows a task to be created without + * using any dynamic memory allocation. + * + * @param pxTaskCode Pointer to the task entry function. Tasks + * must be implemented to never return (i.e. continuous loop). + * + * @param pcName A descriptive name for the task. This is mainly used to + * facilitate debugging. The maximum length of the string is defined by + * configMAX_TASK_NAME_LEN in FreeRTOSConfig.h. + * + * @param ulStackDepth The size of the task stack specified as the number of + * variables the stack can hold - not the number of bytes. For example, if + * the stack is 32-bits wide and ulStackDepth is defined as 100 then 400 bytes + * will be allocated for stack storage. + * + * @param pvParameters Pointer that will be used as the parameter for the task + * being created. + * + * @param uxPriority The priority at which the task will run. + * + * @param puxStackBuffer Must point to a StackType_t array that has at least + * ulStackDepth indexes - the array will then be used as the task's stack, + * removing the need for the stack to be allocated dynamically. + * + * @param pxTaskBuffer Must point to a variable of type StaticTask_t, which will + * then be used to hold the task's data structures, removing the need for the + * memory to be allocated dynamically. + * + * @return If neither puxStackBuffer nor pxTaskBuffer are NULL, then the task + * will be created and a handle to the created task is returned. If either + * puxStackBuffer or pxTaskBuffer are NULL then the task will not be created and + * NULL is returned. + * + * Example usage: + * @code{c} + * + * // Dimensions of the buffer that the task being created will use as its + stack. + * // NOTE: This is the number of words the stack will hold, not the number of + * // bytes. For example, if each stack item is 32-bits, and this is set to + 100, + * // then 400 bytes (100 * 32-bits) will be allocated. + #define STACK_SIZE 200 + * + * // Structure that will hold the TCB of the task being created. + * StaticTask_t xTaskBuffer; + * + * // Buffer that the task being created will use as its stack. Note this is + * // an array of StackType_t variables. The size of StackType_t is dependent + on + * // the RTOS port. + * StackType_t xStack[ STACK_SIZE ]; + * + * // Function that implements the task being created. + * void vTaskCode( void * pvParameters ) + * { + * // The parameter value is expected to be 1 as 1 is passed in the + * // pvParameters value in the call to xTaskCreateStatic(). + * configASSERT( ( uint32_t ) pvParameters == 1UL ); + * + * for( ;; ) + * { + * // Task code goes here. + * } + * } + * + * // Function that creates a task. + * void vOtherFunction( void ) + * { + * TaskHandle_t xHandle = NULL; + * + * // Create the task without using any dynamic memory allocation. + * xHandle = xTaskCreateStatic( + * vTaskCode, // Function that implements the task. + * "NAME", // Text name for the task. + * STACK_SIZE, // Stack size in words, not bytes. + * ( void * ) 1, // Parameter passed into the task. + * tskIDLE_PRIORITY,// Priority at which the task is created. + * xStack, // Array to use as the task's stack. + * &xTaskBuffer ); // Variable to hold the task's data + structure. + * + * // puxStackBuffer and pxTaskBuffer were not NULL, so the task will have + * // been created, and xHandle will be the task's handle. Use the handle + * // to suspend the task. + * vTaskSuspend( xHandle ); + * } + * @endcode + * \defgroup xTaskCreateStatic xTaskCreateStatic + * \ingroup Tasks + */ +#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 ) PRIVILEGED_FUNCTION; +#endif /* configSUPPORT_STATIC_ALLOCATION */ + +#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && \ + ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) +TaskHandle_t xTaskCreateStaticAffinitySet( + 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, + UBaseType_t uxCoreAffinityMask ) PRIVILEGED_FUNCTION; +#endif + +/** + * task. h + * @code{c} + * BaseType_t xTaskCreateRestricted( TaskParameters_t *pxTaskDefinition, + * TaskHandle_t *pxCreatedTask ); + * @endcode + * + * Only available when configSUPPORT_DYNAMIC_ALLOCATION is set to 1. + * + * xTaskCreateRestricted() should only be used in systems that include an MPU + * implementation. + * + * Create a new task and add it to the list of tasks that are ready to run. + * The function parameters define the memory regions and associated access + * permissions allocated to the task. + * + * See xTaskCreateRestrictedStatic() for a version that does not use any + * dynamic memory allocation. + * + * @param pxTaskDefinition Pointer to a structure that contains a member + * for each of the normal xTaskCreate() parameters (see the xTaskCreate() API + * documentation) plus an optional stack buffer and the memory region + * definitions. + * + * @param pxCreatedTask Used to pass back a handle by which the created task + * can be referenced. + * + * @return pdPASS if the task was successfully created and added to a ready + * list, otherwise an error code defined in the file projdefs.h + * + * Example usage: + * @code{c} + * // Create an TaskParameters_t structure that defines the task to be created. + * static const TaskParameters_t xCheckTaskParameters = + * { + * vATask, // pvTaskCode - the function that implements the task. + * "ATask", // pcName - just a text name for the task to assist debugging. + * 100, // usStackDepth - the stack size DEFINED IN WORDS. + * NULL, // pvParameters - passed into the task function as the function + * parameters. ( 1UL | portPRIVILEGE_BIT ),// uxPriority - task priority, set + * the portPRIVILEGE_BIT if the task should run in a privileged state. + * cStackBuffer,// puxStackBuffer - the buffer to be used as the task stack. + * + * // xRegions - Allocate up to three separate memory regions for access by + * // the task, with appropriate access permissions. Different processors have + * // different memory alignment requirements - refer to the FreeRTOS + * documentation + * // for full information. + * { + * // Base address Length Parameters + * { cReadWriteArray, 32, portMPU_REGION_READ_WRITE }, + * { cReadOnlyArray, 32, portMPU_REGION_READ_ONLY }, + * { cPrivilegedOnlyAccessArray, 128, + * portMPU_REGION_PRIVILEGED_READ_WRITE } + * } + * }; + * + * int main( void ) + * { + * TaskHandle_t xHandle; + * + * // Create a task from the const structure defined above. The task handle + * // is requested (the second parameter is not NULL) but in this case just for + * // demonstration purposes as its not actually used. + * xTaskCreateRestricted( &xRegTest1Parameters, &xHandle ); + * + * // Start the scheduler. + * vTaskStartScheduler(); + * + * // Will only get here if there was insufficient memory to create the idle + * // and/or timer task. + * for( ;; ); + * } + * @endcode + * \defgroup xTaskCreateRestricted xTaskCreateRestricted + * \ingroup Tasks + */ +#if( portUSING_MPU_WRAPPERS == 1 ) +BaseType_t xTaskCreateRestricted( + const TaskParameters_t * const pxTaskDefinition, + TaskHandle_t * pxCreatedTask ) PRIVILEGED_FUNCTION; +#endif + +#if( ( portUSING_MPU_WRAPPERS == 1 ) && ( configNUMBER_OF_CORES > 1 ) && \ + ( configUSE_CORE_AFFINITY == 1 ) ) +BaseType_t xTaskCreateRestrictedAffinitySet( + const TaskParameters_t * const pxTaskDefinition, + UBaseType_t uxCoreAffinityMask, + TaskHandle_t * pxCreatedTask ) PRIVILEGED_FUNCTION; +#endif + +/** + * task. h + * @code{c} + * BaseType_t xTaskCreateRestrictedStatic( TaskParameters_t *pxTaskDefinition, + * TaskHandle_t *pxCreatedTask ); + * @endcode + * + * Only available when configSUPPORT_STATIC_ALLOCATION is set to 1. + * + * xTaskCreateRestrictedStatic() should only be used in systems that include an + * MPU implementation. + * + * Internally, within the FreeRTOS implementation, tasks use two blocks of + * memory. The first block is used to hold the task's data structures. The + * second block is used by the task as its stack. If a task is created using + * xTaskCreateRestricted() then the stack is provided by the application writer, + * and the memory used to hold the task's data structure is automatically + * dynamically allocated inside the xTaskCreateRestricted() function. If a task + * is created using xTaskCreateRestrictedStatic() then the application writer + * must provide the memory used to hold the task's data structures too. + * xTaskCreateRestrictedStatic() therefore allows a memory protected task to be + * created without using any dynamic memory allocation. + * + * @param pxTaskDefinition Pointer to a structure that contains a member + * for each of the normal xTaskCreate() parameters (see the xTaskCreate() API + * documentation) plus an optional stack buffer and the memory region + * definitions. If configSUPPORT_STATIC_ALLOCATION is set to 1 the structure + * contains an additional member, which is used to point to a variable of type + * StaticTask_t - which is then used to hold the task's data structure. + * + * @param pxCreatedTask Used to pass back a handle by which the created task + * can be referenced. + * + * @return pdPASS if the task was successfully created and added to a ready + * list, otherwise an error code defined in the file projdefs.h + * + * Example usage: + * @code{c} + * // Create an TaskParameters_t structure that defines the task to be created. + * // The StaticTask_t variable is only included in the structure when + * // configSUPPORT_STATIC_ALLOCATION is set to 1. The PRIVILEGED_DATA macro + * can + * // be used to force the variable into the RTOS kernel's privileged data area. + * static PRIVILEGED_DATA StaticTask_t xTaskBuffer; + * static const TaskParameters_t xCheckTaskParameters = + * { + * vATask, // pvTaskCode - the function that implements the task. + * "ATask", // pcName - just a text name for the task to assist debugging. + * 100, // usStackDepth - the stack size DEFINED IN WORDS. + * NULL, // pvParameters - passed into the task function as the function + * parameters. ( 1UL | portPRIVILEGE_BIT ),// uxPriority - task priority, set + * the portPRIVILEGE_BIT if the task should run in a privileged state. + * cStackBuffer,// puxStackBuffer - the buffer to be used as the task stack. + * + * // xRegions - Allocate up to three separate memory regions for access by + * // the task, with appropriate access permissions. Different processors have + * // different memory alignment requirements - refer to the FreeRTOS + * documentation + * // for full information. + * { + * // Base address Length Parameters + * { cReadWriteArray, 32, portMPU_REGION_READ_WRITE }, + * { cReadOnlyArray, 32, portMPU_REGION_READ_ONLY }, + * { cPrivilegedOnlyAccessArray, 128, + * portMPU_REGION_PRIVILEGED_READ_WRITE } + * } + * + * &xTaskBuffer; // Holds the task's data structure. + * }; + * + * int main( void ) + * { + * TaskHandle_t xHandle; + * + * // Create a task from the const structure defined above. The task handle + * // is requested (the second parameter is not NULL) but in this case just for + * // demonstration purposes as its not actually used. + * xTaskCreateRestrictedStatic( &xRegTest1Parameters, &xHandle ); + * + * // Start the scheduler. + * vTaskStartScheduler(); + * + * // Will only get here if there was insufficient memory to create the idle + * // and/or timer task. + * for( ;; ); + * } + * @endcode + * \defgroup xTaskCreateRestrictedStatic xTaskCreateRestrictedStatic + * \ingroup Tasks + */ +#if( ( portUSING_MPU_WRAPPERS == 1 ) && \ + ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) +BaseType_t xTaskCreateRestrictedStatic( + const TaskParameters_t * const pxTaskDefinition, + TaskHandle_t * pxCreatedTask ) PRIVILEGED_FUNCTION; +#endif + +#if( ( portUSING_MPU_WRAPPERS == 1 ) && \ + ( configSUPPORT_STATIC_ALLOCATION == 1 ) && \ + ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) +BaseType_t xTaskCreateRestrictedStaticAffinitySet( + const TaskParameters_t * const pxTaskDefinition, + UBaseType_t uxCoreAffinityMask, + TaskHandle_t * pxCreatedTask ) PRIVILEGED_FUNCTION; +#endif + +/** + * task. h + * @code{c} + * void vTaskAllocateMPURegions( TaskHandle_t xTask, const MemoryRegion_t * + * const pxRegions ); + * @endcode + * + * Memory regions are assigned to a restricted task when the task is created by + * a call to xTaskCreateRestricted(). These regions can be redefined using + * vTaskAllocateMPURegions(). + * + * @param xTask The handle of the task being updated. + * + * @param[in] pxRegions A pointer to a MemoryRegion_t structure that contains + * the new memory region definitions. + * + * Example usage: + * @code{c} + * // Define an array of MemoryRegion_t structures that configures an MPU region + * // allowing read/write access for 1024 bytes starting at the beginning of the + * // ucOneKByte array. The other two of the maximum 3 definable regions are + * // unused so set to zero. + * static const MemoryRegion_t xAltRegions[ portNUM_CONFIGURABLE_REGIONS ] = + * { + * // Base address Length Parameters + * { ucOneKByte, 1024, portMPU_REGION_READ_WRITE }, + * { 0, 0, 0 }, + * { 0, 0, 0 } + * }; + * + * void vATask( void *pvParameters ) + * { + * // This task was created such that it has access to certain regions of + * // memory as defined by the MPU configuration. At some point it is + * // desired that these MPU regions are replaced with that defined in the + * // xAltRegions const struct above. Use a call to vTaskAllocateMPURegions() + * // for this purpose. NULL is used as the task handle to indicate that this + * // function should modify the MPU regions of the calling task. + * vTaskAllocateMPURegions( NULL, xAltRegions ); + * + * // Now the task can continue its function, but from this point on can only + * // access its stack and the ucOneKByte array (unless any other statically + * // defined or shared regions have been declared elsewhere). + * } + * @endcode + * \defgroup vTaskAllocateMPURegions vTaskAllocateMPURegions + * \ingroup Tasks + */ +void vTaskAllocateMPURegions( TaskHandle_t xTaskToModify, + const MemoryRegion_t * const pxRegions ) + PRIVILEGED_FUNCTION; + +/** + * task. h + * @code{c} + * void vTaskDelete( TaskHandle_t xTaskToDelete ); + * @endcode + * + * INCLUDE_vTaskDelete must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Remove a task from the RTOS real time kernel's management. The task being + * deleted will be removed from all ready, blocked, suspended and event lists. + * + * NOTE: The idle task is responsible for freeing the kernel allocated + * memory from tasks that have been deleted. It is therefore important that + * the idle task is not starved of microcontroller processing time if your + * application makes any calls to vTaskDelete (). Memory allocated by the + * task code is not automatically freed, and should be freed before the task + * is deleted. + * + * See the demo application file death.c for sample code that utilises + * vTaskDelete (). + * + * @param xTaskToDelete The handle of the task to be deleted. Passing NULL will + * cause the calling task to be deleted. + * + * Example usage: + * @code{c} + * void vOtherFunction( void ) + * { + * TaskHandle_t xHandle; + * + * // Create the task, storing the handle. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, + * &xHandle ); + * + * // Use the handle to delete the task. + * vTaskDelete( xHandle ); + * } + * @endcode + * \defgroup vTaskDelete vTaskDelete + * \ingroup Tasks + */ +void vTaskDelete( TaskHandle_t xTaskToDelete ) PRIVILEGED_FUNCTION; + +/*----------------------------------------------------------- + * TASK CONTROL API + *----------------------------------------------------------*/ + +/** + * task. h + * @code{c} + * void vTaskDelay( const TickType_t xTicksToDelay ); + * @endcode + * + * Delay a task for a given number of ticks. The actual time that the + * task remains blocked depends on the tick rate. The constant + * portTICK_PERIOD_MS can be used to calculate real time from the tick + * rate - with the resolution of one tick period. + * + * INCLUDE_vTaskDelay must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * + * vTaskDelay() specifies a time at which the task wishes to unblock relative to + * the time at which vTaskDelay() is called. For example, specifying a block + * period of 100 ticks will cause the task to unblock 100 ticks after + * vTaskDelay() is called. vTaskDelay() does not therefore provide a good + * method of controlling the frequency of a periodic task as the path taken + * through the code, as well as other task and interrupt activity, will affect + * the frequency at which vTaskDelay() gets called and therefore the time at + * which the task next executes. See xTaskDelayUntil() for an alternative API + * function designed to facilitate fixed frequency execution. It does this by + * specifying an absolute time (rather than a relative time) at which the + * calling task should unblock. + * + * @param xTicksToDelay The amount of time, in tick periods, that + * the calling task should block. + * + * Example usage: + * + * void vTaskFunction( void * pvParameters ) + * { + * // Block for 500ms. + * const TickType_t xDelay = 500 / portTICK_PERIOD_MS; + * + * for( ;; ) + * { + * // Simply toggle the LED every 500ms, blocking between each toggle. + * vToggleLED(); + * vTaskDelay( xDelay ); + * } + * } + * + * \defgroup vTaskDelay vTaskDelay + * \ingroup TaskCtrl + */ +void vTaskDelay( const TickType_t xTicksToDelay ) PRIVILEGED_FUNCTION; + +/** + * task. h + * @code{c} + * BaseType_t xTaskDelayUntil( TickType_t *pxPreviousWakeTime, const TickType_t + * xTimeIncrement ); + * @endcode + * + * INCLUDE_xTaskDelayUntil must be defined as 1 for this function to be + * available. See the configuration section for more information. + * + * Delay a task until a specified time. This function can be used by periodic + * tasks to ensure a constant execution frequency. + * + * This function differs from vTaskDelay () in one important aspect: vTaskDelay + * () will cause a task to block for the specified number of ticks from the time + * vTaskDelay () is called. It is therefore difficult to use vTaskDelay () by + * itself to generate a fixed execution frequency as the time between a task + * starting to execute and that task calling vTaskDelay () may not be fixed [the + * task may take a different path though the code between calls, or may get + * interrupted or preempted a different number of times each time it executes]. + * + * Whereas vTaskDelay () specifies a wake time relative to the time at which the + * function is called, xTaskDelayUntil () specifies the absolute (exact) time at + * which it wishes to unblock. + * + * The macro pdMS_TO_TICKS() can be used to calculate the number of ticks from a + * time specified in milliseconds with a resolution of one tick period. + * + * @param pxPreviousWakeTime Pointer to a variable that holds the time at which + * the task was last unblocked. The variable must be initialised with the + * current time prior to its first use (see the example below). Following this + * the variable is automatically updated within xTaskDelayUntil (). + * + * @param xTimeIncrement The cycle time period. The task will be unblocked at + * time *pxPreviousWakeTime + xTimeIncrement. Calling xTaskDelayUntil with the + * same xTimeIncrement parameter value will cause the task to execute with + * a fixed interface period. + * + * @return Value which can be used to check whether the task was actually + * delayed. Will be pdTRUE if the task way delayed and pdFALSE otherwise. A + * task will not be delayed if the next expected wake time is in the past. + * + * Example usage: + * @code{c} + * // Perform an action every 10 ticks. + * void vTaskFunction( void * pvParameters ) + * { + * TickType_t xLastWakeTime; + * const TickType_t xFrequency = 10; + * BaseType_t xWasDelayed; + * + * // Initialise the xLastWakeTime variable with the current time. + * xLastWakeTime = xTaskGetTickCount (); + * for( ;; ) + * { + * // Wait for the next cycle. + * xWasDelayed = xTaskDelayUntil( &xLastWakeTime, xFrequency ); + * + * // Perform action here. xWasDelayed value can be used to determine + * // whether a deadline was missed if the code here took too long. + * } + * } + * @endcode + * \defgroup xTaskDelayUntil xTaskDelayUntil + * \ingroup TaskCtrl + */ +BaseType_t xTaskDelayUntil( TickType_t * const pxPreviousWakeTime, + const TickType_t xTimeIncrement ) + PRIVILEGED_FUNCTION; + +/* + * vTaskDelayUntil() is the older version of xTaskDelayUntil() and does not + * return a value. + */ +#define vTaskDelayUntil( pxPreviousWakeTime, xTimeIncrement ) \ + do \ + { \ + ( void ) xTaskDelayUntil( ( pxPreviousWakeTime ), \ + ( xTimeIncrement ) ); \ + } while( 0 ) + +/** + * task. h + * @code{c} + * BaseType_t xTaskAbortDelay( TaskHandle_t xTask ); + * @endcode + * + * INCLUDE_xTaskAbortDelay must be defined as 1 in FreeRTOSConfig.h for this + * function to be available. + * + * A task will enter the Blocked state when it is waiting for an event. The + * event it is waiting for can be a temporal event (waiting for a time), such + * as when vTaskDelay() is called, or an event on an object, such as when + * xQueueReceive() or ulTaskNotifyTake() is called. If the handle of a task + * that is in the Blocked state is used in a call to xTaskAbortDelay() then the + * task will leave the Blocked state, and return from whichever function call + * placed the task into the Blocked state. + * + * There is no 'FromISR' version of this function as an interrupt would need to + * know which object a task was blocked on in order to know which actions to + * take. For example, if the task was blocked on a queue the interrupt handler + * would then need to know if the queue was locked. + * + * @param xTask The handle of the task to remove from the Blocked state. + * + * @return If the task referenced by xTask was not in the Blocked state then + * pdFAIL is returned. Otherwise pdPASS is returned. + * + * \defgroup xTaskAbortDelay xTaskAbortDelay + * \ingroup TaskCtrl + */ +BaseType_t xTaskAbortDelay( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +/** + * task. h + * @code{c} + * UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask ); + * @endcode + * + * INCLUDE_uxTaskPriorityGet must be defined as 1 for this function to be + * available. See the configuration section for more information. + * + * Obtain the priority of any task. + * + * @param xTask Handle of the task to be queried. Passing a NULL + * handle results in the priority of the calling task being returned. + * + * @return The priority of xTask. + * + * Example usage: + * @code{c} + * void vAFunction( void ) + * { + * TaskHandle_t xHandle; + * + * // Create a task, storing the handle. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, + * &xHandle ); + * + * // ... + * + * // Use the handle to obtain the priority of the created task. + * // It was created with tskIDLE_PRIORITY, but may have changed + * // it itself. + * if( uxTaskPriorityGet( xHandle ) != tskIDLE_PRIORITY ) + * { + * // The task has changed it's priority. + * } + * + * // ... + * + * // Is our priority higher than the created task? + * if( uxTaskPriorityGet( xHandle ) < uxTaskPriorityGet( NULL ) ) + * { + * // Our priority (obtained using NULL handle) is higher. + * } + * } + * @endcode + * \defgroup uxTaskPriorityGet uxTaskPriorityGet + * \ingroup TaskCtrl + */ +UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +/** + * task. h + * @code{c} + * UBaseType_t uxTaskPriorityGetFromISR( const TaskHandle_t xTask ); + * @endcode + * + * A version of uxTaskPriorityGet() that can be used from an ISR. + */ +UBaseType_t uxTaskPriorityGetFromISR( const TaskHandle_t xTask ) + PRIVILEGED_FUNCTION; + +/** + * task. h + * @code{c} + * eTaskState eTaskGetState( TaskHandle_t xTask ); + * @endcode + * + * INCLUDE_eTaskGetState must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Obtain the state of any task. States are encoded by the eTaskState + * enumerated type. + * + * @param xTask Handle of the task to be queried. + * + * @return The state of xTask at the time the function was called. Note the + * state of the task might change between the function being called, and the + * functions return value being tested by the calling task. + */ +eTaskState eTaskGetState( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +/** + * task. h + * @code{c} + * void vTaskGetInfo( TaskHandle_t xTask, TaskStatus_t *pxTaskStatus, BaseType_t + * xGetFreeStackSpace, eTaskState eState ); + * @endcode + * + * configUSE_TRACE_FACILITY must be defined as 1 for this function to be + * available. See the configuration section for more information. + * + * Populates a TaskStatus_t structure with information about a task. + * + * @param xTask Handle of the task being queried. If xTask is NULL then + * information will be returned about the calling task. + * + * @param pxTaskStatus A pointer to the TaskStatus_t structure that will be + * filled with information about the task referenced by the handle passed using + * the xTask parameter. + * + * @param xGetFreeStackSpace The TaskStatus_t structure contains a member to + * report the stack high water mark of the task being queried. Calculating the + * stack high water mark takes a relatively long time, and can make the system + * temporarily unresponsive - so the xGetFreeStackSpace parameter is provided to + * allow the high water mark checking to be skipped. The high watermark value + * will only be written to the TaskStatus_t structure if xGetFreeStackSpace is + * not set to pdFALSE; + * + * @param eState The TaskStatus_t structure contains a member to report the + * state of the task being queried. Obtaining the task state is not as fast as + * a simple assignment - so the eState parameter is provided to allow the state + * information to be omitted from the TaskStatus_t structure. To obtain state + * information then set eState to eInvalid - otherwise the value passed in + * eState will be reported as the task state in the TaskStatus_t structure. + * + * Example usage: + * @code{c} + * void vAFunction( void ) + * { + * TaskHandle_t xHandle; + * TaskStatus_t xTaskDetails; + * + * // Obtain the handle of a task from its name. + * xHandle = xTaskGetHandle( "Task_Name" ); + * + * // Check the handle is not NULL. + * configASSERT( xHandle ); + * + * // Use the handle to obtain further information about the task. + * vTaskGetInfo( xHandle, + * &xTaskDetails, + * pdTRUE, // Include the high water mark in xTaskDetails. + * eInvalid ); // Include the task state in xTaskDetails. + * } + * @endcode + * \defgroup vTaskGetInfo vTaskGetInfo + * \ingroup TaskCtrl + */ +void vTaskGetInfo( TaskHandle_t xTask, + TaskStatus_t * pxTaskStatus, + BaseType_t xGetFreeStackSpace, + eTaskState eState ) PRIVILEGED_FUNCTION; + +/** + * task. h + * @code{c} + * void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority ); + * @endcode + * + * INCLUDE_vTaskPrioritySet must be defined as 1 for this function to be + * available. See the configuration section for more information. + * + * Set the priority of any task. + * + * A context switch will occur before the function returns if the priority + * being set is higher than the currently executing task. + * + * @param xTask Handle to the task for which the priority is being set. + * Passing a NULL handle results in the priority of the calling task being set. + * + * @param uxNewPriority The priority to which the task will be set. + * + * Example usage: + * @code{c} + * void vAFunction( void ) + * { + * TaskHandle_t xHandle; + * + * // Create a task, storing the handle. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, + * &xHandle ); + * + * // ... + * + * // Use the handle to raise the priority of the created task. + * vTaskPrioritySet( xHandle, tskIDLE_PRIORITY + 1 ); + * + * // ... + * + * // Use a NULL handle to raise our priority to the same value. + * vTaskPrioritySet( NULL, tskIDLE_PRIORITY + 1 ); + * } + * @endcode + * \defgroup vTaskPrioritySet vTaskPrioritySet + * \ingroup TaskCtrl + */ +void vTaskPrioritySet( TaskHandle_t xTask, + UBaseType_t uxNewPriority ) PRIVILEGED_FUNCTION; + +/** + * task. h + * @code{c} + * void vTaskSuspend( TaskHandle_t xTaskToSuspend ); + * @endcode + * + * INCLUDE_vTaskSuspend must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Suspend any task. When suspended a task will never get any microcontroller + * processing time, no matter what its priority. + * + * Calls to vTaskSuspend are not accumulative - + * i.e. calling vTaskSuspend () twice on the same task still only requires one + * call to vTaskResume () to ready the suspended task. + * + * @param xTaskToSuspend Handle to the task being suspended. Passing a NULL + * handle will cause the calling task to be suspended. + * + * Example usage: + * @code{c} + * void vAFunction( void ) + * { + * TaskHandle_t xHandle; + * + * // Create a task, storing the handle. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, + * &xHandle ); + * + * // ... + * + * // Use the handle to suspend the created task. + * vTaskSuspend( xHandle ); + * + * // ... + * + * // The created task will not run during this period, unless + * // another task calls vTaskResume( xHandle ). + * + * //... + * + * + * // Suspend ourselves. + * vTaskSuspend( NULL ); + * + * // We cannot get here unless another task calls vTaskResume + * // with our handle as the parameter. + * } + * @endcode + * \defgroup vTaskSuspend vTaskSuspend + * \ingroup TaskCtrl + */ +void vTaskSuspend( TaskHandle_t xTaskToSuspend ) PRIVILEGED_FUNCTION; + +/** + * task. h + * @code{c} + * void vTaskResume( TaskHandle_t xTaskToResume ); + * @endcode + * + * INCLUDE_vTaskSuspend must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Resumes a suspended task. + * + * A task that has been suspended by one or more calls to vTaskSuspend () + * will be made available for running again by a single call to + * vTaskResume (). + * + * @param xTaskToResume Handle to the task being readied. + * + * Example usage: + * @code{c} + * void vAFunction( void ) + * { + * TaskHandle_t xHandle; + * + * // Create a task, storing the handle. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, + * &xHandle ); + * + * // ... + * + * // Use the handle to suspend the created task. + * vTaskSuspend( xHandle ); + * + * // ... + * + * // The created task will not run during this period, unless + * // another task calls vTaskResume( xHandle ). + * + * //... + * + * + * // Resume the suspended task ourselves. + * vTaskResume( xHandle ); + * + * // The created task will once again get microcontroller processing + * // time in accordance with its priority within the system. + * } + * @endcode + * \defgroup vTaskResume vTaskResume + * \ingroup TaskCtrl + */ +void vTaskResume( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION; + +/** + * task. h + * @code{c} + * void xTaskResumeFromISR( TaskHandle_t xTaskToResume ); + * @endcode + * + * INCLUDE_xTaskResumeFromISR must be defined as 1 for this function to be + * available. See the configuration section for more information. + * + * An implementation of vTaskResume() that can be called from within an ISR. + * + * A task that has been suspended by one or more calls to vTaskSuspend () + * will be made available for running again by a single call to + * xTaskResumeFromISR (). + * + * xTaskResumeFromISR() should not be used to synchronise a task with an + * interrupt if there is a chance that the interrupt could arrive prior to the + * task being suspended - as this can lead to interrupts being missed. Use of a + * semaphore as a synchronisation mechanism would avoid this eventuality. + * + * @param xTaskToResume Handle to the task being readied. + * + * @return pdTRUE if resuming the task should result in a context switch, + * otherwise pdFALSE. This is used by the ISR to determine if a context switch + * may be required following the ISR. + * + * \defgroup vTaskResumeFromISR vTaskResumeFromISR + * \ingroup TaskCtrl + */ +BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION; + +#if( configUSE_CORE_AFFINITY == 1 ) + +/** + * @brief Sets the core affinity mask for a task. + * + * It sets the cores on which a task can run. configUSE_CORE_AFFINITY must + * be defined as 1 for this function to be available. + * + * @param xTask The handle of the task to set the core affinity mask for. + * Passing NULL will set the core affinity mask for the calling task. + * + * @param uxCoreAffinityMask A bitwise value that indicates the cores on + * which the task can run. Cores are numbered from 0 to configNUMBER_OF_CORES + * - 1. For example, to ensure that a task can run on core 0 and core 1, set + * uxCoreAffinityMask to 0x03. + * + * Example usage: + * + * // The function that creates task. + * void vAFunction( void ) + * { + * TaskHandle_t xHandle; + * UBaseType_t uxCoreAffinityMask; + * + * // Create a task, storing the handle. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &( + * xHandle ) ); + * + * // Define the core affinity mask such that this task can only run + * // on core 0 and core 2. + * uxCoreAffinityMask = ( ( 1 << 0 ) | ( 1 << 2 ) ); + * + * //Set the core affinity mask for the task. + * vTaskCoreAffinitySet( xHandle, uxCoreAffinityMask ); + * } + */ +void vTaskCoreAffinitySet( const TaskHandle_t xTask, + UBaseType_t uxCoreAffinityMask ); +#endif + +#if( configUSE_CORE_AFFINITY == 1 ) + +/** + * @brief Gets the core affinity mask for a task. + * + * configUSE_CORE_AFFINITY must be defined as 1 for this function to be + * available. + * + * @param xTask The handle of the task to get the core affinity mask for. + * Passing NULL will get the core affinity mask for the calling task. + * + * @return The core affinity mask which is a bitwise value that indicates + * the cores on which a task can run. Cores are numbered from 0 to + * configNUMBER_OF_CORES - 1. For example, if a task can run on core 0 and core + * 1, the core affinity mask is 0x03. + * + * Example usage: + * + * // Task handle of the networking task - it is populated elsewhere. + * TaskHandle_t xNetworkingTaskHandle; + * + * void vAFunction( void ) + * { + * TaskHandle_t xHandle; + * UBaseType_t uxNetworkingCoreAffinityMask; + * + * // Create a task, storing the handle. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &( + * xHandle ) ); + * + * //Get the core affinity mask for the networking task. + * uxNetworkingCoreAffinityMask = vTaskCoreAffinityGet( + * xNetworkingTaskHandle ); + * + * // Here is a hypothetical scenario, just for the example. Assume that we + * // have 2 cores - Core 0 and core 1. We want to pin the application task + * to + * // the core different than the networking task to ensure that the + * // application task does not interfere with networking. + * if( ( uxNetworkingCoreAffinityMask & ( 1 << 0 ) ) != 0 ) + * { + * // The networking task can run on core 0, pin our task to core 1. + * vTaskCoreAffinitySet( xHandle, ( 1 << 1 ) ); + * } + * else + * { + * // Otherwise, pin our task to core 0. + * vTaskCoreAffinitySet( xHandle, ( 1 << 0 ) ); + * } + * } + */ +UBaseType_t vTaskCoreAffinityGet( ConstTaskHandle_t xTask ); +#endif + +#if( configUSE_TASK_PREEMPTION_DISABLE == 1 ) + +/** + * @brief Disables preemption for a task. + * + * @param xTask The handle of the task to disable preemption. Passing NULL + * disables preemption for the calling task. + * + * Example usage: + * + * void vTaskCode( void *pvParameters ) + * { + * // Silence warnings about unused parameters. + * ( void ) pvParameters; + * + * for( ;; ) + * { + * // ... Perform some function here. + * + * // Disable preemption for this task. + * vTaskPreemptionDisable( NULL ); + * + * // The task will not be preempted when it is executing in this + * portion ... + * + * // ... until the preemption is enabled again. + * vTaskPreemptionEnable( NULL ); + * + * // The task can be preempted when it is executing in this portion. + * } + * } + */ +void vTaskPreemptionDisable( const TaskHandle_t xTask ); +#endif + +#if( configUSE_TASK_PREEMPTION_DISABLE == 1 ) + +/** + * @brief Enables preemption for a task. + * + * @param xTask The handle of the task to enable preemption. Passing NULL + * enables preemption for the calling task. + * + * Example usage: + * + * void vTaskCode( void *pvParameters ) + * { + * // Silence warnings about unused parameters. + * ( void ) pvParameters; + * + * for( ;; ) + * { + * // ... Perform some function here. + * + * // Disable preemption for this task. + * vTaskPreemptionDisable( NULL ); + * + * // The task will not be preempted when it is executing in this + * portion ... + * + * // ... until the preemption is enabled again. + * vTaskPreemptionEnable( NULL ); + * + * // The task can be preempted when it is executing in this portion. + * } + * } + */ +void vTaskPreemptionEnable( const TaskHandle_t xTask ); +#endif + +/*----------------------------------------------------------- + * SCHEDULER CONTROL + *----------------------------------------------------------*/ + +/** + * task. h + * @code{c} + * void vTaskStartScheduler( void ); + * @endcode + * + * Starts the real time kernel tick processing. After calling the kernel + * has control over which tasks are executed and when. + * + * See the demo application file main.c for an example of creating + * tasks and starting the kernel. + * + * Example usage: + * @code{c} + * void vAFunction( void ) + * { + * // Create at least one task before starting the kernel. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL ); + * + * // Start the real time kernel with preemption. + * vTaskStartScheduler (); + * + * // Will not get here unless a task calls vTaskEndScheduler () + * } + * @endcode + * + * \defgroup vTaskStartScheduler vTaskStartScheduler + * \ingroup SchedulerControl + */ +void vTaskStartScheduler( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + * @code{c} + * void vTaskEndScheduler( void ); + * @endcode + * + * NOTE: At the time of writing only the x86 real mode port, which runs on a PC + * in place of DOS, implements this function. + * + * Stops the real time kernel tick. All created tasks will be automatically + * deleted and multitasking (either preemptive or cooperative) will + * stop. Execution then resumes from the point where vTaskStartScheduler () + * was called, as if vTaskStartScheduler () had just returned. + * + * See the demo application file main. c in the demo/PC directory for an + * example that uses vTaskEndScheduler (). + * + * vTaskEndScheduler () requires an exit function to be defined within the + * portable layer (see vPortEndScheduler () in port. c for the PC port). This + * performs hardware specific operations such as stopping the kernel tick. + * + * vTaskEndScheduler () will cause all of the resources allocated by the + * kernel to be freed - but will not free resources allocated by application + * tasks. + * + * Example usage: + * @code{c} + * void vTaskCode( void * pvParameters ) + * { + * for( ;; ) + * { + * // Task code goes here. + * + * // At some point we want to end the real time kernel processing + * // so call ... + * vTaskEndScheduler (); + * } + * } + * + * void vAFunction( void ) + * { + * // Create at least one task before starting the kernel. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL ); + * + * // Start the real time kernel with preemption. + * vTaskStartScheduler (); + * + * // Will only get here when the vTaskCode () task has called + * // vTaskEndScheduler (). When we get here we are back to single task + * // execution. + * } + * @endcode + * + * \defgroup vTaskEndScheduler vTaskEndScheduler + * \ingroup SchedulerControl + */ +void vTaskEndScheduler( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + * @code{c} + * void vTaskSuspendAll( void ); + * @endcode + * + * Suspends the scheduler without disabling interrupts. Context switches will + * not occur while the scheduler is suspended. + * + * After calling vTaskSuspendAll () the calling task will continue to execute + * without risk of being swapped out until a call to xTaskResumeAll () has been + * made. + * + * API functions that have the potential to cause a context switch (for example, + * xTaskDelayUntil(), xQueueSend(), etc.) must not be called while the scheduler + * is suspended. + * + * Example usage: + * @code{c} + * void vTask1( void * pvParameters ) + * { + * for( ;; ) + * { + * // Task code goes here. + * + * // ... + * + * // At some point the task wants to perform a long operation during + * // which it does not want to get swapped out. It cannot use + * // taskENTER_CRITICAL ()/taskEXIT_CRITICAL () as the length of the + * // operation may cause interrupts to be missed - including the + * // ticks. + * + * // Prevent the real time kernel swapping out the task. + * vTaskSuspendAll (); + * + * // Perform the operation here. There is no need to use critical + * // sections as we have all the microcontroller processing time. + * // During this time interrupts will still operate and the kernel + * // tick count will be maintained. + * + * // ... + * + * // The operation is complete. Restart the kernel. + * xTaskResumeAll (); + * } + * } + * @endcode + * \defgroup vTaskSuspendAll vTaskSuspendAll + * \ingroup SchedulerControl + */ +void vTaskSuspendAll( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + * @code{c} + * BaseType_t xTaskResumeAll( void ); + * @endcode + * + * Resumes scheduler activity after it was suspended by a call to + * vTaskSuspendAll(). + * + * xTaskResumeAll() only resumes the scheduler. It does not unsuspend tasks + * that were previously suspended by a call to vTaskSuspend(). + * + * @return If resuming the scheduler caused a context switch then pdTRUE is + * returned, otherwise pdFALSE is returned. + * + * Example usage: + * @code{c} + * void vTask1( void * pvParameters ) + * { + * for( ;; ) + * { + * // Task code goes here. + * + * // ... + * + * // At some point the task wants to perform a long operation during + * // which it does not want to get swapped out. It cannot use + * // taskENTER_CRITICAL ()/taskEXIT_CRITICAL () as the length of the + * // operation may cause interrupts to be missed - including the + * // ticks. + * + * // Prevent the real time kernel swapping out the task. + * vTaskSuspendAll (); + * + * // Perform the operation here. There is no need to use critical + * // sections as we have all the microcontroller processing time. + * // During this time interrupts will still operate and the real + * // time kernel tick count will be maintained. + * + * // ... + * + * // The operation is complete. Restart the kernel. We want to force + * // a context switch - but there is no point if resuming the scheduler + * // caused a context switch already. + * if( !xTaskResumeAll () ) + * { + * taskYIELD (); + * } + * } + * } + * @endcode + * \defgroup xTaskResumeAll xTaskResumeAll + * \ingroup SchedulerControl + */ +BaseType_t xTaskResumeAll( void ) PRIVILEGED_FUNCTION; + +/*----------------------------------------------------------- + * TASK UTILITIES + *----------------------------------------------------------*/ + +/** + * task. h + * @code{c} + * TickType_t xTaskGetTickCount( void ); + * @endcode + * + * @return The count of ticks since vTaskStartScheduler was called. + * + * \defgroup xTaskGetTickCount xTaskGetTickCount + * \ingroup TaskUtils + */ +TickType_t xTaskGetTickCount( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + * @code{c} + * TickType_t xTaskGetTickCountFromISR( void ); + * @endcode + * + * @return The count of ticks since vTaskStartScheduler was called. + * + * This is a version of xTaskGetTickCount() that is safe to be called from an + * ISR - provided that TickType_t is the natural word size of the + * microcontroller being used or interrupt nesting is either not supported or + * not being used. + * + * \defgroup xTaskGetTickCountFromISR xTaskGetTickCountFromISR + * \ingroup TaskUtils + */ +TickType_t xTaskGetTickCountFromISR( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + * @code{c} + * uint16_t uxTaskGetNumberOfTasks( void ); + * @endcode + * + * @return The number of tasks that the real time kernel is currently managing. + * This includes all ready, blocked and suspended tasks. A task that + * has been deleted but not yet freed by the idle task will also be + * included in the count. + * + * \defgroup uxTaskGetNumberOfTasks uxTaskGetNumberOfTasks + * \ingroup TaskUtils + */ +UBaseType_t uxTaskGetNumberOfTasks( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + * @code{c} + * char *pcTaskGetName( TaskHandle_t xTaskToQuery ); + * @endcode + * + * @return The text (human readable) name of the task referenced by the handle + * xTaskToQuery. A task can query its own name by either passing in its own + * handle, or by setting xTaskToQuery to NULL. + * + * \defgroup pcTaskGetName pcTaskGetName + * \ingroup TaskUtils + */ +char * pcTaskGetName( TaskHandle_t xTaskToQuery ) + PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for + strings and single characters only. */ + +/** + * task. h + * @code{c} + * TaskHandle_t xTaskGetHandle( const char *pcNameToQuery ); + * @endcode + * + * NOTE: This function takes a relatively long time to complete and should be + * used sparingly. + * + * @return The handle of the task that has the human readable name + * pcNameToQuery. NULL is returned if no matching name is found. + * INCLUDE_xTaskGetHandle must be set to 1 in FreeRTOSConfig.h for + * pcTaskGetHandle() to be available. + * + * \defgroup pcTaskGetHandle pcTaskGetHandle + * \ingroup TaskUtils + */ +TaskHandle_t xTaskGetHandle( const char * pcNameToQuery ) + PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for + strings and single characters only. */ + +/** + * task. h + * @code{c} + * BaseType_t xTaskGetStaticBuffers( TaskHandle_t xTask, + * StackType_t ** ppuxStackBuffer, + * StaticTask_t ** ppxTaskBuffer ); + * @endcode + * + * Retrieve pointers to a statically created task's data structure + * buffer and stack buffer. These are the same buffers that are supplied + * at the time of creation. + * + * @param xTask The task for which to retrieve the buffers. + * + * @param ppuxStackBuffer Used to return a pointer to the task's stack buffer. + * + * @param ppxTaskBuffer Used to return a pointer to the task's data structure + * buffer. + * + * @return pdTRUE if buffers were retrieved, pdFALSE otherwise. + * + * \defgroup xTaskGetStaticBuffers xTaskGetStaticBuffers + * \ingroup TaskUtils + */ +#if( configSUPPORT_STATIC_ALLOCATION == 1 ) +BaseType_t xTaskGetStaticBuffers( TaskHandle_t xTask, + StackType_t ** ppuxStackBuffer, + StaticTask_t ** ppxTaskBuffer ) + PRIVILEGED_FUNCTION; +#endif /* configSUPPORT_STATIC_ALLOCATION */ + +/** + * task.h + * @code{c} + * UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask ); + * @endcode + * + * INCLUDE_uxTaskGetStackHighWaterMark must be set to 1 in FreeRTOSConfig.h for + * this function to be available. + * + * Returns the high water mark of the stack associated with xTask. That is, + * the minimum free stack space there has been (in words, so on a 32 bit machine + * a value of 1 means 4 bytes) since the task started. The smaller the returned + * number the closer the task has come to overflowing its stack. + * + * 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. + * + * @param xTask Handle of the task associated with the stack to be checked. + * Set xTask to NULL to check the stack of the calling task. + * + * @return The smallest amount of free stack space there has been (in words, so + * actual spaces on the stack rather than bytes) since the task referenced by + * xTask was created. + */ +UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask ) + PRIVILEGED_FUNCTION; + +/** + * task.h + * @code{c} + * configSTACK_DEPTH_TYPE uxTaskGetStackHighWaterMark2( TaskHandle_t xTask ); + * @endcode + * + * INCLUDE_uxTaskGetStackHighWaterMark2 must be set to 1 in FreeRTOSConfig.h for + * this function to be available. + * + * Returns the high water mark of the stack associated with xTask. That is, + * the minimum free stack space there has been (in words, so on a 32 bit machine + * a value of 1 means 4 bytes) since the task started. The smaller the returned + * number the closer the task has come to overflowing its stack. + * + * 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. + * + * @param xTask Handle of the task associated with the stack to be checked. + * Set xTask to NULL to check the stack of the calling task. + * + * @return The smallest amount of free stack space there has been (in words, so + * actual spaces on the stack rather than bytes) since the task referenced by + * xTask was created. + */ +configSTACK_DEPTH_TYPE uxTaskGetStackHighWaterMark2( TaskHandle_t xTask ) + PRIVILEGED_FUNCTION; + +/* When using trace macros it is sometimes necessary to include task.h before + * FreeRTOS.h. When this is done TaskHookFunction_t will not yet have been + * defined, so the following two prototypes will cause a compilation error. This + * can be fixed by simply guarding against the inclusion of these two prototypes + * unless they are explicitly required by the configUSE_APPLICATION_TASK_TAG + * configuration constant. */ +#ifdef configUSE_APPLICATION_TASK_TAG + #if configUSE_APPLICATION_TASK_TAG == 1 + +/** + * task.h + * @code{c} + * void vTaskSetApplicationTaskTag( TaskHandle_t xTask, TaskHookFunction_t + * pxHookFunction ); + * @endcode + * + * Sets pxHookFunction to be the task hook function used by the task xTask. + * Passing xTask as NULL has the effect of setting the calling tasks hook + * function. + */ +void vTaskSetApplicationTaskTag( TaskHandle_t xTask, + TaskHookFunction_t pxHookFunction ) + PRIVILEGED_FUNCTION; + +/** + * task.h + * @code{c} + * void xTaskGetApplicationTaskTag( TaskHandle_t xTask ); + * @endcode + * + * Returns the pxHookFunction value assigned to the task xTask. Do not + * call from an interrupt service routine - call + * xTaskGetApplicationTaskTagFromISR() instead. + */ +TaskHookFunction_t xTaskGetApplicationTaskTag( TaskHandle_t xTask ) + PRIVILEGED_FUNCTION; + +/** + * task.h + * @code{c} + * void xTaskGetApplicationTaskTagFromISR( TaskHandle_t xTask ); + * @endcode + * + * Returns the pxHookFunction value assigned to the task xTask. Can + * be called from an interrupt service routine. + */ +TaskHookFunction_t xTaskGetApplicationTaskTagFromISR( TaskHandle_t xTask ) + PRIVILEGED_FUNCTION; + #endif /* configUSE_APPLICATION_TASK_TAG ==1 */ +#endif /* ifdef configUSE_APPLICATION_TASK_TAG */ + +#if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) + +/* Each task contains an array of pointers that is dimensioned by the + * configNUM_THREAD_LOCAL_STORAGE_POINTERS setting in FreeRTOSConfig.h. The + * kernel does not use the pointers itself, so the application writer can use + * the pointers for any purpose they wish. The following two functions are + * used to set and query a pointer respectively. */ +void vTaskSetThreadLocalStoragePointer( TaskHandle_t xTaskToSet, + BaseType_t xIndex, + void * pvValue ) PRIVILEGED_FUNCTION; +void * pvTaskGetThreadLocalStoragePointer( TaskHandle_t xTaskToQuery, + BaseType_t xIndex ) + PRIVILEGED_FUNCTION; + +#endif + +#if( configCHECK_FOR_STACK_OVERFLOW > 0 ) + +/** + * task.h + * @code{c} + * void vApplicationStackOverflowHook( TaskHandle_t xTask, char *pcTaskName); + * @endcode + * + * The application stack overflow hook is called when a stack overflow is + * detected for a task. + * + * Details on stack overflow detection can be found here: + * https://www.FreeRTOS.org/Stacks-and-stack-overflow-checking.html + * + * @param xTask the task that just exceeded its stack boundaries. + * @param pcTaskName A character string containing the name of the offending + * task. + */ +void vApplicationStackOverflowHook( TaskHandle_t xTask, char * pcTaskName ); + +#endif + +#if( configUSE_IDLE_HOOK == 1 ) + +/** + * task.h + * @code{c} + * void vApplicationIdleHook( void ); + * @endcode + * + * The application idle hook is called by the idle task. + * This allows the application designer to add background functionality without + * the overhead of a separate task. + * NOTE: vApplicationIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES, CALL A + * FUNCTION THAT MIGHT BLOCK. + */ +void vApplicationIdleHook( void ); + +#endif + +#if( configUSE_TICK_HOOK != 0 ) + +/** + * task.h + * @code{c} + * void vApplicationTickHook( void ); + * @endcode + * + * This hook function is called in the system tick handler after any OS work is + * completed. + */ +void vApplicationTickHook( void ); /*lint !e526 Symbol not defined as it is an + application callback. */ + +#endif + +#if( configSUPPORT_STATIC_ALLOCATION == 1 ) + +/** + * task.h + * @code{c} + * void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer, + * StackType_t ** ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize ) + * @endcode + * + * This function is used to provide a statically allocated block of memory to + * FreeRTOS to hold the Idle Task TCB. This function is required when + * configSUPPORT_STATIC_ALLOCATION is set. For more information see this URI: + * https://www.FreeRTOS.org/a00110.html#configSUPPORT_STATIC_ALLOCATION + * + * @param ppxIdleTaskTCBBuffer A handle to a statically allocated TCB buffer + * @param ppxIdleTaskStackBuffer A handle to a statically allocated Stack buffer + * for the idle task + * @param pulIdleTaskStackSize A pointer to the number of elements that will fit + * in the allocated stack buffer + */ +void vApplicationGetIdleTaskMemory( + StaticTask_t ** ppxIdleTaskTCBBuffer, + StackType_t ** ppxIdleTaskStackBuffer, + uint32_t * pulIdleTaskStackSize ); /*lint !e526 Symbol not defined as it is + an application callback. */ +#endif + +/** + * task.h + * @code{c} + * BaseType_t xTaskCallApplicationTaskHook( TaskHandle_t xTask, void + * *pvParameter ); + * @endcode + * + * Calls the hook function associated with xTask. Passing xTask as NULL has + * the effect of calling the Running tasks (the calling task) hook function. + * + * pvParameter is passed to the hook function for the task to interpret as it + * wants. The return value is the value returned by the task hook function + * registered by the user. + */ +BaseType_t xTaskCallApplicationTaskHook( TaskHandle_t xTask, + void * pvParameter ) + PRIVILEGED_FUNCTION; + +/** + * xTaskGetIdleTaskHandle() is only available if + * INCLUDE_xTaskGetIdleTaskHandle is set to 1 in FreeRTOSConfig.h. + * + * Simply returns the handle of the idle task. It is not valid to call + * xTaskGetIdleTaskHandle() before the scheduler has been started. + */ +TaskHandle_t xTaskGetIdleTaskHandle( void ) PRIVILEGED_FUNCTION; + +/** + * configUSE_TRACE_FACILITY must be defined as 1 in FreeRTOSConfig.h for + * uxTaskGetSystemState() to be available. + * + * uxTaskGetSystemState() populates an TaskStatus_t structure for each task in + * the system. TaskStatus_t structures contain, among other things, members + * for the task handle, task name, task priority, task state, and total amount + * of run time consumed by the task. See the TaskStatus_t structure + * definition in this file for the full member list. + * + * NOTE: This function is intended for debugging use only as its use results in + * the scheduler remaining suspended for an extended period. + * + * @param pxTaskStatusArray A pointer to an array of TaskStatus_t structures. + * The array must contain at least one TaskStatus_t structure for each task + * that is under the control of the RTOS. The number of tasks under the control + * of the RTOS can be determined using the uxTaskGetNumberOfTasks() API + * function. + * + * @param uxArraySize The size of the array pointed to by the pxTaskStatusArray + * parameter. The size is specified as the number of indexes in the array, or + * the number of TaskStatus_t structures contained in the array, not by the + * number of bytes in the array. + * + * @param pulTotalRunTime If configGENERATE_RUN_TIME_STATS is set to 1 in + * FreeRTOSConfig.h then *pulTotalRunTime is set by uxTaskGetSystemState() to + * the total run time (as defined by the run time stats clock, see + * https://www.FreeRTOS.org/rtos-run-time-stats.html) since the target booted. + * pulTotalRunTime can be set to NULL to omit the total run time information. + * + * @return The number of TaskStatus_t structures that were populated by + * uxTaskGetSystemState(). This should equal the number returned by the + * uxTaskGetNumberOfTasks() API function, but will be zero if the value passed + * in the uxArraySize parameter was too small. + * + * Example usage: + * @code{c} + * // This example demonstrates how a human readable table of run time stats + * // information is generated from raw data provided by + * uxTaskGetSystemState(). + * // The human readable table is written to pcWriteBuffer + * void vTaskGetRunTimeStats( char *pcWriteBuffer ) + * { + * TaskStatus_t *pxTaskStatusArray; + * volatile UBaseType_t uxArraySize, x; + * configRUN_TIME_COUNTER_TYPE ulTotalRunTime, ulStatsAsPercentage; + * + * // Make sure the write buffer does not contain a string. + * pcWriteBuffer = 0x00; + * + * // Take a snapshot of the number of tasks in case it changes while this + * // function is executing. + * uxArraySize = uxTaskGetNumberOfTasks(); + * + * // Allocate a TaskStatus_t structure for each task. An array could be + * // allocated statically at compile time. + * pxTaskStatusArray = pvPortMalloc( uxArraySize * sizeof( TaskStatus_t ) + * ); + * + * if( pxTaskStatusArray != NULL ) + * { + * // Generate raw status information about each task. + * uxArraySize = uxTaskGetSystemState( pxTaskStatusArray, uxArraySize, + * &ulTotalRunTime ); + * + * // For percentage calculations. + * ulTotalRunTime /= 100UL; + * + * // Avoid divide by zero errors. + * if( ulTotalRunTime > 0 ) + * { + * // For each populated position in the pxTaskStatusArray array, + * // format the raw data as human readable ASCII 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. + * // ulTotalRunTimeDiv100 has already been divided by 100. + * ulStatsAsPercentage = pxTaskStatusArray[ x + * ].ulRunTimeCounter / ulTotalRunTime; + * + * if( ulStatsAsPercentage > 0UL ) + * { + * sprintf( pcWriteBuffer, "%s\t\t%lu\t\t%lu%%\r\n", + * pxTaskStatusArray[ x ].pcTaskName, pxTaskStatusArray[ x ].ulRunTimeCounter, + * ulStatsAsPercentage ); + * } + * else + * { + * // If the percentage is zero here then the task has + * // consumed less than 1% of the total run time. + * sprintf( pcWriteBuffer, "%s\t\t%lu\t\t<1%%\r\n", + * pxTaskStatusArray[ x ].pcTaskName, pxTaskStatusArray[ x ].ulRunTimeCounter ); + * } + * + * pcWriteBuffer += strlen( ( char * ) pcWriteBuffer ); + * } + * } + * + * // The array is no longer needed, free the memory it consumes. + * vPortFree( pxTaskStatusArray ); + * } + * } + * @endcode + */ +UBaseType_t uxTaskGetSystemState( + TaskStatus_t * const pxTaskStatusArray, + const UBaseType_t uxArraySize, + configRUN_TIME_COUNTER_TYPE * const pulTotalRunTime ) PRIVILEGED_FUNCTION; + +/** + * task. h + * @code{c} + * void vTaskList( char *pcWriteBuffer ); + * @endcode + * + * configUSE_TRACE_FACILITY and configUSE_STATS_FORMATTING_FUNCTIONS must + * both be defined as 1 for this function to be available. See the + * configuration section of the FreeRTOS.org website for more information. + * + * NOTE 1: This function will disable interrupts for its duration. It is + * not intended for normal application runtime use but as a debug aid. + * + * Lists all the current tasks, along with their current state and stack + * usage high water mark. + * + * Tasks are reported as blocked ('B'), ready ('R'), deleted ('D') or + * suspended ('S'). + * + * 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(). + * + * @param pcWriteBuffer A buffer into which the above mentioned details + * will be written, in ASCII form. This buffer is assumed to be large + * enough to contain the generated report. Approximately 40 bytes per + * task should be sufficient. + * + * \defgroup vTaskList vTaskList + * \ingroup TaskUtils + */ +void vTaskList( char * pcWriteBuffer ) + PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for + strings and single characters only. */ + +/** + * task. h + * @code{c} + * void vTaskGetRunTimeStats( char *pcWriteBuffer ); + * @endcode + * + * configGENERATE_RUN_TIME_STATS and configUSE_STATS_FORMATTING_FUNCTIONS + * must both be defined as 1 for this function to be available. The application + * must also then provide definitions for + * portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() and portGET_RUN_TIME_COUNTER_VALUE() + * to configure a peripheral timer/counter and return the timers current count + * value respectively. The counter should be at least 10 times the frequency of + * the tick count. + * + * NOTE 1: This function will disable interrupts for its duration. It is + * not intended for normal application runtime use but as a debug aid. + * + * Setting configGENERATE_RUN_TIME_STATS to 1 will result in a total + * accumulated execution time being stored for each task. The resolution + * of the accumulated time value depends on the frequency of the timer + * configured by the portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() macro. + * Calling vTaskGetRunTimeStats() writes the total execution time of each + * task into a buffer, both as an absolute count value and as a percentage + * of the total system execution time. + * + * NOTE 2: + * + * 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(). + * + * @param pcWriteBuffer A buffer into which the execution times will be + * written, in ASCII form. This buffer is assumed to be large enough to + * contain the generated report. Approximately 40 bytes per task should + * be sufficient. + * + * \defgroup vTaskGetRunTimeStats vTaskGetRunTimeStats + * \ingroup TaskUtils + */ +void vTaskGetRunTimeStats( char * pcWriteBuffer ) + PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for + strings and single characters only. */ + +/** + * task. h + * @code{c} + * configRUN_TIME_COUNTER_TYPE ulTaskGetRunTimeCounter( const TaskHandle_t xTask + * ); configRUN_TIME_COUNTER_TYPE ulTaskGetRunTimePercent( const TaskHandle_t + * xTask ); + * @endcode + * + * configGENERATE_RUN_TIME_STATS must be defined as 1 for these functions to be + * available. The application must also then provide definitions for + * portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() and + * portGET_RUN_TIME_COUNTER_VALUE() to configure a peripheral timer/counter and + * return the timers current count value respectively. The counter should be + * at least 10 times the frequency of the tick count. + * + * Setting configGENERATE_RUN_TIME_STATS to 1 will result in a total + * accumulated execution time being stored for each task. The resolution + * of the accumulated time value depends on the frequency of the timer + * configured by the portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() macro. + * While uxTaskGetSystemState() and vTaskGetRunTimeStats() writes the total + * execution time of each task into a buffer, ulTaskGetRunTimeCounter() + * returns the total execution time of just one task and + * ulTaskGetRunTimePercent() returns the percentage of the CPU time used by + * just one task. + * + * @return The total run time of the given task or the percentage of the total + * run time consumed by the given task. This is the amount of time the task + * has actually been executing. The unit of time is dependent on the frequency + * configured using the portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() and + * portGET_RUN_TIME_COUNTER_VALUE() macros. + * + * \defgroup ulTaskGetRunTimeCounter ulTaskGetRunTimeCounter + * \ingroup TaskUtils + */ +configRUN_TIME_COUNTER_TYPE ulTaskGetRunTimeCounter( const TaskHandle_t xTask ) + PRIVILEGED_FUNCTION; +configRUN_TIME_COUNTER_TYPE ulTaskGetRunTimePercent( const TaskHandle_t xTask ) + PRIVILEGED_FUNCTION; + +/** + * task. h + * @code{c} + * configRUN_TIME_COUNTER_TYPE ulTaskGetIdleRunTimeCounter( void ); + * configRUN_TIME_COUNTER_TYPE ulTaskGetIdleRunTimePercent( void ); + * @endcode + * + * configGENERATE_RUN_TIME_STATS must be defined as 1 for these functions to be + * available. The application must also then provide definitions for + * portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() and + * portGET_RUN_TIME_COUNTER_VALUE() to configure a peripheral timer/counter and + * return the timers current count value respectively. The counter should be + * at least 10 times the frequency of the tick count. + * + * Setting configGENERATE_RUN_TIME_STATS to 1 will result in a total + * accumulated execution time being stored for each task. The resolution + * of the accumulated time value depends on the frequency of the timer + * configured by the portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() macro. + * While uxTaskGetSystemState() and vTaskGetRunTimeStats() writes the total + * execution time of each task into a buffer, ulTaskGetIdleRunTimeCounter() + * returns the total execution time of just the idle task and + * ulTaskGetIdleRunTimePercent() returns the percentage of the CPU time used by + * just the idle task. + * + * Note the amount of idle time is only a good measure of the slack time in a + * system if there are no other tasks executing at the idle priority, tickless + * idle is not used, and configIDLE_SHOULD_YIELD is set to 0. + * + * @return The total run time of the idle task or the percentage of the total + * run time consumed by the idle task. This is the amount of time the + * idle task has actually been executing. The unit of time is dependent on the + * frequency configured using the portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() and + * portGET_RUN_TIME_COUNTER_VALUE() macros. + * + * \defgroup ulTaskGetIdleRunTimeCounter ulTaskGetIdleRunTimeCounter + * \ingroup TaskUtils + */ +configRUN_TIME_COUNTER_TYPE ulTaskGetIdleRunTimeCounter( void ) + PRIVILEGED_FUNCTION; +configRUN_TIME_COUNTER_TYPE ulTaskGetIdleRunTimePercent( void ) + PRIVILEGED_FUNCTION; + +/** + * task. h + * @code{c} + * BaseType_t xTaskNotifyIndexed( TaskHandle_t xTaskToNotify, UBaseType_t + * uxIndexToNotify, uint32_t ulValue, eNotifyAction eAction ); BaseType_t + * xTaskNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction + * eAction ); + * @endcode + * + * See https://www.FreeRTOS.org/RTOS-task-notifications.html for details. + * + * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for these + * functions to be available. + * + * Sends a direct to task notification to a task, with an optional value and + * action. + * + * Each task has a private array of "notification values" (or 'notifications'), + * each of which is a 32-bit unsigned integer (uint32_t). The constant + * configTASK_NOTIFICATION_ARRAY_ENTRIES sets the number of indexes in the + * array, and (for backward compatibility) defaults to 1 if left undefined. + * Prior to FreeRTOS V10.4.0 there was only one notification value per task. + * + * Events can be sent to a task using an intermediary object. Examples of such + * objects are queues, semaphores, mutexes and event groups. Task notifications + * are a method of sending an event directly to a task without the need for such + * an intermediary object. + * + * A notification sent to a task can optionally perform an action, such as + * update, overwrite or increment one of the task's notification values. In + * that way task notifications can be used to send data to a task, or be used as + * light weight and fast binary or counting semaphores. + * + * A task can use xTaskNotifyWaitIndexed() or ulTaskNotifyTakeIndexed() to + * [optionally] block to wait for a notification to be pending. The task does + * not consume any CPU time while it is in the Blocked state. + * + * A notification sent to a task will remain pending until it is cleared by the + * task calling xTaskNotifyWaitIndexed() or ulTaskNotifyTakeIndexed() (or their + * un-indexed equivalents). If the task was already in the Blocked state to + * wait for a notification when the notification arrives then the task will + * automatically be removed from the Blocked state (unblocked) and the + * notification cleared. + * + * **NOTE** Each notification within the array operates independently - a task + * can only block on one notification within the array at a time and will not be + * unblocked by a notification sent to any other array index. + * + * Backward compatibility information: + * Prior to FreeRTOS V10.4.0 each task had a single "notification value", and + * all task notification API functions operated on that value. Replacing the + * single notification value with an array of notification values necessitated a + * new set of API functions that could address specific notifications within the + * array. xTaskNotify() is the original API function, and remains backward + * compatible by always operating on the notification value at index 0 in the + * array. Calling xTaskNotify() is equivalent to calling xTaskNotifyIndexed() + * with the uxIndexToNotify parameter set to 0. + * + * @param xTaskToNotify The handle of the task being notified. The handle to a + * task can be returned from the xTaskCreate() API function used to create the + * task, and the handle of the currently running task can be obtained by calling + * xTaskGetCurrentTaskHandle(). + * + * @param uxIndexToNotify The index within the target task's array of + * notification values to which the notification is to be sent. uxIndexToNotify + * must be less than configTASK_NOTIFICATION_ARRAY_ENTRIES. xTaskNotify() does + * not have this parameter and always sends notifications to index 0. + * + * @param ulValue Data that can be sent with the notification. How the data is + * used depends on the value of the eAction parameter. + * + * @param eAction Specifies how the notification updates the task's notification + * value, if at all. Valid values for eAction are as follows: + * + * eSetBits - + * The target notification value is bitwise ORed with ulValue. + * xTaskNotifyIndexed() always returns pdPASS in this case. + * + * eIncrement - + * The target notification value is incremented. ulValue is not used and + * xTaskNotifyIndexed() always returns pdPASS in this case. + * + * eSetValueWithOverwrite - + * The target notification value is set to the value of ulValue, even if the + * task being notified had not yet processed the previous notification at the + * same array index (the task already had a notification pending at that index). + * xTaskNotifyIndexed() always returns pdPASS in this case. + * + * eSetValueWithoutOverwrite - + * If the task being notified did not already have a notification pending at the + * same array index then the target notification value is set to ulValue and + * xTaskNotifyIndexed() will return pdPASS. If the task being notified already + * had a notification pending at the same array index then no action is + * performed and pdFAIL is returned. + * + * eNoAction - + * The task receives a notification at the specified array index without the + * notification value at that index being updated. ulValue is not used and + * xTaskNotifyIndexed() always returns pdPASS in this case. + * + * pulPreviousNotificationValue - + * Can be used to pass out the subject task's notification value before any + * bits are modified by the notify function. + * + * @return Dependent on the value of eAction. See the description of the + * eAction parameter. + * + * \defgroup xTaskNotifyIndexed xTaskNotifyIndexed + * \ingroup TaskNotifications + */ +BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify, + UBaseType_t uxIndexToNotify, + uint32_t ulValue, + eNotifyAction eAction, + uint32_t * pulPreviousNotificationValue ) + PRIVILEGED_FUNCTION; +#define xTaskNotify( xTaskToNotify, ulValue, eAction ) \ + xTaskGenericNotify( ( xTaskToNotify ), \ + ( tskDEFAULT_INDEX_TO_NOTIFY ), \ + ( ulValue ), \ + ( eAction ), \ + NULL ) +#define xTaskNotifyIndexed( xTaskToNotify, uxIndexToNotify, ulValue, eAction ) \ + xTaskGenericNotify( ( xTaskToNotify ), \ + ( uxIndexToNotify ), \ + ( ulValue ), \ + ( eAction ), \ + NULL ) + +/** + * task. h + * @code{c} + * BaseType_t xTaskNotifyAndQueryIndexed( TaskHandle_t xTaskToNotify, + * UBaseType_t uxIndexToNotify, uint32_t ulValue, eNotifyAction eAction, + * uint32_t *pulPreviousNotifyValue ); BaseType_t xTaskNotifyAndQuery( + * TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t + * *pulPreviousNotifyValue ); + * @endcode + * + * See https://www.FreeRTOS.org/RTOS-task-notifications.html for details. + * + * xTaskNotifyAndQueryIndexed() performs the same operation as + * xTaskNotifyIndexed() with the addition that it also returns the subject + * task's prior notification value (the notification value at the time the + * function is called rather than when the function returns) in the additional + * pulPreviousNotifyValue parameter. + * + * xTaskNotifyAndQuery() performs the same operation as xTaskNotify() with the + * addition that it also returns the subject task's prior notification value + * (the notification value as it was at the time the function is called, rather + * than when the function returns) in the additional pulPreviousNotifyValue + * parameter. + * + * \defgroup xTaskNotifyAndQueryIndexed xTaskNotifyAndQueryIndexed + * \ingroup TaskNotifications + */ +#define xTaskNotifyAndQuery( xTaskToNotify, \ + ulValue, \ + eAction, \ + pulPreviousNotifyValue ) \ + xTaskGenericNotify( ( xTaskToNotify ), \ + ( tskDEFAULT_INDEX_TO_NOTIFY ), \ + ( ulValue ), \ + ( eAction ), \ + ( pulPreviousNotifyValue ) ) +#define xTaskNotifyAndQueryIndexed( xTaskToNotify, \ + uxIndexToNotify, \ + ulValue, \ + eAction, \ + pulPreviousNotifyValue ) \ + xTaskGenericNotify( ( xTaskToNotify ), \ + ( uxIndexToNotify ), \ + ( ulValue ), \ + ( eAction ), \ + ( pulPreviousNotifyValue ) ) + +/** + * task. h + * @code{c} + * BaseType_t xTaskNotifyIndexedFromISR( TaskHandle_t xTaskToNotify, UBaseType_t + * uxIndexToNotify, uint32_t ulValue, eNotifyAction eAction, BaseType_t + * *pxHigherPriorityTaskWoken ); BaseType_t xTaskNotifyFromISR( TaskHandle_t + * xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, BaseType_t + * *pxHigherPriorityTaskWoken ); + * @endcode + * + * See https://www.FreeRTOS.org/RTOS-task-notifications.html for details. + * + * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for these + * functions to be available. + * + * A version of xTaskNotifyIndexed() that can be used from an interrupt service + * routine (ISR). + * + * Each task has a private array of "notification values" (or 'notifications'), + * each of which is a 32-bit unsigned integer (uint32_t). The constant + * configTASK_NOTIFICATION_ARRAY_ENTRIES sets the number of indexes in the + * array, and (for backward compatibility) defaults to 1 if left undefined. + * Prior to FreeRTOS V10.4.0 there was only one notification value per task. + * + * Events can be sent to a task using an intermediary object. Examples of such + * objects are queues, semaphores, mutexes and event groups. Task notifications + * are a method of sending an event directly to a task without the need for such + * an intermediary object. + * + * A notification sent to a task can optionally perform an action, such as + * update, overwrite or increment one of the task's notification values. In + * that way task notifications can be used to send data to a task, or be used as + * light weight and fast binary or counting semaphores. + * + * A task can use xTaskNotifyWaitIndexed() to [optionally] block to wait for a + * notification to be pending, or ulTaskNotifyTakeIndexed() to [optionally] + * block to wait for a notification value to have a non-zero value. The task + * does not consume any CPU time while it is in the Blocked state. + * + * A notification sent to a task will remain pending until it is cleared by the + * task calling xTaskNotifyWaitIndexed() or ulTaskNotifyTakeIndexed() (or their + * un-indexed equivalents). If the task was already in the Blocked state to + * wait for a notification when the notification arrives then the task will + * automatically be removed from the Blocked state (unblocked) and the + * notification cleared. + * + * **NOTE** Each notification within the array operates independently - a task + * can only block on one notification within the array at a time and will not be + * unblocked by a notification sent to any other array index. + * + * Backward compatibility information: + * Prior to FreeRTOS V10.4.0 each task had a single "notification value", and + * all task notification API functions operated on that value. Replacing the + * single notification value with an array of notification values necessitated a + * new set of API functions that could address specific notifications within the + * array. xTaskNotifyFromISR() is the original API function, and remains + * backward compatible by always operating on the notification value at index 0 + * within the array. Calling xTaskNotifyFromISR() is equivalent to calling + * xTaskNotifyIndexedFromISR() with the uxIndexToNotify parameter set to 0. + * + * @param uxIndexToNotify The index within the target task's array of + * notification values to which the notification is to be sent. uxIndexToNotify + * must be less than configTASK_NOTIFICATION_ARRAY_ENTRIES. xTaskNotifyFromISR() + * does not have this parameter and always sends notifications to index 0. + * + * @param xTaskToNotify The handle of the task being notified. The handle to a + * task can be returned from the xTaskCreate() API function used to create the + * task, and the handle of the currently running task can be obtained by calling + * xTaskGetCurrentTaskHandle(). + * + * @param ulValue Data that can be sent with the notification. How the data is + * used depends on the value of the eAction parameter. + * + * @param eAction Specifies how the notification updates the task's notification + * value, if at all. Valid values for eAction are as follows: + * + * eSetBits - + * The task's notification value is bitwise ORed with ulValue. xTaskNotify() + * always returns pdPASS in this case. + * + * eIncrement - + * The task's notification value is incremented. ulValue is not used and + * xTaskNotify() always returns pdPASS in this case. + * + * eSetValueWithOverwrite - + * The task's notification value is set to the value of ulValue, even if the + * task being notified had not yet processed the previous notification (the + * task already had a notification pending). xTaskNotify() always returns + * pdPASS in this case. + * + * eSetValueWithoutOverwrite - + * If the task being notified did not already have a notification pending then + * the task's notification value is set to ulValue and xTaskNotify() will + * return pdPASS. If the task being notified already had a notification + * pending then no action is performed and pdFAIL is returned. + * + * eNoAction - + * The task receives a notification without its notification value being + * updated. ulValue is not used and xTaskNotify() always returns pdPASS in + * this case. + * + * @param pxHigherPriorityTaskWoken xTaskNotifyFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if sending the notification caused the + * task to which the notification was sent to leave the Blocked state, and the + * unblocked task has a priority higher than the currently running task. If + * xTaskNotifyFromISR() sets this value to pdTRUE then a context switch should + * be requested before the interrupt is exited. How a context switch is + * requested from an ISR is dependent on the port - see the documentation page + * for the port in use. + * + * @return Dependent on the value of eAction. See the description of the + * eAction parameter. + * + * \defgroup xTaskNotifyIndexedFromISR xTaskNotifyIndexedFromISR + * \ingroup TaskNotifications + */ +BaseType_t xTaskGenericNotifyFromISR( TaskHandle_t xTaskToNotify, + UBaseType_t uxIndexToNotify, + uint32_t ulValue, + eNotifyAction eAction, + uint32_t * pulPreviousNotificationValue, + BaseType_t * pxHigherPriorityTaskWoken ) + PRIVILEGED_FUNCTION; +#define xTaskNotifyFromISR( xTaskToNotify, \ + ulValue, \ + eAction, \ + pxHigherPriorityTaskWoken ) \ + xTaskGenericNotifyFromISR( ( xTaskToNotify ), \ + ( tskDEFAULT_INDEX_TO_NOTIFY ), \ + ( ulValue ), \ + ( eAction ), \ + NULL, \ + ( pxHigherPriorityTaskWoken ) ) +#define xTaskNotifyIndexedFromISR( xTaskToNotify, \ + uxIndexToNotify, \ + ulValue, \ + eAction, \ + pxHigherPriorityTaskWoken ) \ + xTaskGenericNotifyFromISR( ( xTaskToNotify ), \ + ( uxIndexToNotify ), \ + ( ulValue ), \ + ( eAction ), \ + NULL, \ + ( pxHigherPriorityTaskWoken ) ) + +/** + * task. h + * @code{c} + * BaseType_t xTaskNotifyAndQueryIndexedFromISR( TaskHandle_t xTaskToNotify, + * UBaseType_t uxIndexToNotify, uint32_t ulValue, eNotifyAction eAction, + * uint32_t *pulPreviousNotificationValue, BaseType_t *pxHigherPriorityTaskWoken + * ); BaseType_t xTaskNotifyAndQueryFromISR( TaskHandle_t xTaskToNotify, + * uint32_t ulValue, eNotifyAction eAction, uint32_t + * *pulPreviousNotificationValue, BaseType_t *pxHigherPriorityTaskWoken ); + * @endcode + * + * See https://www.FreeRTOS.org/RTOS-task-notifications.html for details. + * + * xTaskNotifyAndQueryIndexedFromISR() performs the same operation as + * xTaskNotifyIndexedFromISR() with the addition that it also returns the + * subject task's prior notification value (the notification value at the time + * the function is called rather than at the time the function returns) in the + * additional pulPreviousNotifyValue parameter. + * + * xTaskNotifyAndQueryFromISR() performs the same operation as + * xTaskNotifyFromISR() with the addition that it also returns the subject + * task's prior notification value (the notification value at the time the + * function is called rather than at the time the function returns) in the + * additional pulPreviousNotifyValue parameter. + * + * \defgroup xTaskNotifyAndQueryIndexedFromISR xTaskNotifyAndQueryIndexedFromISR + * \ingroup TaskNotifications + */ +#define xTaskNotifyAndQueryIndexedFromISR( xTaskToNotify, \ + uxIndexToNotify, \ + ulValue, \ + eAction, \ + pulPreviousNotificationValue, \ + pxHigherPriorityTaskWoken ) \ + xTaskGenericNotifyFromISR( ( xTaskToNotify ), \ + ( uxIndexToNotify ), \ + ( ulValue ), \ + ( eAction ), \ + ( pulPreviousNotificationValue ), \ + ( pxHigherPriorityTaskWoken ) ) +#define xTaskNotifyAndQueryFromISR( xTaskToNotify, \ + ulValue, \ + eAction, \ + pulPreviousNotificationValue, \ + pxHigherPriorityTaskWoken ) \ + xTaskGenericNotifyFromISR( ( xTaskToNotify ), \ + ( tskDEFAULT_INDEX_TO_NOTIFY ), \ + ( ulValue ), \ + ( eAction ), \ + ( pulPreviousNotificationValue ), \ + ( pxHigherPriorityTaskWoken ) ) + +/** + * task. h + * @code{c} + * BaseType_t xTaskNotifyWaitIndexed( UBaseType_t uxIndexToWaitOn, uint32_t + * ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t + * *pulNotificationValue, TickType_t xTicksToWait ); + * + * BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t + * ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait + * ); + * @endcode + * + * Waits for a direct to task notification to be pending at a given index within + * an array of direct to task notifications. + * + * See https://www.FreeRTOS.org/RTOS-task-notifications.html for details. + * + * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for this + * function to be available. + * + * Each task has a private array of "notification values" (or 'notifications'), + * each of which is a 32-bit unsigned integer (uint32_t). The constant + * configTASK_NOTIFICATION_ARRAY_ENTRIES sets the number of indexes in the + * array, and (for backward compatibility) defaults to 1 if left undefined. + * Prior to FreeRTOS V10.4.0 there was only one notification value per task. + * + * Events can be sent to a task using an intermediary object. Examples of such + * objects are queues, semaphores, mutexes and event groups. Task notifications + * are a method of sending an event directly to a task without the need for such + * an intermediary object. + * + * A notification sent to a task can optionally perform an action, such as + * update, overwrite or increment one of the task's notification values. In + * that way task notifications can be used to send data to a task, or be used as + * light weight and fast binary or counting semaphores. + * + * A notification sent to a task will remain pending until it is cleared by the + * task calling xTaskNotifyWaitIndexed() or ulTaskNotifyTakeIndexed() (or their + * un-indexed equivalents). If the task was already in the Blocked state to + * wait for a notification when the notification arrives then the task will + * automatically be removed from the Blocked state (unblocked) and the + * notification cleared. + * + * A task can use xTaskNotifyWaitIndexed() to [optionally] block to wait for a + * notification to be pending, or ulTaskNotifyTakeIndexed() to [optionally] + * block to wait for a notification value to have a non-zero value. The task + * does not consume any CPU time while it is in the Blocked state. + * + * **NOTE** Each notification within the array operates independently - a task + * can only block on one notification within the array at a time and will not be + * unblocked by a notification sent to any other array index. + * + * Backward compatibility information: + * Prior to FreeRTOS V10.4.0 each task had a single "notification value", and + * all task notification API functions operated on that value. Replacing the + * single notification value with an array of notification values necessitated a + * new set of API functions that could address specific notifications within the + * array. xTaskNotifyWait() is the original API function, and remains backward + * compatible by always operating on the notification value at index 0 in the + * array. Calling xTaskNotifyWait() is equivalent to calling + * xTaskNotifyWaitIndexed() with the uxIndexToWaitOn parameter set to 0. + * + * @param uxIndexToWaitOn The index within the calling task's array of + * notification values on which the calling task will wait for a notification to + * be received. uxIndexToWaitOn must be less than + * configTASK_NOTIFICATION_ARRAY_ENTRIES. xTaskNotifyWait() does + * not have this parameter and always waits for notifications on index 0. + * + * @param ulBitsToClearOnEntry Bits that are set in ulBitsToClearOnEntry value + * will be cleared in the calling task's notification value before the task + * checks to see if any notifications are pending, and optionally blocks if no + * notifications are pending. Setting ulBitsToClearOnEntry to ULONG_MAX (if + * limits.h is included) or 0xffffffffUL (if limits.h is not included) will have + * the effect of resetting the task's notification value to 0. Setting + * ulBitsToClearOnEntry to 0 will leave the task's notification value unchanged. + * + * @param ulBitsToClearOnExit If a notification is pending or received before + * the calling task exits the xTaskNotifyWait() function then the task's + * notification value (see the xTaskNotify() API function) is passed out using + * the pulNotificationValue parameter. Then any bits that are set in + * ulBitsToClearOnExit will be cleared in the task's notification value (note + * *pulNotificationValue is set before any bits are cleared). Setting + * ulBitsToClearOnExit to ULONG_MAX (if limits.h is included) or 0xffffffffUL + * (if limits.h is not included) will have the effect of resetting the task's + * notification value to 0 before the function exits. Setting + * ulBitsToClearOnExit to 0 will leave the task's notification value unchanged + * when the function exits (in which case the value passed out in + * pulNotificationValue will match the task's notification value). + * + * @param pulNotificationValue Used to pass the task's notification value out + * of the function. Note the value passed out will not be effected by the + * clearing of any bits caused by ulBitsToClearOnExit being non-zero. + * + * @param xTicksToWait The maximum amount of time that the task should wait in + * the Blocked state for a notification to be received, should a notification + * not already be pending when xTaskNotifyWait() was called. The task + * will not consume any processing time while it is in the Blocked state. This + * is specified in kernel ticks, the macro pdMS_TO_TICKS( value_in_ms ) can be + * used to convert a time specified in milliseconds to a time specified in + * ticks. + * + * @return If a notification was received (including notifications that were + * already pending when xTaskNotifyWait was called) then pdPASS is + * returned. Otherwise pdFAIL is returned. + * + * \defgroup xTaskNotifyWaitIndexed xTaskNotifyWaitIndexed + * \ingroup TaskNotifications + */ +BaseType_t xTaskGenericNotifyWait( UBaseType_t uxIndexToWaitOn, + uint32_t ulBitsToClearOnEntry, + uint32_t ulBitsToClearOnExit, + uint32_t * pulNotificationValue, + TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; +#define xTaskNotifyWait( ulBitsToClearOnEntry, \ + ulBitsToClearOnExit, \ + pulNotificationValue, \ + xTicksToWait ) \ + xTaskGenericNotifyWait( tskDEFAULT_INDEX_TO_NOTIFY, \ + ( ulBitsToClearOnEntry ), \ + ( ulBitsToClearOnExit ), \ + ( pulNotificationValue ), \ + ( xTicksToWait ) ) +#define xTaskNotifyWaitIndexed( uxIndexToWaitOn, \ + ulBitsToClearOnEntry, \ + ulBitsToClearOnExit, \ + pulNotificationValue, \ + xTicksToWait ) \ + xTaskGenericNotifyWait( ( uxIndexToWaitOn ), \ + ( ulBitsToClearOnEntry ), \ + ( ulBitsToClearOnExit ), \ + ( pulNotificationValue ), \ + ( xTicksToWait ) ) + +/** + * task. h + * @code{c} + * BaseType_t xTaskNotifyGiveIndexed( TaskHandle_t xTaskToNotify, UBaseType_t + * uxIndexToNotify ); BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify ); + * @endcode + * + * Sends a direct to task notification to a particular index in the target + * task's notification array in a manner similar to giving a counting semaphore. + * + * See https://www.FreeRTOS.org/RTOS-task-notifications.html for more details. + * + * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for these + * macros to be available. + * + * Each task has a private array of "notification values" (or 'notifications'), + * each of which is a 32-bit unsigned integer (uint32_t). The constant + * configTASK_NOTIFICATION_ARRAY_ENTRIES sets the number of indexes in the + * array, and (for backward compatibility) defaults to 1 if left undefined. + * Prior to FreeRTOS V10.4.0 there was only one notification value per task. + * + * Events can be sent to a task using an intermediary object. Examples of such + * objects are queues, semaphores, mutexes and event groups. Task notifications + * are a method of sending an event directly to a task without the need for such + * an intermediary object. + * + * A notification sent to a task can optionally perform an action, such as + * update, overwrite or increment one of the task's notification values. In + * that way task notifications can be used to send data to a task, or be used as + * light weight and fast binary or counting semaphores. + * + * xTaskNotifyGiveIndexed() is a helper macro intended for use when task + * notifications are used as light weight and faster binary or counting + * semaphore equivalents. Actual FreeRTOS semaphores are given using the + * xSemaphoreGive() API function, the equivalent action that instead uses a task + * notification is xTaskNotifyGiveIndexed(). + * + * When task notifications are being used as a binary or counting semaphore + * equivalent then the task being notified should wait for the notification + * using the ulTaskNotifyTakeIndexed() API function rather than the + * xTaskNotifyWaitIndexed() API function. + * + * **NOTE** Each notification within the array operates independently - a task + * can only block on one notification within the array at a time and will not be + * unblocked by a notification sent to any other array index. + * + * Backward compatibility information: + * Prior to FreeRTOS V10.4.0 each task had a single "notification value", and + * all task notification API functions operated on that value. Replacing the + * single notification value with an array of notification values necessitated a + * new set of API functions that could address specific notifications within the + * array. xTaskNotifyGive() is the original API function, and remains backward + * compatible by always operating on the notification value at index 0 in the + * array. Calling xTaskNotifyGive() is equivalent to calling + * xTaskNotifyGiveIndexed() with the uxIndexToNotify parameter set to 0. + * + * @param xTaskToNotify The handle of the task being notified. The handle to a + * task can be returned from the xTaskCreate() API function used to create the + * task, and the handle of the currently running task can be obtained by calling + * xTaskGetCurrentTaskHandle(). + * + * @param uxIndexToNotify The index within the target task's array of + * notification values to which the notification is to be sent. uxIndexToNotify + * must be less than configTASK_NOTIFICATION_ARRAY_ENTRIES. xTaskNotifyGive() + * does not have this parameter and always sends notifications to index 0. + * + * @return xTaskNotifyGive() is a macro that calls xTaskNotify() with the + * eAction parameter set to eIncrement - so pdPASS is always returned. + * + * \defgroup xTaskNotifyGiveIndexed xTaskNotifyGiveIndexed + * \ingroup TaskNotifications + */ +#define xTaskNotifyGive( xTaskToNotify ) \ + xTaskGenericNotify( ( xTaskToNotify ), \ + ( tskDEFAULT_INDEX_TO_NOTIFY ), \ + ( 0 ), \ + eIncrement, \ + NULL ) +#define xTaskNotifyGiveIndexed( xTaskToNotify, uxIndexToNotify ) \ + xTaskGenericNotify( ( xTaskToNotify ), \ + ( uxIndexToNotify ), \ + ( 0 ), \ + eIncrement, \ + NULL ) + +/** + * task. h + * @code{c} + * void vTaskNotifyGiveIndexedFromISR( TaskHandle_t xTaskHandle, UBaseType_t + * uxIndexToNotify, BaseType_t *pxHigherPriorityTaskWoken ); void + * vTaskNotifyGiveFromISR( TaskHandle_t xTaskHandle, BaseType_t + * *pxHigherPriorityTaskWoken ); + * @endcode + * + * A version of xTaskNotifyGiveIndexed() that can be called from an interrupt + * service routine (ISR). + * + * See https://www.FreeRTOS.org/RTOS-task-notifications.html for more details. + * + * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for this macro + * to be available. + * + * Each task has a private array of "notification values" (or 'notifications'), + * each of which is a 32-bit unsigned integer (uint32_t). The constant + * configTASK_NOTIFICATION_ARRAY_ENTRIES sets the number of indexes in the + * array, and (for backward compatibility) defaults to 1 if left undefined. + * Prior to FreeRTOS V10.4.0 there was only one notification value per task. + * + * Events can be sent to a task using an intermediary object. Examples of such + * objects are queues, semaphores, mutexes and event groups. Task notifications + * are a method of sending an event directly to a task without the need for such + * an intermediary object. + * + * A notification sent to a task can optionally perform an action, such as + * update, overwrite or increment one of the task's notification values. In + * that way task notifications can be used to send data to a task, or be used as + * light weight and fast binary or counting semaphores. + * + * vTaskNotifyGiveIndexedFromISR() is intended for use when task notifications + * are used as light weight and faster binary or counting semaphore equivalents. + * Actual FreeRTOS semaphores are given from an ISR using the + * xSemaphoreGiveFromISR() API function, the equivalent action that instead uses + * a task notification is vTaskNotifyGiveIndexedFromISR(). + * + * When task notifications are being used as a binary or counting semaphore + * equivalent then the task being notified should wait for the notification + * using the ulTaskNotifyTakeIndexed() API function rather than the + * xTaskNotifyWaitIndexed() API function. + * + * **NOTE** Each notification within the array operates independently - a task + * can only block on one notification within the array at a time and will not be + * unblocked by a notification sent to any other array index. + * + * Backward compatibility information: + * Prior to FreeRTOS V10.4.0 each task had a single "notification value", and + * all task notification API functions operated on that value. Replacing the + * single notification value with an array of notification values necessitated a + * new set of API functions that could address specific notifications within the + * array. xTaskNotifyFromISR() is the original API function, and remains + * backward compatible by always operating on the notification value at index 0 + * within the array. Calling xTaskNotifyGiveFromISR() is equivalent to calling + * xTaskNotifyGiveIndexedFromISR() with the uxIndexToNotify parameter set to 0. + * + * @param xTaskToNotify The handle of the task being notified. The handle to a + * task can be returned from the xTaskCreate() API function used to create the + * task, and the handle of the currently running task can be obtained by calling + * xTaskGetCurrentTaskHandle(). + * + * @param uxIndexToNotify The index within the target task's array of + * notification values to which the notification is to be sent. uxIndexToNotify + * must be less than configTASK_NOTIFICATION_ARRAY_ENTRIES. + * xTaskNotifyGiveFromISR() does not have this parameter and always sends + * notifications to index 0. + * + * @param pxHigherPriorityTaskWoken vTaskNotifyGiveFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if sending the notification caused the + * task to which the notification was sent to leave the Blocked state, and the + * unblocked task has a priority higher than the currently running task. If + * vTaskNotifyGiveFromISR() sets this value to pdTRUE then a context switch + * should be requested before the interrupt is exited. How a context switch is + * requested from an ISR is dependent on the port - see the documentation page + * for the port in use. + * + * \defgroup vTaskNotifyGiveIndexedFromISR vTaskNotifyGiveIndexedFromISR + * \ingroup TaskNotifications + */ +void vTaskGenericNotifyGiveFromISR( TaskHandle_t xTaskToNotify, + UBaseType_t uxIndexToNotify, + BaseType_t * pxHigherPriorityTaskWoken ) + PRIVILEGED_FUNCTION; +#define vTaskNotifyGiveFromISR( xTaskToNotify, pxHigherPriorityTaskWoken ) \ + vTaskGenericNotifyGiveFromISR( ( xTaskToNotify ), \ + ( tskDEFAULT_INDEX_TO_NOTIFY ), \ + ( pxHigherPriorityTaskWoken ) ) +#define vTaskNotifyGiveIndexedFromISR( xTaskToNotify, \ + uxIndexToNotify, \ + pxHigherPriorityTaskWoken ) \ + vTaskGenericNotifyGiveFromISR( ( xTaskToNotify ), \ + ( uxIndexToNotify ), \ + ( pxHigherPriorityTaskWoken ) ) + +/** + * task. h + * @code{c} + * uint32_t ulTaskNotifyTakeIndexed( UBaseType_t uxIndexToWaitOn, BaseType_t + * xClearCountOnExit, TickType_t xTicksToWait ); + * + * uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t + * xTicksToWait ); + * @endcode + * + * Waits for a direct to task notification on a particular index in the calling + * task's notification array in a manner similar to taking a counting semaphore. + * + * See https://www.FreeRTOS.org/RTOS-task-notifications.html for details. + * + * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for this + * function to be available. + * + * Each task has a private array of "notification values" (or 'notifications'), + * each of which is a 32-bit unsigned integer (uint32_t). The constant + * configTASK_NOTIFICATION_ARRAY_ENTRIES sets the number of indexes in the + * array, and (for backward compatibility) defaults to 1 if left undefined. + * Prior to FreeRTOS V10.4.0 there was only one notification value per task. + * + * Events can be sent to a task using an intermediary object. Examples of such + * objects are queues, semaphores, mutexes and event groups. Task notifications + * are a method of sending an event directly to a task without the need for such + * an intermediary object. + * + * A notification sent to a task can optionally perform an action, such as + * update, overwrite or increment one of the task's notification values. In + * that way task notifications can be used to send data to a task, or be used as + * light weight and fast binary or counting semaphores. + * + * ulTaskNotifyTakeIndexed() is intended for use when a task notification is + * used as a faster and lighter weight binary or counting semaphore alternative. + * Actual FreeRTOS semaphores are taken using the xSemaphoreTake() API function, + * the equivalent action that instead uses a task notification is + * ulTaskNotifyTakeIndexed(). + * + * When a task is using its notification value as a binary or counting semaphore + * other tasks should send notifications to it using the + * xTaskNotifyGiveIndexed() macro, or xTaskNotifyIndex() function with the + * eAction parameter set to eIncrement. + * + * ulTaskNotifyTakeIndexed() can either clear the task's notification value at + * the array index specified by the uxIndexToWaitOn parameter to zero on exit, + * in which case the notification value acts like a binary semaphore, or + * decrement the notification value on exit, in which case the notification + * value acts like a counting semaphore. + * + * A task can use ulTaskNotifyTakeIndexed() to [optionally] block to wait for + * a notification. The task does not consume any CPU time while it is in the + * Blocked state. + * + * Where as xTaskNotifyWaitIndexed() will return when a notification is pending, + * ulTaskNotifyTakeIndexed() will return when the task's notification value is + * not zero. + * + * **NOTE** Each notification within the array operates independently - a task + * can only block on one notification within the array at a time and will not be + * unblocked by a notification sent to any other array index. + * + * Backward compatibility information: + * Prior to FreeRTOS V10.4.0 each task had a single "notification value", and + * all task notification API functions operated on that value. Replacing the + * single notification value with an array of notification values necessitated a + * new set of API functions that could address specific notifications within the + * array. ulTaskNotifyTake() is the original API function, and remains backward + * compatible by always operating on the notification value at index 0 in the + * array. Calling ulTaskNotifyTake() is equivalent to calling + * ulTaskNotifyTakeIndexed() with the uxIndexToWaitOn parameter set to 0. + * + * @param uxIndexToWaitOn The index within the calling task's array of + * notification values on which the calling task will wait for a notification to + * be non-zero. uxIndexToWaitOn must be less than + * configTASK_NOTIFICATION_ARRAY_ENTRIES. xTaskNotifyTake() does + * not have this parameter and always waits for notifications on index 0. + * + * @param xClearCountOnExit if xClearCountOnExit is pdFALSE then the task's + * notification value is decremented when the function exits. In this way the + * notification value acts like a counting semaphore. If xClearCountOnExit is + * not pdFALSE then the task's notification value is cleared to zero when the + * function exits. In this way the notification value acts like a binary + * semaphore. + * + * @param xTicksToWait The maximum amount of time that the task should wait in + * the Blocked state for the task's notification value to be greater than zero, + * should the count not already be greater than zero when + * ulTaskNotifyTake() was called. The task will not consume any processing + * time while it is in the Blocked state. This is specified in kernel ticks, + * the macro pdMS_TO_TICKS( value_in_ms ) can be used to convert a time + * specified in milliseconds to a time specified in ticks. + * + * @return The task's notification count before it is either cleared to zero or + * decremented (see the xClearCountOnExit parameter). + * + * \defgroup ulTaskNotifyTakeIndexed ulTaskNotifyTakeIndexed + * \ingroup TaskNotifications + */ +uint32_t ulTaskGenericNotifyTake( UBaseType_t uxIndexToWaitOn, + BaseType_t xClearCountOnExit, + TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; +#define ulTaskNotifyTake( xClearCountOnExit, xTicksToWait ) \ + ulTaskGenericNotifyTake( ( tskDEFAULT_INDEX_TO_NOTIFY ), \ + ( xClearCountOnExit ), \ + ( xTicksToWait ) ) +#define ulTaskNotifyTakeIndexed( uxIndexToWaitOn, \ + xClearCountOnExit, \ + xTicksToWait ) \ + ulTaskGenericNotifyTake( ( uxIndexToWaitOn ), \ + ( xClearCountOnExit ), \ + ( xTicksToWait ) ) + +/** + * task. h + * @code{c} + * BaseType_t xTaskNotifyStateClearIndexed( TaskHandle_t xTask, UBaseType_t + * uxIndexToCLear ); + * + * BaseType_t xTaskNotifyStateClear( TaskHandle_t xTask ); + * @endcode + * + * See https://www.FreeRTOS.org/RTOS-task-notifications.html for details. + * + * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for these + * functions to be available. + * + * Each task has a private array of "notification values" (or 'notifications'), + * each of which is a 32-bit unsigned integer (uint32_t). The constant + * configTASK_NOTIFICATION_ARRAY_ENTRIES sets the number of indexes in the + * array, and (for backward compatibility) defaults to 1 if left undefined. + * Prior to FreeRTOS V10.4.0 there was only one notification value per task. + * + * If a notification is sent to an index within the array of notifications then + * the notification at that index is said to be 'pending' until it is read or + * explicitly cleared by the receiving task. xTaskNotifyStateClearIndexed() + * is the function that clears a pending notification without reading the + * notification value. The notification value at the same array index is not + * altered. Set xTask to NULL to clear the notification state of the calling + * task. + * + * Backward compatibility information: + * Prior to FreeRTOS V10.4.0 each task had a single "notification value", and + * all task notification API functions operated on that value. Replacing the + * single notification value with an array of notification values necessitated a + * new set of API functions that could address specific notifications within the + * array. xTaskNotifyStateClear() is the original API function, and remains + * backward compatible by always operating on the notification value at index 0 + * within the array. Calling xTaskNotifyStateClear() is equivalent to calling + * xTaskNotifyStateClearIndexed() with the uxIndexToNotify parameter set to 0. + * + * @param xTask The handle of the RTOS task that will have a notification state + * cleared. Set xTask to NULL to clear a notification state in the calling + * task. To obtain a task's handle create the task using xTaskCreate() and + * make use of the pxCreatedTask parameter, or create the task using + * xTaskCreateStatic() and store the returned value, or use the task's name in + * a call to xTaskGetHandle(). + * + * @param uxIndexToClear The index within the target task's array of + * notification values to act upon. For example, setting uxIndexToClear to 1 + * will clear the state of the notification at index 1 within the array. + * uxIndexToClear must be less than configTASK_NOTIFICATION_ARRAY_ENTRIES. + * ulTaskNotifyStateClear() does not have this parameter and always acts on the + * notification at index 0. + * + * @return pdTRUE if the task's notification state was set to + * eNotWaitingNotification, otherwise pdFALSE. + * + * \defgroup xTaskNotifyStateClearIndexed xTaskNotifyStateClearIndexed + * \ingroup TaskNotifications + */ +BaseType_t xTaskGenericNotifyStateClear( TaskHandle_t xTask, + UBaseType_t uxIndexToClear ) + PRIVILEGED_FUNCTION; +#define xTaskNotifyStateClear( xTask ) \ + xTaskGenericNotifyStateClear( ( xTask ), ( tskDEFAULT_INDEX_TO_NOTIFY ) ) +#define xTaskNotifyStateClearIndexed( xTask, uxIndexToClear ) \ + xTaskGenericNotifyStateClear( ( xTask ), ( uxIndexToClear ) ) + +/** + * task. h + * @code{c} + * uint32_t ulTaskNotifyValueClearIndexed( TaskHandle_t xTask, UBaseType_t + * uxIndexToClear, uint32_t ulBitsToClear ); + * + * uint32_t ulTaskNotifyValueClear( TaskHandle_t xTask, uint32_t ulBitsToClear + * ); + * @endcode + * + * See https://www.FreeRTOS.org/RTOS-task-notifications.html for details. + * + * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for these + * functions to be available. + * + * Each task has a private array of "notification values" (or 'notifications'), + * each of which is a 32-bit unsigned integer (uint32_t). The constant + * configTASK_NOTIFICATION_ARRAY_ENTRIES sets the number of indexes in the + * array, and (for backward compatibility) defaults to 1 if left undefined. + * Prior to FreeRTOS V10.4.0 there was only one notification value per task. + * + * ulTaskNotifyValueClearIndexed() clears the bits specified by the + * ulBitsToClear bit mask in the notification value at array index + * uxIndexToClear of the task referenced by xTask. + * + * Backward compatibility information: + * Prior to FreeRTOS V10.4.0 each task had a single "notification value", and + * all task notification API functions operated on that value. Replacing the + * single notification value with an array of notification values necessitated a + * new set of API functions that could address specific notifications within the + * array. ulTaskNotifyValueClear() is the original API function, and remains + * backward compatible by always operating on the notification value at index 0 + * within the array. Calling ulTaskNotifyValueClear() is equivalent to calling + * ulTaskNotifyValueClearIndexed() with the uxIndexToClear parameter set to 0. + * + * @param xTask The handle of the RTOS task that will have bits in one of its + * notification values cleared. Set xTask to NULL to clear bits in a + * notification value of the calling task. To obtain a task's handle create the + * task using xTaskCreate() and make use of the pxCreatedTask parameter, or + * create the task using xTaskCreateStatic() and store the returned value, or + * use the task's name in a call to xTaskGetHandle(). + * + * @param uxIndexToClear The index within the target task's array of + * notification values in which to clear the bits. uxIndexToClear + * must be less than configTASK_NOTIFICATION_ARRAY_ENTRIES. + * ulTaskNotifyValueClear() does not have this parameter and always clears bits + * in the notification value at index 0. + * + * @param ulBitsToClear Bit mask of the bits to clear in the notification value + * of xTask. Set a bit to 1 to clear the corresponding bits in the task's + * notification value. Set ulBitsToClear to 0xffffffff (UINT_MAX on 32-bit + * architectures) to clear the notification value to 0. Set ulBitsToClear to 0 + * to query the task's notification value without clearing any bits. + * + * + * @return The value of the target task's notification value before the bits + * specified by ulBitsToClear were cleared. + * \defgroup ulTaskNotifyValueClear ulTaskNotifyValueClear + * \ingroup TaskNotifications + */ +uint32_t ulTaskGenericNotifyValueClear( TaskHandle_t xTask, + UBaseType_t uxIndexToClear, + uint32_t ulBitsToClear ) + PRIVILEGED_FUNCTION; +#define ulTaskNotifyValueClear( xTask, ulBitsToClear ) \ + ulTaskGenericNotifyValueClear( ( xTask ), \ + ( tskDEFAULT_INDEX_TO_NOTIFY ), \ + ( ulBitsToClear ) ) +#define ulTaskNotifyValueClearIndexed( xTask, uxIndexToClear, ulBitsToClear ) \ + ulTaskGenericNotifyValueClear( ( xTask ), \ + ( uxIndexToClear ), \ + ( ulBitsToClear ) ) + +/** + * task.h + * @code{c} + * void vTaskSetTimeOutState( TimeOut_t * const pxTimeOut ); + * @endcode + * + * Capture the current time for future use with xTaskCheckForTimeOut(). + * + * @param pxTimeOut Pointer to a timeout object into which the current time + * is to be captured. The captured time includes the tick count and the number + * of times the tick count has overflowed since the system first booted. + * \defgroup vTaskSetTimeOutState vTaskSetTimeOutState + * \ingroup TaskCtrl + */ +void vTaskSetTimeOutState( TimeOut_t * const pxTimeOut ) PRIVILEGED_FUNCTION; + +/** + * task.h + * @code{c} + * BaseType_t xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, TickType_t * + * const pxTicksToWait ); + * @endcode + * + * Determines if pxTicksToWait ticks has passed since a time was captured + * using a call to vTaskSetTimeOutState(). The captured time includes the tick + * count and the number of times the tick count has overflowed. + * + * @param pxTimeOut The time status as captured previously using + * vTaskSetTimeOutState. If the timeout has not yet occurred, it is updated + * to reflect the current time status. + * @param pxTicksToWait The number of ticks to check for timeout i.e. if + * pxTicksToWait ticks have passed since pxTimeOut was last updated (either by + * vTaskSetTimeOutState() or xTaskCheckForTimeOut()), the timeout has occurred. + * If the timeout has not occurred, pxTicksToWait is updated to reflect the + * number of remaining ticks. + * + * @return If timeout has occurred, pdTRUE is returned. Otherwise pdFALSE is + * returned and pxTicksToWait is updated to reflect the number of remaining + * ticks. + * + * @see https://www.FreeRTOS.org/xTaskCheckForTimeOut.html + * + * Example Usage: + * @code{c} + * // Driver library function used to receive uxWantedBytes from an Rx buffer + * // that is filled by a UART interrupt. If there are not enough bytes in the + * // Rx buffer then the task enters the Blocked state until it is notified + * that + * // more data has been placed into the buffer. If there is still not enough + * // data then the task re-enters the Blocked state, and + * xTaskCheckForTimeOut() + * // is used to re-calculate the Block time to ensure the total amount of time + * // spent in the Blocked state does not exceed MAX_TIME_TO_WAIT. This + * // continues until either the buffer contains at least uxWantedBytes bytes, + * // or the total amount of time spent in the Blocked state reaches + * // MAX_TIME_TO_WAIT - at which point the task reads however many bytes are + * // available up to a maximum of uxWantedBytes. + * + * size_t xUART_Receive( uint8_t *pucBuffer, size_t uxWantedBytes ) + * { + * size_t uxReceived = 0; + * TickType_t xTicksToWait = MAX_TIME_TO_WAIT; + * TimeOut_t xTimeOut; + * + * // Initialize xTimeOut. This records the time at which this function + * // was entered. + * vTaskSetTimeOutState( &xTimeOut ); + * + * // Loop until the buffer contains the wanted number of bytes, or a + * // timeout occurs. + * while( UART_bytes_in_rx_buffer( pxUARTInstance ) < uxWantedBytes ) + * { + * // The buffer didn't contain enough data so this task is going to + * // enter the Blocked state. Adjusting xTicksToWait to account for + * // any time that has been spent in the Blocked state within this + * // function so far to ensure the total amount of time spent in the + * // Blocked state does not exceed MAX_TIME_TO_WAIT. + * if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) != pdFALSE ) + * { + * //Timed out before the wanted number of bytes were available, + * // exit the loop. + * break; + * } + * + * // Wait for a maximum of xTicksToWait ticks to be notified that the + * // receive interrupt has placed more data into the buffer. + * ulTaskNotifyTake( pdTRUE, xTicksToWait ); + * } + * + * // Attempt to read uxWantedBytes from the receive buffer into pucBuffer. + * // The actual number of bytes read (which might be less than + * // uxWantedBytes) is returned. + * uxReceived = UART_read_from_receive_buffer( pxUARTInstance, + * pucBuffer, + * uxWantedBytes ); + * + * return uxReceived; + * } + * @endcode + * \defgroup xTaskCheckForTimeOut xTaskCheckForTimeOut + * \ingroup TaskCtrl + */ +BaseType_t xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, + TickType_t * const pxTicksToWait ) + PRIVILEGED_FUNCTION; + +/** + * task.h + * @code{c} + * BaseType_t xTaskCatchUpTicks( TickType_t xTicksToCatchUp ); + * @endcode + * + * This function corrects the tick count value after the application code has + * held interrupts disabled for an extended period resulting in tick interrupts + * having been missed. + * + * This function is similar to vTaskStepTick(), however, unlike + * vTaskStepTick(), xTaskCatchUpTicks() may move the tick count forward past a + * time at which a task should be removed from the blocked state. That means + * tasks may have to be removed from the blocked state as the tick count is + * moved. + * + * @param xTicksToCatchUp The number of tick interrupts that have been missed + * due to interrupts being disabled. Its value is not computed automatically, + * so must be computed by the application writer. + * + * @return pdTRUE if moving the tick count forward resulted in a task leaving + * the blocked state and a context switch being performed. Otherwise pdFALSE. + * + * \defgroup xTaskCatchUpTicks xTaskCatchUpTicks + * \ingroup TaskCtrl + */ +BaseType_t xTaskCatchUpTicks( TickType_t xTicksToCatchUp ) PRIVILEGED_FUNCTION; + +/*----------------------------------------------------------- + * SCHEDULER INTERNALS AVAILABLE FOR PORTING PURPOSES + *----------------------------------------------------------*/ + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS ONLY + * INTENDED FOR USE WHEN IMPLEMENTING A PORT OF THE SCHEDULER AND IS + * AN INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * Called from the real time kernel tick (either preemptive or cooperative), + * this increments the tick count and checks if any tasks that are blocked + * for a finite period required removing from a blocked list and placing on + * a ready list. If a non-zero value is returned then a context switch is + * required because either: + * + A task was removed from a blocked list because its timeout had expired, + * or + * + Time slicing is in use and there is a task of equal priority to the + * currently running task. + */ +BaseType_t xTaskIncrementTick( void ) PRIVILEGED_FUNCTION; + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN + * INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED. + * + * Removes the calling task from the ready list and places it both + * on the list of tasks waiting for a particular event, and the + * list of delayed tasks. The task will be removed from both lists + * and replaced on the ready list should either the event occur (and + * there be no higher priority tasks waiting on the same event) or + * the delay period expires. + * + * The 'unordered' version replaces the event list item value with the + * xItemValue value, and inserts the list item at the end of the list. + * + * The 'ordered' version uses the existing event list item value (which is the + * owning task's priority) to insert the list item into the event list in task + * priority order. + * + * @param pxEventList The list containing tasks that are blocked waiting + * for the event to occur. + * + * @param xItemValue The item value to use for the event list item when the + * event list is not ordered by task priority. + * + * @param xTicksToWait The maximum amount of time that the task should wait + * for the event to occur. This is specified in kernel ticks, the constant + * portTICK_PERIOD_MS can be used to convert kernel ticks into a real time + * period. + */ +void vTaskPlaceOnEventList( List_t * const pxEventList, + const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; +void vTaskPlaceOnUnorderedEventList( List_t * pxEventList, + const TickType_t xItemValue, + const TickType_t xTicksToWait ) + PRIVILEGED_FUNCTION; + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN + * INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED. + * + * This function performs nearly the same function as vTaskPlaceOnEventList(). + * The difference being that this function does not permit tasks to block + * indefinitely, whereas vTaskPlaceOnEventList() does. + * + */ +void vTaskPlaceOnEventListRestricted( List_t * const pxEventList, + TickType_t xTicksToWait, + const BaseType_t xWaitIndefinitely ) + PRIVILEGED_FUNCTION; + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN + * INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED. + * + * Removes a task from both the specified event list and the list of blocked + * tasks, and places it on a ready queue. + * + * xTaskRemoveFromEventList()/vTaskRemoveFromUnorderedEventList() will be called + * if either an event occurs to unblock a task, or the block timeout period + * expires. + * + * xTaskRemoveFromEventList() is used when the event list is in task priority + * order. It removes the list item from the head of the event list as that will + * have the highest priority owning task of all the tasks on the event list. + * vTaskRemoveFromUnorderedEventList() is used when the event list is not + * ordered and the event list items hold something other than the owning tasks + * priority. In this case the event list item value is updated to the value + * passed in the xItemValue parameter. + * + * @return pdTRUE if the task being removed has a higher priority than the task + * making the call, otherwise pdFALSE. + */ +BaseType_t xTaskRemoveFromEventList( const List_t * const pxEventList ) + PRIVILEGED_FUNCTION; +void vTaskRemoveFromUnorderedEventList( ListItem_t * pxEventListItem, + const TickType_t xItemValue ) + PRIVILEGED_FUNCTION; + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS ONLY + * INTENDED FOR USE WHEN IMPLEMENTING A PORT OF THE SCHEDULER AND IS + * AN INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * Sets the pointer to the current TCB to the TCB of the highest priority task + * that is ready to run. + */ +#if( configNUMBER_OF_CORES == 1 ) +portDONT_DISCARD void vTaskSwitchContext( void ) PRIVILEGED_FUNCTION; +#else +portDONT_DISCARD void vTaskSwitchContext( BaseType_t xCoreID ) + PRIVILEGED_FUNCTION; +#endif + +/* + * THESE FUNCTIONS MUST NOT BE USED FROM APPLICATION CODE. THEY ARE USED BY + * THE EVENT BITS MODULE. + */ +TickType_t uxTaskResetEventItemValue( void ) PRIVILEGED_FUNCTION; + +/* + * Return the handle of the calling task. + */ +TaskHandle_t xTaskGetCurrentTaskHandle( void ) PRIVILEGED_FUNCTION; + +/* + * Return the handle of the task running on specified core. + */ +TaskHandle_t xTaskGetCurrentTaskHandleCPU( BaseType_t xCoreID ) + PRIVILEGED_FUNCTION; + +/* + * Shortcut used by the queue implementation to prevent unnecessary call to + * taskYIELD(); + */ +void vTaskMissedYield( void ) PRIVILEGED_FUNCTION; + +/* + * Returns the scheduler state as taskSCHEDULER_RUNNING, + * taskSCHEDULER_NOT_STARTED or taskSCHEDULER_SUSPENDED. + */ +BaseType_t xTaskGetSchedulerState( void ) PRIVILEGED_FUNCTION; + +/* + * Raises the priority of the mutex holder to that of the calling task should + * the mutex holder have a priority less than the calling task. + */ +BaseType_t xTaskPriorityInherit( TaskHandle_t const pxMutexHolder ) + PRIVILEGED_FUNCTION; + +/* + * Set the priority of a task back to its proper priority in the case that it + * inherited a higher priority while it was holding a semaphore. + */ +BaseType_t xTaskPriorityDisinherit( TaskHandle_t const pxMutexHolder ) + PRIVILEGED_FUNCTION; + +/* + * If a higher priority task attempting to obtain a mutex caused a lower + * priority task to inherit the higher priority task's priority - but the higher + * priority task then timed out without obtaining the mutex, then the lower + * priority task will disinherit the priority again - but only down as far as + * the highest priority task that is still waiting for the mutex (if there were + * more than one task waiting for the mutex). + */ +void vTaskPriorityDisinheritAfterTimeout( + TaskHandle_t const pxMutexHolder, + UBaseType_t uxHighestPriorityWaitingTask ) PRIVILEGED_FUNCTION; + +/* + * Get the uxTaskNumber assigned to the task referenced by the xTask parameter. + */ +UBaseType_t uxTaskGetTaskNumber( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +/* + * Set the uxTaskNumber of the task referenced by the xTask parameter to + * uxHandle. + */ +void vTaskSetTaskNumber( TaskHandle_t xTask, + const UBaseType_t uxHandle ) PRIVILEGED_FUNCTION; + +/* + * Only available when configUSE_TICKLESS_IDLE is set to 1. + * If tickless mode is being used, or a low power mode is implemented, then + * the tick interrupt will not execute during idle periods. When this is the + * case, the tick count value maintained by the scheduler needs to be kept up + * to date with the actual execution time by being skipped forward by a time + * equal to the idle period. + */ +void vTaskStepTick( TickType_t xTicksToJump ) PRIVILEGED_FUNCTION; + +/* + * Only available when configUSE_TICKLESS_IDLE is set to 1. + * Provided for use within portSUPPRESS_TICKS_AND_SLEEP() to allow the port + * specific sleep function to determine if it is ok to proceed with the sleep, + * and if it is ok to proceed, if it is ok to sleep indefinitely. + * + * This function is necessary because portSUPPRESS_TICKS_AND_SLEEP() is only + * called with the scheduler suspended, not from within a critical section. It + * is therefore possible for an interrupt to request a context switch between + * portSUPPRESS_TICKS_AND_SLEEP() and the low power mode actually being + * entered. eTaskConfirmSleepModeStatus() should be called from a short + * critical section between the timer being stopped and the sleep mode being + * entered to ensure it is ok to proceed into the sleep mode. + */ +eSleepModeStatus eTaskConfirmSleepModeStatus( void ) PRIVILEGED_FUNCTION; + +/* + * For internal use only. Increment the mutex held count when a mutex is + * taken and return the handle of the task that has taken the mutex. + */ +TaskHandle_t pvTaskIncrementMutexHeldCount( void ) PRIVILEGED_FUNCTION; + +/* + * For internal use only. Same as vTaskSetTimeOutState(), but without a + * critical section. + */ +void vTaskInternalSetTimeOutState( TimeOut_t * const pxTimeOut ) + PRIVILEGED_FUNCTION; + +/* + * For internal use only. Same as portYIELD_WITHIN_API() in single core + * FreeRTOS. For SMP this is not defined by the port. + */ +void vTaskYieldWithinAPI( void ); + +/* + * This function is only intended for use when implementing a port of the + * scheduler and is only available when portCRITICAL_NESTING_IN_TCB is set to 1 + * or configNUMBER_OF_CORES is greater than 1. This function can be used in the + * implementation of portENTER_CRITICAL if port wants to maintain critical + * nesting count in TCB in single core FreeRTOS. It should be used in the + * implementation of portENTER_CRITICAL if port is running a multiple core + * FreeRTOS. + */ +void vTaskEnterCritical( void ); + +/* + * This function is only intended for use when implementing a port of the + * scheduler and is only available when portCRITICAL_NESTING_IN_TCB is set to 1 + * or configNUMBER_OF_CORES is greater than 1. This function can be used in the + * implementation of portEXIT_CRITICAL if port wants to maintain critical + * nesting count in TCB in single core FreeRTOS. It should be used in the + * implementation of portEXIT_CRITICAL if port is running a multiple core + * FreeRTOS. + */ +void vTaskExitCritical( void ); + +/* + * This function is only intended for use when implementing a port of the + * scheduler and is only available when configNUMBER_OF_CORES is greater than 1. + * This function should be used in the implementation of + * portENTER_CRITICAL_FROM_ISR if port is running a multiple core FreeRTOS. + */ +UBaseType_t vTaskEnterCriticalFromISR( void ); + +/* + * This function is only intended for use when implementing a port of the + * scheduler and is only available when configNUMBER_OF_CORES is greater than 1. + * This function should be used in the implementation of + * portEXIT_CRITICAL_FROM_ISR if port is running a multiple core FreeRTOS. + */ +void vTaskExitCriticalFromISR( UBaseType_t uxSavedInterruptStatus ); + +#if( portUSING_MPU_WRAPPERS == 1 ) + +/* + * For internal use only. Get MPU settings associated with a task. + */ +xMPU_SETTINGS * xTaskGetMPUSettings( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +#endif /* portUSING_MPU_WRAPPERS */ + +/* *INDENT-OFF* */ +#ifdef __cplusplus +} +#endif +/* *INDENT-ON* */ +#endif /* INC_TASK_H */ diff --git a/formatting/goodFiles/source/README.md b/formatting/goodFiles/source/README.md new file mode 100644 index 00000000..5570a387 --- /dev/null +++ b/formatting/goodFiles/source/README.md @@ -0,0 +1,6153 @@ +/* + * This is intentional. This file exists to be matched against in a test case where + * A non standard file type is provided to the getFiles() function + * 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 "stack_macros.h" +#include "task.h" +#include "timers.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 ) */ diff --git a/formatting/goodFiles/source/badFile.c b/formatting/goodFiles/source/badFile.c new file mode 100644 index 00000000..0eaca8b0 --- /dev/null +++ b/formatting/goodFiles/source/badFile.c @@ -0,0 +1,193 @@ +/* + * FreeRTOS V202212.00 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 + * + */ + +/****************************************************************************** + * NOTE 1: This project provides two demo applications. A simple blinky style + * project, and a more comprehensive test and demo application. The + * mainCREATE_SIMPLE_BLINKY_DEMO_ONLY setting in main.c is used to select + * between the two. See the notes on using mainCREATE_SIMPLE_BLINKY_DEMO_ONLY + * in main.c. This file implements the simply blinky style version. + * + * NOTE 2: This file only contains the source code that is specific to the + * basic demo. Generic functions, such FreeRTOS hook functions, and functions + * required to configure the hardware, are defined in main.c. + ****************************************************************************** + * + * main_blinky() creates one queue, and two tasks. It then starts the + * scheduler. + * + * The Queue Send Task: + * The queue send task is implemented by the prvQueueSendTask() function in + * this file. prvQueueSendTask() sits in a loop that causes it to repeatedly + * block for 200 milliseconds before sending the value 100 to the queue that + * was created within main_blinky(). Once the value is sent, the task loops + * back around to block for another 200 milliseconds. + * + * The Queue Receive Task: + * The queue receive task is implemented by the prvQueueReceiveTask() function + * in this file. prvQueueReceiveTask() sits in a loop where it repeatedly + * blocks on attempts to read data from the queue that was created within + * main_blinky(). When data is received, the task checks the value of the + * data, and if the value equals the expected 100, toggles the LED. The 'block + * time' parameter passed to the queue receive function specifies that the + * task should be held in the Blocked state indefinitely to wait for data to + * be available on the queue. The queue receive task will only leave the + * Blocked state when the queue send task writes to the queue. As the queue + * send task writes to the queue every 200 milliseconds, the queue receive + * task leaves the Blocked state every 200 milliseconds, and therefore toggles + * the LED every 200 milliseconds. + */ + +/* Kernel includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +/* Priorities at which the tasks are created. */ +#define mainQUEUE_RECEIVE_TASK_PRIORITY ( tskIDLE_PRIORITY + 2 ) +#define mainQUEUE_SEND_TASK_PRIORITY ( tskIDLE_PRIORITY + 1 ) + +/* The rate at which data is sent to the queue. The 200ms value is converted +to ticks using the portTICK_PERIOD_MS constant. */ +#define mainQUEUE_SEND_FREQUENCY_MS ( 200 / portTICK_PERIOD_MS ) + +/* The number of items the queue can hold. This is 1 as the receive task +will remove items as they are added, meaning the send task should always find +the queue empty. */ +#define mainQUEUE_LENGTH ( 1 ) + +/* Values passed to the two tasks just to check the task parameter +functionality. */ +#define mainQUEUE_SEND_PARAMETER ( 0x1111UL ) +#define mainQUEUE_RECEIVE_PARAMETER ( 0x22UL ) + + +/*-----------------------------------------------------------*/ + +/* + * The tasks as described in the comments at the top of this file. + */ +static void prvQueueReceiveTask( void *pvParameters ); +static void prvQueueSendTask( void *pvParameters ); + +/* + * Called by main() to create the simply blinky style application if + * mainCREATE_SIMPLE_BLINKY_DEMO_ONLY is set to 1. + */ +void main_blinky( void ); + +/*-----------------------------------------------------------*/ + +/* The queue used by both tasks. */ +static QueueHandle_t xQueue = NULL; + +/*-----------------------------------------------------------*/ + +void main_blinky( void ) +{ + /* Create the queue. */ + xQueue = xQueueCreate( mainQUEUE_LENGTH, sizeof( unsigned long ) ); + + if( xQueue != NULL ) + { + /* Start the two tasks as described in the comments at the top of this + file. */ + xTaskCreate( prvQueueReceiveTask, /* The function that implements the task. */ + "Rx", /* The text name assigned to the task - for debug only as it is not used by the kernel. */ + configMINIMAL_STACK_SIZE, /* The size of the stack to allocate to the task. */ + ( void * ) mainQUEUE_RECEIVE_PARAMETER, /* The parameter passed to the task - just to check the functionality. */ + mainQUEUE_RECEIVE_TASK_PRIORITY, /* The priority assigned to the task. */ + NULL ); /* The task handle is not required, so NULL is passed. */ + + xTaskCreate( prvQueueSendTask, "TX", configMINIMAL_STACK_SIZE, ( void * ) mainQUEUE_SEND_PARAMETER, mainQUEUE_SEND_TASK_PRIORITY, NULL ); + + /* Start the tasks and timer running. */ + vTaskStartScheduler(); + } + + /* If all is well, the scheduler will now be running, and the following + line will never be reached. If the following line does execute, then + there was insufficient FreeRTOS heap memory available for the idle and/or + timer tasks to be created. See the memory management section on the + FreeRTOS web site for more details. */ + for( ;; ); +} +/*-----------------------------------------------------------*/ + +static void prvQueueSendTask( void *pvParameters ) +{ +TickType_t xNextWakeTime; +const unsigned long ulValueToSend = 100UL; + + /* Check the task parameter is as expected. */ + configASSERT( ( ( unsigned long ) pvParameters ) == mainQUEUE_SEND_PARAMETER ); + + /* Initialise xNextWakeTime - this only needs to be done once. */ + xNextWakeTime = xTaskGetTickCount(); + + for( ;; ) + { + /* Place this task in the blocked state until it is time to run again. + The block time is specified in ticks, the constant used converts ticks + to ms. While in the Blocked state this task will not consume any CPU + time. */ + vTaskDelayUntil( &xNextWakeTime, mainQUEUE_SEND_FREQUENCY_MS ); + + /* Send to the queue - causing the queue receive task to unblock and + toggle the LED. 0 is used as the block time so the sending operation + will not block - it shouldn't need to block as the queue should always + be empty at this point in the code. */ + xQueueSend( xQueue, &ulValueToSend, 0U ); + } +} +/*-----------------------------------------------------------*/ + +static void prvQueueReceiveTask( void *pvParameters ) +{ +unsigned long ulReceivedValue; + + /* Check the task parameter is as expected. */ + configASSERT( ( ( unsigned long ) pvParameters ) == mainQUEUE_RECEIVE_PARAMETER ); + + for( ;; ) + { + /* Wait until something arrives in the queue - this task will block + indefinitely provided INCLUDE_vTaskSuspend is set to 1 in + FreeRTOSConfig.h. */ + xQueueReceive( xQueue, &ulReceivedValue, portMAX_DELAY ); + + /* To get here something must have been received from the queue, but + is it the expected value? If it is, toggle the LED. */ + if( ulReceivedValue == 100UL ) + { + /* Toggle the LED. */ + port_pin_toggle_output_level( LED_0_PIN ); + ulReceivedValue = 0U; + } + } +} +/*-----------------------------------------------------------*/ + diff --git a/formatting/goodFiles/source/core_mqtt.c b/formatting/goodFiles/source/core_mqtt.c new file mode 100644 index 00000000..6c200fca --- /dev/null +++ b/formatting/goodFiles/source/core_mqtt.c @@ -0,0 +1,3515 @@ +/* + * 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..3a642c3e --- /dev/null +++ b/formatting/goodFiles/source/tasks.c @@ -0,0 +1,6151 @@ +/* + * 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 "stack_macros.h" +#include "task.h" +#include "timers.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 ) */ diff --git a/formatting/goodFiles/source/test.c b/formatting/goodFiles/source/test.c new file mode 100644 index 00000000..26665c49 --- /dev/null +++ b/formatting/goodFiles/source/test.c @@ -0,0 +1,100 @@ +#include +#include +#include +#include + +typedef struct DateAndTime +{ + uint64_t hour; + uint64_t minutes; + uint64_t seconds; + uint64_t msec; +} DateAndTime; + +#if defined( WIN32 ) || defined( _WIN32 ) || defined( __WIN32__ ) || \ + defined( __NT__ ) || defined( WIN64 ) || defined( __WIN64 ) + #include +/* Remove the warning about implicit sleep even with windows.h included */ +extern void sleep( int miliseconds ); +void getTime( struct DateAndTime * currentTime ) +{ + SYSTEMTIME st, lt; + + GetLocalTime( < ); + currentTime->hour = lt.wHour; + currentTime->minutes = lt.wMinute; + currentTime->seconds = lt.wSecond; + currentTime->msec = lt.wMilliseconds; +} +#else /* if defined( WIN32 ) || defined( _WIN32 ) || defined( __WIN32__ ) || \ + defined( __NT__ ) || defined( WIN64 ) || defined( __WIN64 ) */ + #include + #include +void getTime( struct DateAndTime * currentTime ) +{ + struct timeval tv; + struct tm * tm; + + gettimeofday( &tv, NULL ); + tm = localtime( &tv.tv_sec ); + currentTime->hour = tm->tm_hour; + currentTime->minutes = tm->tm_min; + currentTime->seconds = tm->tm_sec; + currentTime->msec = ( int ) ( tv.tv_usec / 1000 ); +} +#endif /* if defined( WIN32 ) || defined( _WIN32 ) || defined( __WIN32__ ) || \ + defined( __NT__ ) || defined( WIN64 ) || defined( __WIN64 ) */ + +int main( int argc, char ** argv ) +{ + DateAndTime currentTime = { 0 }; + int32_t loop = 0; + int32_t totalLoops = 5U; + int32_t exitCode = 0; + + if( argc == 1 ) + { + printf( "This is a basic test application.\n" ); + printf( + "It prints the date and time and then sleeps for loopCount * 3\n" ); + printf( "This program takes in two inputs, a loop count and an exit " + "code\n" ); + printf( "By default it will run %d loops and exit with exit status " + "%d\n", + totalLoops, + exitCode ); + } + + if( argc == 2 ) + { + totalLoops = atoi( argv[ 1 ] ); + printf( "Will run for requested %d loops\n", totalLoops ); + } + + if( argc == 3 ) + { + exitCode = atoi( argv[ 2 ] ); + printf( "Will exit with supplied exit code %d\n", exitCode ); + } + + setvbuf( stdout, NULL, _IONBF, 0 ); + + for( int i = 1U; i < totalLoops; i++ ) + { + getTime( ¤tTime ); + printf( "%02llu:%02llu:%02llu.%03llu TEST APPLICATION SLEEPING FOR %d " + "SECONDS\n", + currentTime.hour, + currentTime.minutes, + currentTime.seconds, + currentTime.msec, + i * 3U ); + sleep( i * 3U ); + } + +#ifdef EXIT_WITH_MINUTES + exitCode = currentTime.minutes; +#endif + printf( "EXITING TEST APPLICATION WITH EXIT CODE = %d\n", exitCode ); + return exitCode; +} diff --git a/formatting/uncrustify.cfg b/formatting/uncrustify.cfg deleted file mode 100644 index 0cb7d3fb..00000000 --- a/formatting/uncrustify.cfg +++ /dev/null @@ -1,160 +0,0 @@ -# Uncrustify-0.67 -input_tab_size = 4 # unsigned number -output_tab_size = 4 # unsigned number -sp_arith = force # ignore/add/remove/force -sp_assign = force # ignore/add/remove/force -sp_assign_default = force # ignore/add/remove/force -sp_before_assign = force # ignore/add/remove/force -sp_after_assign = force # ignore/add/remove/force -sp_enum_assign = force # ignore/add/remove/force -sp_enum_before_assign = force # ignore/add/remove/force -sp_enum_after_assign = force # ignore/add/remove/force -sp_pp_stringify = add # ignore/add/remove/force -sp_bool = force # ignore/add/remove/force -sp_compare = force # ignore/add/remove/force -sp_inside_paren = force # ignore/add/remove/force -sp_paren_paren = force # ignore/add/remove/force -sp_paren_brace = force # ignore/add/remove/force -sp_before_ptr_star = force # ignore/add/remove/force -sp_before_unnamed_ptr_star = force # ignore/add/remove/force -sp_between_ptr_star = remove # ignore/add/remove/force -sp_after_ptr_star = force # ignore/add/remove/force -sp_before_byref = force # ignore/add/remove/force -sp_after_byref = remove # ignore/add/remove/force -sp_after_byref_func = remove # ignore/add/remove/force -sp_before_angle = remove # ignore/add/remove/force -sp_inside_angle = remove # ignore/add/remove/force -sp_after_angle = force # ignore/add/remove/force -sp_before_sparen = remove # ignore/add/remove/force -sp_inside_sparen = force # ignore/add/remove/force -sp_after_sparen = force # ignore/add/remove/force -sp_sparen_brace = force # ignore/add/remove/force -sp_before_semi_for = remove # ignore/add/remove/force -sp_before_semi_for_empty = add # ignore/add/remove/force -sp_after_semi_for_empty = force # ignore/add/remove/force -sp_before_square = remove # ignore/add/remove/force -sp_before_squares = remove # ignore/add/remove/force -sp_inside_square = force # ignore/add/remove/force -sp_after_comma = force # ignore/add/remove/force -sp_after_cast = force # ignore/add/remove/force -sp_inside_paren_cast = force # ignore/add/remove/force -sp_sizeof_paren = remove # ignore/add/remove/force -sp_inside_braces_enum = force # ignore/add/remove/force -sp_inside_braces_struct = force # ignore/add/remove/force -sp_inside_braces = force # ignore/add/remove/force -sp_inside_braces_empty = remove # ignore/add/remove/force -sp_type_func = force # ignore/add/remove/force -sp_func_proto_paren = remove # ignore/add/remove/force -sp_func_def_paren = remove # ignore/add/remove/force -sp_inside_fparens = remove # ignore/add/remove/force -sp_inside_fparen = force # ignore/add/remove/force -sp_fparen_brace = add # ignore/add/remove/force -sp_func_call_paren = remove # ignore/add/remove/force -sp_func_class_paren = remove # ignore/add/remove/force -sp_return_paren = remove # ignore/add/remove/force -sp_attribute_paren = remove # ignore/add/remove/force -sp_defined_paren = remove # ignore/add/remove/force -sp_macro = force # ignore/add/remove/force -sp_macro_func = force # ignore/add/remove/force -sp_brace_typedef = force # ignore/add/remove/force -sp_before_dc = remove # ignore/add/remove/force -sp_after_dc = remove # ignore/add/remove/force -sp_cond_colon = force # ignore/add/remove/force -sp_cond_question = force # ignore/add/remove/force -sp_case_label = force # ignore/add/remove/force -sp_endif_cmt = force # ignore/add/remove/force -sp_before_tr_emb_cmt = force # ignore/add/remove/force -sp_num_before_tr_emb_cmt = 1 # unsigned number -indent_columns = 4 # unsigned number -indent_with_tabs = 0 # unsigned number -indent_align_string = true # false/true -indent_class = true # false/true -indent_class_colon = true # false/true -indent_member = 3 # unsigned number -indent_switch_case = 4 # unsigned number -indent_case_brace = 3 # number -nl_assign_leave_one_liners = true # false/true -nl_class_leave_one_liners = true # false/true -nl_start_of_file = remove # ignore/add/remove/force -nl_end_of_file = force # ignore/add/remove/force -nl_end_of_file_min = 1 # unsigned number -nl_assign_brace = add # ignore/add/remove/force -nl_func_var_def_blk = 1 # unsigned number -nl_fcall_brace = add # ignore/add/remove/force -nl_enum_brace = force # ignore/add/remove/force -nl_struct_brace = force # ignore/add/remove/force -nl_union_brace = force # ignore/add/remove/force -nl_if_brace = add # ignore/add/remove/force -nl_brace_else = add # ignore/add/remove/force -nl_else_brace = add # ignore/add/remove/force -nl_getset_brace = force # ignore/add/remove/force -nl_for_brace = add # ignore/add/remove/force -nl_while_brace = add # ignore/add/remove/force -nl_do_brace = add # ignore/add/remove/force -nl_switch_brace = add # ignore/add/remove/force -nl_multi_line_define = true # false/true -nl_before_case = true # false/true -nl_after_case = true # false/true -nl_func_type_name = remove # ignore/add/remove/force -nl_func_proto_type_name = remove # ignore/add/remove/force -nl_func_paren = remove # ignore/add/remove/force -nl_func_def_paren = remove # ignore/add/remove/force -nl_func_decl_start = remove # ignore/add/remove/force -nl_func_def_start = remove # ignore/add/remove/force -nl_func_decl_args = add # ignore/add/remove/force -nl_func_def_args = add # ignore/add/remove/force -nl_func_decl_end = remove # ignore/add/remove/force -nl_func_def_end = remove # ignore/add/remove/force -nl_fdef_brace = add # ignore/add/remove/force -nl_after_semicolon = true # false/true -nl_after_brace_open = true # false/true -nl_after_brace_close = true # false/true -nl_squeeze_ifdef = true # false/true -nl_before_if = force # ignore/add/remove/force -nl_after_if = force # ignore/add/remove/force -nl_before_for = force # ignore/add/remove/force -nl_after_for = force # ignore/add/remove/force -nl_before_while = force # ignore/add/remove/force -nl_after_while = force # ignore/add/remove/force -nl_before_switch = force # ignore/add/remove/force -nl_after_switch = force # ignore/add/remove/force -nl_before_do = force # ignore/add/remove/force -nl_after_do = force # ignore/add/remove/force -nl_max = 4 # unsigned number -nl_after_func_proto_group = 1 # unsigned number -nl_after_func_body_class = 2 # unsigned number -nl_before_block_comment = 2 # unsigned number -eat_blanks_after_open_brace = true # false/true -eat_blanks_before_close_brace = true # false/true -nl_after_return = true # false/true -pos_bool = trail # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force -align_var_def_amp_style = 1 # unsigned number -align_var_def_thresh = 16 # unsigned number -align_assign_thresh = 12 # unsigned number -align_struct_init_span = 3 # unsigned number -align_typedef_gap = 3 # unsigned number -align_typedef_span = 5 # unsigned number -align_typedef_star_style = 1 # unsigned number -align_typedef_amp_style = 1 # unsigned number -align_right_cmt_span = 3 # unsigned number -align_nl_cont = true # false/true -align_pp_define_gap = 4 # unsigned number -align_pp_define_span = 3 # unsigned number -cmt_cpp_to_c = true # false/true -cmt_star_cont = true # false/true -mod_full_brace_do = add # ignore/add/remove/force -mod_full_brace_for = add # ignore/add/remove/force -mod_full_brace_if = add # ignore/add/remove/force -mod_full_brace_while = add # ignore/add/remove/force -mod_full_paren_if_bool = true # false/true -mod_remove_extra_semicolon = true # false/true -mod_add_long_ifdef_endif_comment = 10 # unsigned number -mod_add_long_ifdef_else_comment = 10 # unsigned number -mod_case_brace = remove # ignore/add/remove/force -mod_remove_empty_return = true # false/true -pp_indent = force # ignore/add/remove/force -pp_indent_at_level = true # false/true -pp_indent_count = 4 # unsigned number -pp_space = remove # ignore/add/remove/force -pp_if_indent_code = true # false/true -# option(s) with 'not default' value: 158 diff --git a/link-verifier/action.yml b/link-verifier/action.yml index f1e99d68..9799234a 100644 --- a/link-verifier/action.yml +++ b/link-verifier/action.yml @@ -24,33 +24,47 @@ inputs: runs: using: "composite" steps: - - name: Install pandoc + - env: + # The bash escape character is \033 + # At time of writing, you can't add a global environment + # to an action file. If this gets changed please move this + bashPass: \033[32;1mPASSED - + bashInfo: \033[33;1mINFO - + bashFail: \033[31;1mFAILED - + bashEnd: \033[0m + stepName: Install Dependencies + name: ${{ env.stepName }} + shell: bash run: | + # ${{ env.stepName }} + echo "::group::${{ env.stepName }}" wget https://github.com/jgm/pandoc/releases/download/2.11/pandoc-2.11-1-amd64.deb -O pandoc.deb sudo dpkg -i pandoc.deb rm pandoc.deb sudo apt install debsums sudo debsums pandoc - shell: bash - - name: Install ghcurl - run: | sudo type -p curl >/dev/null || sudo apt install curl -y - shell: bash - - name: Install gh - run: | curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null sudo apt update sudo apt install -y gh - shell: bash - - name: Install python dependencies - run: | sudo apt-get install -y python3-setuptools python3-pip python3 -m pip install -r $GITHUB_ACTION_PATH/requirements.txt + echo "::endgroup::" + echo -e "${{ env.bashPass }} ${{ env.stepName }} ${{ env.bashEnd}}" + + - env: + bashPass: \033[32;1mPASSED - + bashInfo: \033[33;1mINFO - + bashFail: \033[31;1mFAILED - + bashEnd: \033[0m + stepName: Run link verifier script + name: ${{ env.stepName }} shell: bash - - name: Run link verifier script run: | + # ${{ env.stepName }} + echo "::group::${{ env.stepName }}" args="--verbose --test-markdown" if [ -n "${{ inputs.exclude-dirs }}" ]; then dirs="${{ inputs.exclude-dirs }}" @@ -67,10 +81,17 @@ runs: allowlist_file="${allowlist_file//,/ }" args+=" --allowlist-file ${allowlist_file}" fi - echo "Running verify-links.py ${args} --user-agent \"${{ inputs.user-agent }}\"" - if python3 ${GITHUB_ACTION_PATH}/verify-links.py ${args} --user-agent "${{ inputs.user-agent }}"; then - exit 0 + + echo -e "${{ env.bashInfo }} Running verify-links.py ${args} --user-agent \"${{ inputs.user-agent }}\" ${{ env.bashEnd }}" + set +e + python3 ${GITHUB_ACTION_PATH}/verify-links.py ${args} --user-agent "${{ inputs.user-agent }}"; + exitStatus=$? + set -e + echo "::endgroup::" + # We check the backwards condition for this one + if [ $exitStatus -eq 1 ]; then + echo -e "${{ env.bashFail }} ${{ env.stepName }} ${{ env.bashEnd }}" else - exit 1 + echo -e "${{ env.bashPass }} ${{ env.stepName }} ${{ env.bashEnd }}" fi - shell: bash + exit $exitStatus diff --git a/localhost-echo-server/action.yml b/localhost-echo-server/action.yml index e581e463..5ed733bf 100644 --- a/localhost-echo-server/action.yml +++ b/localhost-echo-server/action.yml @@ -8,7 +8,7 @@ inputs: runs: using: "composite" - steps: + steps: - name: Run localhost Echo server run: | python3 --version diff --git a/localhost-http-1.1-server/action.yml b/localhost-http-1.1-server/action.yml index 796ef662..8b40e667 100644 --- a/localhost-http-1.1-server/action.yml +++ b/localhost-http-1.1-server/action.yml @@ -14,7 +14,7 @@ inputs: runs: using: "composite" - steps: + steps: - name: Install dependencies run: pip install -r $GITHUB_ACTION_PATH/requirements.txt shell: bash diff --git a/localhost-http-1.1-server/localhost_http_1.1_server.py b/localhost-http-1.1-server/localhost_http_1.1_server.py index 3e8d2590..bfb84d0a 100644 --- a/localhost-http-1.1-server/localhost_http_1.1_server.py +++ b/localhost-http-1.1-server/localhost_http_1.1_server.py @@ -30,7 +30,7 @@ def do_GET(self): self.send_header("Content-Length", str(len(response_body))) self.end_headers() self.wfile.write(response_body) - + def do_PUT(self): # Receive the body of the request - don't do anything with it, # but this needs to be done to clear the receiving buffer. @@ -60,7 +60,7 @@ def do_POST(self): self.send_header("Content-Length", str(len(response_body))) self.end_headers() self.wfile.write(response_body) - + def do_HEAD(self): # Receive the body of the request - don't do anything with it, # but this needs to be done to clear the receiving buffer. diff --git a/localhost-mqtt-broker/action.yml b/localhost-mqtt-broker/action.yml index 83911452..f3579255 100644 --- a/localhost-mqtt-broker/action.yml +++ b/localhost-mqtt-broker/action.yml @@ -14,7 +14,7 @@ inputs: runs: using: "composite" - steps: + steps: - name: Install dependencies run: pip install -r $GITHUB_ACTION_PATH/requirements.txt shell: bash diff --git a/manifest-verifier/action.yml b/manifest-verifier/action.yml index acbb98e7..a7c9c58a 100644 --- a/manifest-verifier/action.yml +++ b/manifest-verifier/action.yml @@ -9,23 +9,52 @@ inputs: description: 'List of comma-separated relative path to submodules that should not be present in manifest.yml. Eg libraries/thirdparty/tinycbor,libraries/thirdparty/mbedtls' required: false default: '' - fail-on-incorrect-version: + fail-on-incorrect-version: description: 'Boolean flag to indicate if verification should fail if any submodule version in manifest.yml file is incorrect or stale.' required: false default: 'false' runs: using: "composite" - steps: - - name: Install dependencies - run: pip install -r $GITHUB_ACTION_PATH/requirements.txt + steps: + - env: + bashPass: \033[32;1mPASSED - + bashInfo: \033[33;1mINFO - + bashFail: \033[31;1mFAILED - + bashEnd: \033[0m + stepName: Install Manifest Verification Dependencies + name: ${{ env.stepName }} shell: bash - - name: Run verifier script + run: | + # ${{ env.stepName }} + echo "::group::${{ env.stepName }}" + pip install -r $GITHUB_ACTION_PATH/requirements.txt + exitStatus=$? + echo "::endgroup::" + echo -e "${{ env.bashPass }} ${{ env.stepName }} ${{ env.bashEnd }}" + + - env: + bashPass: \033[32;1mPASSED - + bashInfo: \033[33;1mINFO - + bashFail: \033[31;1mFAILED - + bashEnd: \033[0m + stepName: Run Manifest Verification Script + name: ${{ env.stepName }} working-directory: ${{ inputs.path }} + shell: bash run: | + # ${{ env.stepName }} + echo "::group::${{ env.stepName }}" if [[ "${{ inputs.fail-on-incorrect-version }}" == "true" ]]; then - echo 'Value of flag is ${{ inputs.fail-on-incorrect-version }}' + echo -e "${{ env.bashInfo}} Value of flag is ${{ inputs.fail-on-incorrect-version }} ${{ env.bashEnd }}" python3 $GITHUB_ACTION_PATH/verify_manifest.py --ignore-submodule-path=${{ inputs.exclude-submodules }} --fail-on-incorrect-version else python3 $GITHUB_ACTION_PATH/verify_manifest.py --ignore-submodule-path=${{ inputs.exclude-submodules }} fi - shell: bash + exitStatus=$? + echo "::endgroup::" + if [ "$exitStatus" = "0" ]; then + echo -e "${{ env.bashPass }} ${{ env.stepName }} ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} ${{ env.stepName }} ${{ env.bashEnd }}" + exit 1 + fi diff --git a/manifest-verifier/verify_manifest.py b/manifest-verifier/verify_manifest.py index 0389ed21..68a712a1 100644 --- a/manifest-verifier/verify_manifest.py +++ b/manifest-verifier/verify_manifest.py @@ -7,22 +7,33 @@ from git import Repo from argparse import ArgumentParser +# At time of writing GitHub Runners will select Bash by default. +bashPass= "\033[32;1mPASSED -" +bashInfo= "\033[33;1mINFO -" +bashFail= "\033[31;1mFAILED -" +bashEnd= "\033[0m" + REPO_PATH='' # List of submodules excluded from manifest.yml file IGNORE_SUBMODULES_LIST = [] # Obtain submodule path of all entries in manifest.yml file. -def read_manifest(): +def read_manifest(git_modules, path_manifest): dict = {} - # Read YML file - path_manifest = os.path.join(REPO_PATH, 'manifest.yml') - assert os.path.exists(path_manifest), 'Missing manifest.yml' + with open(git_modules, 'r') as fp: + module_lines = fp.read() + if "submodule" not in module_lines: + print("{0} No submodules in the repo. Exiting. {1}".format(bashInfo, bashEnd)) + exit(0) + with open(path_manifest, 'r') as fp: manifest_data = fp.read() yml = load(manifest_data, Loader=Loader) - assert 'dependencies' in yml, 'Manifest YML parsing error' + if "dependencies" not in yml: + print("{0} No dependencies in {0}. Exiting {1}".format(bashInfo, path_manifest, bashEnd)) + exit(0) # Iterate over all the "dependencies" entries, verify that # each contains entries for the following hierarchy: @@ -34,8 +45,10 @@ def read_manifest(): # path: # for dep in yml['dependencies']: - assert 'version' in dep, "Failed to parse 'version/tag' for submodule" - assert 'repository' in dep and 'path' in dep['repository'] and 'url' in dep['repository'], "Failed to parse 'repository' object for submodule" + assert 'version' in dep, f"Failed to parse 'version/tag' for submodule {dep}" + assert 'repository' in dep, f"Failed to parse 'repository' for {dep}" + assert 'path' in dep['repository'], f"Failed to parse 'path' for {dep}" + assert 'url' in dep['repository'], f"Failed to parse 'repository' object for {dep}" dict[dep['name']] = (dep['repository']['path'], dep['version']) return dict @@ -75,30 +88,36 @@ def get_all_submodules(): # Convert any relative path (like './') in passed argument to absolute path. REPO_PATH = os.path.abspath(args.repo_root_path) + # Read YML file + path_manifest = os.path.join(REPO_PATH, 'manifest.yml') + assert os.path.exists(path_manifest), f"{bashFail} NO FILE {REPO_PATH}/manifest.yml {bashEnd}" - submodules_info_from_manifest = read_manifest() + git_modules = os.path.join(REPO_PATH, '.gitmodules') + assert os.path.exists(path_manifest), f"{bashFail} NO FILE {REPO_PATH}/.gitmodules {bashEnd}" + + submodules_info_from_manifest = read_manifest(git_modules, path_manifest) submodule_path_from_manifest = [pair[0] for pair in submodules_info_from_manifest.values()] submodule_path_from_manifest = sorted(submodule_path_from_manifest) submodules_info_from_git = get_all_submodules() submodule_path_from_git = sorted(submodules_info_from_git.keys()) - print(REPO_PATH) - print('\nList of submoduled libraries being verified:', submodule_path_from_git) + print("{0} CHECKING PATH: {1}{2}".format(bashInfo, REPO_PATH, bashEnd)) + print("{0} List of submodules being verified: {1}{2}".format(bashInfo, submodule_path_from_git, bashEnd)) # Check that manifest.yml contains entries for all submodules # present in repository. if submodule_path_from_manifest != submodule_path_from_git: - print('manifest.yml is missing entries for:') + print("{0} manifest.yml is missing entries for:{1}".format(bashFail, bashEnd)) # Find list of library submodules missing in manifest.yml for git_path in submodule_path_from_git: if git_path not in submodule_path_from_manifest: - print(git_path) + print(f"{bashFail} git_path {bashEnd}") sys.exit(1) # Verify that manifest contains correct versions of submodules pointers. mismatch_flag = False - print('\nVerifying that manifest.yml versions are up-to-date.....') + print(f"{bashInfo} Verifying that manifest.yml versions are up-to-date..... {bashEnd}") for submodule_name, submodule_info in submodules_info_from_manifest.items(): relative_path = submodule_info[0] manifest_commit = submodule_info[1] @@ -106,13 +125,17 @@ def get_all_submodules(): submodule.remote('origin').fetch() submodule.git.checkout(manifest_commit) if (submodules_info_from_git[relative_path] != submodule.head.commit): - print('manifest.yml does not have correct commit ID for', submodule_name,'manifest Commit=(',manifest_commit, submodule.head.commit,') Actual Commit=',submodules_info_from_git[relative_path]) + print("{0} manifest.yml does not have correct commit ID for {1} manifest Commit=({2},{3}) Actual Commit={4} {5}" + .format(bashFail, submodule_name, manifest_commit, submodule.head.commit, submodules_info_from_git[relative_path], bashEnd)) mismatch_flag = True - - if mismatch_flag and args.fail_on_incorrect_version: + + if ( True == mismatch_flag ) and args.fail_on_incorrect_version: + print(f"{bashFail} MISMATCHES WERE FOUND IN THE MANIFEST. EXITING WITH FAILURE DUE {bashEnd}") sys.exit(1) + elif ( True == mismatch_flag ): + print(f"{bashInfo} MISMATCHES WERE FOUND IN THE MANIFEST. EXITING WITH SUCCESS AS FAIL ON INCORRECT VERSION WAS NOT SET {bashEnd}") - print('\nmanifest.yml file has been verified!') + print("{0} manifest.yml file has been verified!{1}". format(bashPass, bashEnd)) sys.exit(0) diff --git a/memory_statistics/memory_statistics.py b/memory_statistics/memory_statistics.py index ff3264b0..b78d7870 100755 --- a/memory_statistics/memory_statistics.py +++ b/memory_statistics/memory_statistics.py @@ -82,7 +82,7 @@ def parse_make_output(output, values, key): values[filename][key] = total_size_in_kb -# This outputs an object that maps to the report JSON format, and can be passed to +# This outputs an object that maps to the report JSON format, and can be passed to # generate_table_from_object() for library HTML format. def parse_to_object(o1_output, os_output, name, filename_map): sizes = defaultdict(dict) @@ -199,7 +199,7 @@ def generate_library_estimates(config_path): def parse_arguments(): parser = argparse.ArgumentParser() - parser.add_argument('-c', '--config', required=True, help='Configuration json file for memory estimation. ' + + parser.add_argument('-c', '--config', required=True, help='Configuration json file for memory estimation. ' + 'This is the library config file, unless --json_report is used, ' + 'in which case it is the report paths config.') parser.add_argument('-o', '--output', default=None, help='File to save generated size table to.') @@ -216,13 +216,13 @@ def main(): if not args['json_report']: # Generate HTML file for a library. The config parameter is the library config path. - # See test/memory_statistics_config.json for an example. + # See test/memory_statistics_config.json for an example. lib_data = generate_library_estimates(args['config']) doc = generate_table_from_object(lib_data) else: # Generate a JSON report for the given libraries. The config parameter is the path to the report paths config. - # See paths.json for a paths config for the FreeRTOS repository. + # See paths.json for a paths config for the FreeRTOS repository. with open(args['config']) as paths_file: libs = json.load(paths_file) diff --git a/rust-spell-check/Cargo.lock b/rust-spell-check/Cargo.lock new file mode 100644 index 00000000..c9e52fca --- /dev/null +++ b/rust-spell-check/Cargo.lock @@ -0,0 +1,535 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bindgen" +version = "0.59.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "clap 2.34.0", + "env_logger", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "which", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clang-sys" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cc00842eed744b858222c4c9faf7243aafc6d33f92f96935263ef4d8a41ce21" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim 0.8.0", + "textwrap 0.11.0", + "unicode-width", + "vec_map", +] + +[[package]] +name = "clap" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5177fac1ab67102d8989464efd043c6ff44191b1557ec1ddd489b4f7e1447e77" +dependencies = [ + "atty", + "bitflags", + "clap_derive", + "indexmap", + "lazy_static", + "os_str_bytes", + "strsim 0.10.0", + "termcolor", + "textwrap 0.14.2", +] + +[[package]] +name = "clap_derive" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d42c94ce7c2252681b5fed4d3627cc807b13dfc033246bd05d5b252399000e" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "cstr" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a60f0dd132e4b67f20fd764d4835d968f666ff1a2f59e432983d168b98424deb" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "env_logger" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "indexmap" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libaspell-sys" +version = "0.1.0" +dependencies = [ + "bindgen", +] + +[[package]] +name = "libc" +version = "0.2.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" + +[[package]] +name = "libloading" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109" +dependencies = [ + "memchr", + "minimal-lexical", + "version_check", +] + +[[package]] +name = "os_str_bytes" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" +dependencies = [ + "memchr", +] + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "shlex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" + +[[package]] +name = "spell-checker" +version = "0.1.0" +dependencies = [ + "clap 3.1.2", + "cstr", + "libaspell-sys", + "tree-sitter", + "tree-sitter-c", + "tree-sitter-cpp", + "tree-sitter-python", +] + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "termcolor" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "textwrap" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80" + +[[package]] +name = "tree-sitter" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e34327f8eac545e3f037382471b2b19367725a242bba7bc45edb9efb49fe39a" +dependencies = [ + "cc", + "regex", +] + +[[package]] +name = "tree-sitter-c" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bdc5574c6cbc39c409246caeb1dd4d3c4bd6d30d4e9b399776086c20365fd24" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-cpp" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a869e3c5cef4e5db4e9ab16a8dc84d73010e60ada14cdc60d2f6d8aed17779d" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-python" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83c46916553ebc2a5b23763cd2da8d2b104c515c8f828eb678d1477ccd8c379c" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "which" +version = "4.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a5a7e487e921cf220206864a94a89b6c6905bfc19f1057fa26a4cb360e5c1d2" +dependencies = [ + "either", + "lazy_static", + "libc", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/rust-spell-check/Cargo.toml b/rust-spell-check/Cargo.toml new file mode 100644 index 00000000..a277e5f6 --- /dev/null +++ b/rust-spell-check/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "spell-checker" +version = "0.1.0" +edition = "2021" + +[dependencies] +tree-sitter = "0.20.4" +tree-sitter-c = "0.20.1" +tree-sitter-cpp = "0.20.0" +tree-sitter-python = "0.19.1" +libaspell-sys = { path = "crates/libaspell-sys" } +cstr = "0.2.10" +clap = { version = "3.1.2", features = ["derive"] } + +[workspace] +members = ["crates/libaspell-sys"] + +[profile.release] +lto = "thin" +panic = "abort" diff --git a/rust-spell-check/action.yml b/rust-spell-check/action.yml new file mode 100644 index 00000000..c4f7276a --- /dev/null +++ b/rust-spell-check/action.yml @@ -0,0 +1,119 @@ +name: 'rust-spell-check' +description: 'Rust CI spellings check' +inputs: + path: + description: 'Path to repository folder to check spellings in.' + required: false + default: ./ + lexicon: + description: 'Path to lexicon file to check spellings with' + required: false + default: lexicon.txt + exclude-dirs: + description: "Comma separated list of directories to not spell check" + required: false + exclude-files: + description: "Comma separated list of files to not spell check" + required: false + include-extensions: + description: "Comma separated list of files to match to regex" + required: false + + +runs: + using: "composite" + steps: + - env: + bashPass: \033[32;1mPASSED - + bashInfo: \033[33;1mINFO - + bashFail: \033[31;1mFAILED - + bashEnd: \033[0m + stepName: Install Dependencies + name: ${{ env.stepName }} + id: spell-checker-setup + shell: bash + run: | + # ${{ env.stepName }} + echo "::group::${{ env.stepName }}" + + # Install the Dependencies we need to run the spell-checker + sudo apt-get install util-linux -y + sudo apt-get install fd-find -y + sudo apt-get install aspell -y + sudo apt-get install spell -y + + # Check if we can use the current spell checker + # This is not added to the path by default. + # I honestly do not know why + export PATH="$GITHUB_ACTION_PATH:$PATH" + echo -e " ${{ env.bashInfo }} Check for pre-build spell checker ${{ env.bashEnd }}" + + # Wrap the check to use if in a set +e so we don't error out if it fails + # Save the exit code to check later, as "set -e" will overwrite it + set +e + spell-checker + exitCode=$? + set -e + if ! [ $exitCode -eq 0 ]; then + echo "::endgroup::" + echo -e " ${{ env.bashFail }} Don't have the ability to use the current spell checker, building it ${{ env.bashEnd }}" + echo "::group::Compile Spell Checker" + + # If we can't run the current one, install the tools we need to build it + # Run one a time for error checking + sudo apt-get install libaspell-dev -y + sudo apt-get install build-essential -y + sudo apt install rustc -y + + echo -e "${{ env.bashInfo }} cargo --version = $(cargo --version) ${{ env.bashEnd }}" + echo -e "${{ env.bashInfo }} rustc --version = $(rustc --version) ${{ env.bashEnd }}" + pushd "$GITHUB_ACTION_PATH" + cargo build --release + echo -e "find = $(find . -name 'spell-checker') " + # It's possible that we have one in the directory, but just can't suse it + # set +e so we don't error when overriding it + set +e + mv $(find . -name "spell-checker") . + set -e + popd + spell-checker --help + echo "::endgroup::Compile Spell Checker" + + # Only make it to here if nothing above fails + echo -e "${{ env.bashPass }} Compiled the Spell Checker ${{ env.bashEnd }}" + fi + echo "::endgroup::" + # Only get to here if nothing above fails + echo -e "${{ env.bashPass }} ${{ env.stepName }} ${{ env.bashEnd }}" + + - env: + bashPass: \033[32;1mPASSED - + bashInfo: \033[33;1mINFO - + bashFail: \033[31;1mFAILED - + bashEnd: \033[0m + stepName: Run Spell Checker + name: ${{ env.stepName }} + id: run-spell-checker + working-directory: ${{ inputs.path }} + shell: bash + run: | + # ${{ env.stepName }} + #echo "::group::${{ env.stepName }}" + export PATH="$PATH:$GITHUB_ACTION_PATH" + + # So here's the deal. At time of writing this spell checker can check + # every word in every folder in FreeRTOS/FreeRTOS in like 10 seconds. + # So I just let it do that. If this changes in the future, feel free to use + # The various exclude dir/file options + # files=$(getFiles --exclude-dirs="${{ inputs.exclude-dirs}}" --exclude-files="${{ inputs.exclude-files }}" --include-extensions="${{ inputs.include-extensions }}") + files=$(fdfind -e c -e h) + spell-checker -c -w .cSpellWords.txt $files + exitStatus=$? + + #echo "::endgroup::" + if [ $exitStatus -eq 0 ]; then + echo -e "${{ env.bashPass }} ${{ env.stepName }} ${{ env.bashEnd }}" + else + echo -e "${{ env.bashFail }} ${{ env.stepName }} ${{ env.bashEnd }}" + exit 1 + fi diff --git a/rust-spell-check/crates/libaspell-sys/Cargo.toml b/rust-spell-check/crates/libaspell-sys/Cargo.toml new file mode 100644 index 00000000..57a114ae --- /dev/null +++ b/rust-spell-check/crates/libaspell-sys/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "libaspell-sys" +version = "0.1.0" +edition = "2021" + +[dependencies] + +[build-dependencies] +bindgen = "0.59.2" diff --git a/rust-spell-check/crates/libaspell-sys/build.rs b/rust-spell-check/crates/libaspell-sys/build.rs new file mode 100644 index 00000000..18041268 --- /dev/null +++ b/rust-spell-check/crates/libaspell-sys/build.rs @@ -0,0 +1,18 @@ +use std::env; +use std::path::PathBuf; + +fn main() { + println!("cargo:rustc-link-lib=aspell"); + println!("cargo:rerun-if-changed=wrapper.h"); + + let bindings = bindgen::Builder::default() + .header("wrapper.h").clang_arg("-I/opt/homebrew/Cellar/aspell/0.60.8/include/") + .parse_callbacks(Box::new(bindgen::CargoCallbacks)) + .generate() + .expect("Unable to generate bindings"); + + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} diff --git a/rust-spell-check/crates/libaspell-sys/src/lib.rs b/rust-spell-check/crates/libaspell-sys/src/lib.rs new file mode 100644 index 00000000..a38a13a8 --- /dev/null +++ b/rust-spell-check/crates/libaspell-sys/src/lib.rs @@ -0,0 +1,5 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); diff --git a/rust-spell-check/crates/libaspell-sys/wrapper.h b/rust-spell-check/crates/libaspell-sys/wrapper.h new file mode 100644 index 00000000..b5c95cfe --- /dev/null +++ b/rust-spell-check/crates/libaspell-sys/wrapper.h @@ -0,0 +1 @@ +#include diff --git a/rust-spell-check/getFiles b/rust-spell-check/getFiles new file mode 100755 index 00000000..b7112880 --- /dev/null +++ b/rust-spell-check/getFiles @@ -0,0 +1,82 @@ +#!/bin/bash +bashPass="\033[32;1mPASSED -" +bashInfo="\033[33;1mINFO -" +bashFail="\033[31;1mFAILED -" +bashEnd="\033[0m" + +output=$(getopt -V) +if [[ *"getopt"* == "$output" ]] && [ $# != 0 ]; then + echo -e "$bashInfo This script cannot parse options from Mac command line's default 'getopt' $bashEnd" + echo -e "$bashInfo Run \"brew install gnu-getopt\" and then add it to your path using $bashEnd" + echo -e "$bashInfo export PATH=\"/opt/homebrew/opt/gnu-getopt/bin:\$PATH\" $bashEnd" + echo -e "$bashInfo At which point you can then use this script with options $bashEnd" + exit 1; +fi +# Check number of arguments +files="" +file="" +excludeDirs="" +excludeFiles="" +includeExtensions="" +if [ $# != 0 ]; then + VALID_ARGS=$(getopt -o h,ed:,ef:,if: --long help,exclude-dirs:,exclude-files:,include-extensions: -- "$@") + eval set -- "$VALID_ARGS" + while [ : ]; do + case "$1" in + ed | --exclude-dirs ) + # $2 Holds the argument passed after --exclude-files or --ed + # Use sed to replace the commas with the exclude flag + if ! [ -z "$2" ]; then + excludeDirs="-E $(echo "$2" | sed -r 's/,/ -E /g' )" + fi + shift 2 + ;; + + ef | --exclude-files ) + # $2 Holds the argument passed after --exclude-files or --ed + # Use sed to replace the commas with the exclude flag + if ! [ -z "$2" ]; then + excludeFiles="-E $( echo "$2" | sed -r 's/,/ -E /g' )" + fi + shift 2 + ;; + + if | --include-extensions ) + # $2 Holds the argument passed after --exclude-files or --ed + # Use sed to replace the commas with the exclude flag + if ! [ -z "$2" ]; then + includeExtensions="-e $( echo "$2" | sed -r 's/,/ -E /g' )" + fi + shift 2 + ;; + + h | --help ) + echo -e "$bashInfo Find all .c and .h files with the Amazon copyright in them $bashEnd" + echo -e "$bashInfo It exports this to a bash array variable called \"files\" $bashEnd" + echo -e "$bashInfo This script can take in two optional arguments $bashEnd" + echo -e "$bashInfo --exclude-files: A comma seperated list of files to exclude $bashEnd" + echo -e "$bashInfo --exclude-dir: A comma seperated list of directories to exclude $bashEnd" + echo -e "$bashInfo --include-extensions: Any additional exstensions to search for $bashEnd" + exit 0 + ;; + -- ) + shift + break + ;; + esac + done +fi + +# We know we just want the .c and .h files +# It's just way easier to do this then it is to write +# A complicated thing to handle parsing the arguments +#echo "Hello" +#echo "fdfind -e c -e h "$excludeDirs" "$excludeFiles" "$includeExtensions" --exec grep -lriE \"copyright (.*) [0-9]{4} amazon.com\" " +files=$(fdfind -e c -e h $excludeDirs $excludeFiles $includeExtensions --exec grep -lriE "copyright (.*) [0-9]{4} amazon.com" ) + +finalFiles=("") +for file in ${files[@]}; do + if ! [ -z "$file" ]; then + echo "$file" + fi +done diff --git a/rust-spell-check/queries/c.scm b/rust-spell-check/queries/c.scm new file mode 100644 index 00000000..178355c6 --- /dev/null +++ b/rust-spell-check/queries/c.scm @@ -0,0 +1,2 @@ +(comment) @comment +(string_literal) @string diff --git a/rust-spell-check/queries/python.scm b/rust-spell-check/queries/python.scm new file mode 100644 index 00000000..8a58e304 --- /dev/null +++ b/rust-spell-check/queries/python.scm @@ -0,0 +1,2 @@ +(comment) @comment +(string) @string diff --git a/rust-spell-check/spell-checker b/rust-spell-check/spell-checker new file mode 100755 index 0000000000000000000000000000000000000000..b6f19af16a791b28027fe50f6ace61c4663adcba GIT binary patch literal 6295800 zcma&v0hk+CdH(H5vJizB7{Dch8cwWm*gfFhwl})M8+uhN!`i0Zgj4ZaWkQ1Q?i7Qv@~TFYP>&W`3W~ zb$!ZZYjx|@d(L@uW~7zl^=qCr_q*qwbB>i>&a-~Un%*jW@w`=Xbw}g=HGO4Su2r(u zOn!g7b%}NViOx)Zxw@Cv{<+B=>2*$Z)9%y9MY^BY>r|Isr!$;hPakKx)SjNqJH58- zq;mSW`%S-ellysnPXD2+h3WNuAD4{x@YVY*D|`LPBfqig>GgddH_gVq^C_$MyZ@ZM zw!WHlt>2~9Xr9wJrjKhr^l7V)YhIkac9d6oo#r|1GW~T%_w#zwYB$SDucx)s%lVUE z)2~hQEKZ9i7caT8((x+ytSn9+7yr{0tM~Bw;8T+aq}SQ|Qa5?wFox;DAqGWqpV-QKF}%lfZ<;4@zR_C0qz zD0=#1Z+Yzl|Lfu3`RT8}_wLVpZRxAqZ=c+L`bVGoK$ZS~t}dEv_GCwA@49L-(X;tA zhWh!*V$W88#Uz-sd3O@b+5FX$`irwuS-tgXL;s%}e7m9l^sqkLxVIbXpKh4{CPV%E z47t72Q2(EX`Mhtk>a)%B97FxD8~T6UP`}l%uE!Y0y=&6{;l@~C(6HWn41GRt@Rngdw;OVMjG=yH zn9unJ9~sv5IfH-DFz!1H&&yH6xIZxD@Fqk3uMGZ4!+gGG@Mll@Ts#?@moHCzdT!@s z&jikP{9bCvr*5b}Zg?IK8RofcSnuZyeJ(NN-!Sy~xS{?AL;sH$`aHKg08~Wa$4ygI_+GXJPV-mz-hTSwo*U8tT7e$oZ|40nfJIZa4IQvmu}9 zdzbIK!N-RF?>G3h2H$7M|K|pOxuMTf4Zh8=-rt(|#b;UXdklRJ8{C?_f84#Y zxb+E7n7(Jc(pp%!Y5UF{3%ggg>{?k^u)LM}uB9!D3pX#_wy<^b)yk5!aLxR+3yVv; zmTua%du3_Y{Iypv@7%F8zvZRNy6>#_q>XM`TG{O_ZQXX`wyl}wi%ZK(ljdpJ?)#K2 zyy*Is%bkUV$y9dUy0E;orKw znZL-@Y z&%=UOU)*JF-@e7Wa@N{Cxo^v|wQcvUyS?e0r_b(|=}wt$+DY-G-ty#*$(G)F z^W>?ydEv$_+m@|K#q#pbt=5*8?%cIvO~%<;pLAW?zH55DFo|mS(&PwOEuD1VvOIZu zCc|u5S-EX>Y-?-s@X2&1J90AXEh~-b!zUYe^61I)v18}Lr2UqaZ98{ZJH4eHlgUme zwK~%3@|LHQy3w0#zm*%O)3R>#ZdutnUD1~1mDS0v)_S*DH*Q_txqHdlKG`-aOIs(C zO@^MXz}vD?wN{Pnu0*y>Z+0kb33hQMc`$E`P!e@22S? zl2toYFx?C~ z`E1{^ZIbI`q{&fW-MDLM`s6mIIZviF2`>F|@(7(YrklvQcM6l%tFzcK*-H2Bhsmra zo`gGj;7wa6gRLIM*1`*S+`4VY;=&7-c2ADsdv9Jf*)x+VPIs0Hp1rg(w`Ipow@ePS z>vbaAv}I+>eeb`fa?Oq#civZ~6)#v?zG}u;HCJ7|aQUT|UwVagb+!7;tFBqN?9wM&t6Z%vJo(bu zOE16dKKESu)cf9-d_8@5>E17A@BQksr~KcS_tF0=opisqLSs&QU12@@>Z>P1JFC}| z3FxaQPbQXp#&F)`Q;O+-_nUmWFnLK^{d)SDP4ZRlUc110z~r->3s>*AtP3XB$=45@ zd}1?we9n5%H~{ zUVW!@_vH8KV_rUf$K)M@ZGB6(Z~NNECR2Hk73=oOC4V~kXewv@a&mimIcL)QwX2V? zEPrylb^F^VAJ06*x?uAAwBOoo+a{l#JlI;R+wWMt|AE${b$kEn?KRdVy4_j5{QzrL zw^tr9o&WjPGj#i5tNre8Jy*B?c(wk1)(dqzS?|n!)_b1y3*|AHj#c@s+a~L|(5k7v zR(sr}|9Mtjx36El|6FU=$#*Lr-1^ewjm_lJfCu_~w+XMj^O39d zE%>~=4PTIV;B~nVf0?`s-zD$CSLA(oLmt3iDIdW1$U}HjK7_wP9>L!tAHm-)kKqU8 zWB6ant$#^A7fzqY!}1(F&BulZs?Wm*@&f!$c@ch0?!dn)FTq2(3;(*j3_mWf!0(n< z;r}GB!B5ER@PCtg@JQZ(|A)K@zenDJkK}Fm_vIb<59K~QmUrPlk@w&~mG|Lec>w=~ zd;mZ9UCD7B!Y_~y;TOpx_#@>b_@m@8ydWRLpCGqRWcUA*g@09EgNO1u{OfWLeq7#w-z{&#hw>Ku-{o!i zNZx_}Snk8m$h+`!-<|Bw9{fUiAHG%|z}LwK@b&T#eyMy2zd|0te@8xoUoDT}*T~24 zs@(e5?Eb$&o`Y|d+whmk^KegIfbWtQ;kU>g_$%cl_#eq#`0M3m_*>)^_vY)AA1dvvME)d3hIpRNjN%A@9S#EDzv!$p`SS z%R~4#M=bK7#+7Jci#RAH%;ZxBe}=|GzKK!GA8d;b-J|_^;#z__+s?^Gp$b zf4KwC$xHBcau@y>c^Q7OyaIo$yb8ZWUV}eDUWY$Z?!ligZ@{<8oAA241>YfW!*|O& z@K?!w_+EJz{wMMt{B80+{N3^Zeo#Jue?%U_KQ14_KO>LeUyzUBUzW%4^nK$Peq8m| zzi0RVzshs)lX4sW19={PT3&$vLSBT=yeBy>9QXs|C3s%$!Y`JW;ZKlP;7^iQ;aA9O z@ZXWw;aAB$`0vUa@N48v_?)~2f1bPzpO<&wo8>YIzU-R(T)(UU>ljhKpWAHjbrkKyP3d9ts@@U*`bW%qyDKL=0y+wio19{ymBTY&$jya?YY zci?mK68r|a3*Rm;!(Sz@z~3yd!v8{EgMU%u@Kf?8{M`2@&sPinaCsYE zkayr$%6<5A)C85dLL(1V14k!T(bp8+>eV>s#6V zZ~IC9Ir!yr+u(VF7vN>}DH_}{cnSVu^>N|1%F70?7`zI9lls))U3uN$p1~XNzg3?m z{2THX{M+(2{J-TL_=El;c^-ZEqvT!q6XiYlRq{Uk`SJk1MLvMa(7G5p(d>p!yl|0nVsJolGLp0>gB@JFe>0DqFa2;U@k;Ln$r;4hWC@R!TW z@IR7Q;BS#v;Rob3_($b+_-EuEJd`)!|1NLBe;{wc&&b>GhrKU(UOMng*`SUq$#uatHndc?teBxeHIf?<>QvRec41gS-mgCa=L?C9lKZ zF8APnBX7Wu%A4@JFTHb+Q zFZba~@-BRjya&Hs-iLog9>6~%AHeUDhw$&ohwyZtMDSmzegwauo8%wEA0;2d9l3Q+ zcK^Hb9Q-=D4S%sb58o*-z+Wvd!rv@+;O~`};D0T5;djc*@Nda02Cu?@q52y90Ut_o zt{dDlcmw_j^=ZPNAa5DGZSao4eYmUsUHEnK9(=3358opX;2rq@{zZ8R|F(Px|Cv03 zKlsB*{v&u%9>dG>G5kik^_}eg-z(3--z&G_pO)w0LwN!IOL-Cg$d4rJa^RQCOYk|l z3*Ra)!(S<{z}xaF{6q2@{0s6r{NLmr{HO8;+&+}7w+VOTEqF!VhBxFL_@B#tcwgRy z|D(JIkL7*%Lq3|!KY(8>AHbg?58-q25qz^ehS%j|_{-(iceDHXRq`CXDYxN&EYHK= zA}_!@@*@18+<|{gUVmU4^@!Nd8`O z4Q_or@jBcM68GTEzfZgYU-Qw#oA83X1$W<`)VJZzmmjve-VWS*)bwvATh<8v&^IUh zKZfVumiQR{Xw_TFFa7@vFPG=wSIKSooIDSIz^9XS72wuq6EDJl{>j80_&#|FUe&lR z{7+S1hIix@_(6FUzNG#&_)l+7@~p$(^rwk?@XJ--fPYxyHsQ6mCVg7)H)=j@_}o{L z+&b_p`icASk7?X4`~&JAz@MG|e}nKfIxa%^C)Ixl|M4Bkd?NVgR6l|r(R^a~9jYJ0 zKdXA{``Po)wRa`s=HOpe9~*v`JP*HHUV#6rya>NX?!bQ_FTwv)?!wReR5JfEJk6&9 zf6Lz{^;P(T)u#rpzD4^PezW$E2j3xYz#peRP5ATWE%w zZ^&(UQJ#liDKEgEDlft}${qMK+rpD4}QD6 z0snx!3ICkD1^-8R8-BOE1OIdF2OsXsyYTLv$$6j$f4chj;V)620RA7UAHcsS58*$O z58*fJJRHG)ruq^5m+}~Xf1Q8E@CVDSQ`rCV9Q-1=4KK*^@W;vv@F&QN@TbWg_%q}s z`0vVH_*0Za8GcN!7Zvz-EbqZjs81h$p1$u0 z;NMdH0R9ts2tQZvKZo!K$s_o0%17|a*G&>H+xs;|Q@QoRR%=8@!mMFaj1 z>eGb(le`6wL-=RKL_)uPhe@cDo@b9YLgMUc%4R}G`gm00z z;4hcA;iolj2cG*(a{lq*=gGV96M8-E!Plz34}ZKofTyps19=iqNw9~=Hbc^;m=KPkZfUiC%zak&G(M_z*eLhiyZ z)SnlW;s2%Qw*t?rz6yVwyas=oybgb!+=JKU4fsH>k4^Y%RNsQX>T}6{Zo}WB`VRaP zavwg{xLx>HRNsUDK;DO6p!o#w_3{DSm51;fP10d+^>c>C=aYst@7r z^at%uE<<=)AH&_dlln0{t+!8S^R$mA^?7(&@6gr1MA!T)@Zhebe-)ng_uv(+w*gP< z+wj^qll~oeTHk~FT5lho)`xIEO!^PuX?+Zj|1qf_!_#{Er`h~#UrXxq@U-56JKsp^ zOYmNrKfLy$WPeuSf$BZD`{Ja&0Z;q4;lVZ2pVwts9e6tb9=!01q<PPVA&yqedJkp4OM)6&-gKc%+=`@Nh@c--D<1Ex5lWsc*x3+HYNWI{zL#tslU{T}l5CzBrTY z+mXSoaW?0)K5uXb?&)|e!_)a!;Awpwp3dKcKmLBn{9Ev2av%PC_fP8k@Vn)q!AAzS zexA)I?UOgSWAHNkv*#!Ctip$K&)_YC`v&hDJT&;o;MOm)Ii&N<8{9E?8UB%6l0(hl z4R~-^$C1IiaQEv;eGi`Y8Nk!=LW2(t9>LRbNAR|8$8cx*!zCw|F}x|a{wJHiduLLg zgZJdN!Sirmc{*_W8%h5XJgu+5-80Gjss^taybe#t_2B8e8}Rtc$vm6z=CQAdssSUD8n!B>(zi|{m0 z2cG6xGI+XQ?w!B(VHNdhJ|5h3AK3`v&jA({X$7bl!cq7bf!z;MVjX zbeLQQ@HEd5p5{4(7pDJU#N-me(;UVIw||*EpG3+t4^R6z@N~SA!Cixw=^D2JPv>2Q zSCm@~9?9$QG*1tn=GlO!c{br`4jqH{;O;k*=cNx%`-JdxyrID(gOA|pxG_AP_ZZ$w z|NXB2&F%+B`!)wp^R(eS%s&4Ja{@@!{AMWx8UixZFoBG z4&2qhi{`@%%DD?q^X$RXJo^R@;FitVjAbH<4Hn{z(?0!h=iw1WMUNyL9 z@Rq@SgZB*{8hm7MYw~XbPA}=}QM%r|!5w%{uU}<&r0@GG@O0nS;ep=2dGNG<3vTaB zp5Hb+?caslA5ZFg@U;H`Uiq7(K7>aXCi`b(aO<3Go@ssF;Eut|2Co^sVeq!WyYR<7 zFj-e%@S(wDgXhl8=8*O;z=s;wfv5FlgVzk+FnAjt|2TPmyYS|Rv`^rX>Id-N54BI= zY5fSEe!m~X{r^e&OMRN!fS9Uf~w9z3mY!Go1# zK5ckf--V~gbq}7_58&;;PWp%Nw0;CHjFb8pp4R7PviW;IPwH)WT3@86pTEK1`Jm+Z zDjU3J@P@(L2Jad?fWP6qWIiE$*Mk#};J3!hO|u;I;ov`up&<>icl-Oi~}f)BZ!c_D=+_T#)25 zh8HeO+`4}@e_!=Ec=&*%-iD|Bi}3dSl6nVTQ~xr&^33GV9V+m&z7F^G=O7;ZXWA!C z_`k?I@SL6O+a7$cd;mWnkKmt_kKuR8bLVGszDJ&i|Bt)~KO=YH`G+L)ufR9RYw)zc z2T%LA;OX=0!wcsp&uXH4PG~R)8HM0_u#M9xC4Vn@R^4t^Blt)a{Gd8KHrxY;V)d9^eMrAPhN(n{j2a- zs@{WtPu?_m8y=pY{DU`LxIO)cd?uG3JXU=l-u{c^JCgu@{=<{?4&j0N#PIlkl0IX2 zT5mrfn{#>|%foBx@4yS!Beb}uGr+I@^<-QZzAUdA+=J&;--MsHF6rNfUnuwC5104g z>*WD__AyES5dJ)Q1aHV=c-qH$U^eI5RByx6^%mgn_jLTi{cj}a4HxbdlKEHR?~;24 zZyDT&=YJ#V-#2(@@R7l-2W9g~`{&_r)wqtq%kWDsPUcyIzhCab`|_5-eS`N69vXaP zaO=U@9MbvZ4el7cZ15U9x+M8m{~GYx6BBR3-3^Ji;OY6N4ZnJQl7AQ8mInqO8a#%F znt#sD=63IS44#f#G`I_&{jFsF75G!+HTX{+o78*oblj%F+wd9XMJWz-KzUJA4*Y+oUI`H)P_2I89ChP6Ny-w0+0KYeX_`UhV@68{6 zfyN!f|4^QLST_H(z5suX>K%Af?!vputzz(+!9937pBDTcjoX2rdr6XW7oPSB3_dh? zZ1CLLY;Ng#^YGg=&m#PY=2?P&R$hjGPF{tl>+;~O$0s?o;L-P!{pQ2tA0^&}hd)fb z2TzZeKKu`!kc=C`LybFv+h>yVNeoZxbHA3&IX%8?xV<46w+MfO+%Nth(ln3y% zeh5$J6T|)QCf|3A;s2rj_C?t|Q!m1!%aeJ!2Cu@4PfqGRxUJXC7Tozjl3N>Yzd!K~ zJU#Aw_|G(MAMR+}5T3ri4B_ePO9W3}Uq*0C{jGd9&-6IW!9OvZyKR zv`^LGp21u2&Fb&Nhw?uB!&fHr9Kb&!AHvfpp?wVHF(wF zp21rN_YK}Rcxdnuyx=DDw;q|z!IRtYL-Hd0+wu}T&8KYeD*S)dr*7~De9beH`M2N? zly~5TQF2`O;E_H@?Za*L3E@waNAPRqG2GU;xnD=l`g0{4-d3Li{6lgF{%N@jze8Su zr~Pa2w7&;W`#0fts(%}vKJPyK%c}3e(|iVS*Gm4}EreU=>+b=osl@ z-q-798~z;C`|xz!9=xLZ0sLlpWbiS3_PNUQG1;6`FT&H~(S?VYS=*4NO*)zK7zO9)^B8UuwI|^$;0=*K{*(_Z15U9dPTCY8t~?= zi8tY)yaTu8K0H?cE>T;||~(TFH7NgO3evU!2V|?NcUo1Z@aMcS$+>Lsn!y|Juc}Ym;9Y|U@Wnq#<});SZ1CLrY!3IR zPXS(eN3!2a@a9tzcj2$ym&~VXaL?c^gZl>W8$2}l$l%svvpJ;m&l}t^c-i1JgEtJ` zHh9JYg+%i1<+_eJ#q{gkouY-cHurg{Sig z;Coab!qfT@yq8PnV?808XZq(#Ir!}D$$Sd%cgsr#ufWqjb%Qqz-Z6O3-~)q4@O1uT zgWDUj`K0wlgS!T=8r*}|-j?LtGPn;9Ro^ps08jf54IUdj_rz=tX`h0@O9rnPyl(KO z!8-=;8GK;y2>u=A8N<`{<}NklVDOT`D+aF{yb1sO{$!tY4BmsM^B)*If_s0K^dB4C zeo{7vw7zI?*Wguydj@YA+&6gN;Gw}s2DhBEuQzXS$KYjy*WeF+dy;!PGj9iG-V;elRXTJQ(`MY1j* zzFpoocxdpE!L6rcb4&Z@4el7c3@_|Xa;VYYot%g3a7*sN;~hz#7Cim?r)~K4?@My) z!Vky;gAWZJ8$4Ia=9cy^!0&f3nNP{!6?pCaNqybmO@ns~-ZS{X;E}<{2DhJ@%^{tC z(crGZs|NQB-h#jE14(Wjct_rar}GTpUr>DrPwPkUOFx*5JBFwA_R|dc7~F-wL;Wl8 zw13^;O@ns~-ZS{X;1S$=Q*xY+;o;iEt>4b(kk;qn!5fqM0z9oR!Q<~H^)9@o`BdSd z`q$uT{|4N7MAE+rPwP8y_nM^Mho|*@czXU1;A#C3-aeW1kLW*4d<<`XCvj_IHvhKj z^YCbt)ED4seF<)-`NPxtD%`&(>0g7V^$mES^)}&YeFtu7{ysdd@59sm8Nk!}A-wW~ zWd0F6tsm1hf9vUn{Nd?-D8SSHCAhUcnU4!^>iMn0gYPMSc-p@Kzjy!BKc@4o!FvWD zz{CGmp78YhuMs@{SyFHPPBzcr%H;Fp96aq`fTw@2ya;!+E*Jh9dDY;a!CMCR4c>>J z*0=+BI-khkV}skz$mW*zDH_~0c-7#Z!CMCR4c<3+Xz-E2E%)r}%^Tb?c-i1JgEtJ` zHh9=Hx1q~c+cPigGUA*8{EG7>^T$-?i##maL?c^gZl>W8$2}l$l%tdv#&RAaL3?f zgVzk+FnHVGU4sV(9~wM1cs|NSr&JWhCey-kv+mB3s zj!*}_?(dTM^xy^g0KQ%x!He=Se1qKn-E2ONya=C_yYP~{3g0OA;I6y{KlR;Yy*_-C z>ih7rJcQ55NAQZ=dUkfb^YT2rDtF+U1>Ta^;rrxGcw63q@0a)B9r*x$Kpw$;`51mi&zF5oHiv_% zFT%TW7k)@yh4_=>y^Z^)bQJ@O8`DeuAe$_Mb4Jc93&kKt{({k&`r`{hM=NAAK8 z$g6N)?!gbrTkx*jhaZyn;XQc>KP(@?`*N$2UGEWj9v;XY_)&QoK9JYo$K(xoC~w1$ z%e(NQJb<5&58;tKhM$z@o}bNOBrm{E$xHB9UV)#M*WqJ%6MjbCfm=uP{vSRgAHZ|+ z2);%>hTC%c1=$?d%8T&4+=Z``SK$S@2VXC5!HaSqzCqrHJMs`dD<8p2a_jo+dN<1R za98fYH_6NJvb+YLlQ-ZMc^f`2@4~C{0KQp1gxBOTd{Lg8&*o5<7vRhC65Nwl;4AVv zydiJG_sBc&ro0E=D<8mH@(8|9K8CmD_6xH)?3Wke9k~lXAg{uGxd%TeZ^65AAAU&Q zhxgY({ zbHAU>VI(iWPsvO0SYCmjme=89c@us{-ho@6(fJ=fBOkzX@(8|0K8D+JyPC~mt-J`& z%U$?7c@ z>+P#P4?iMz;DNjhKPs=m2l58|n7j=So{Ehwu~f7#_)UH)L}-DKEfB@)G=% zyaJEqb@*v{6F!!A;AiALxb@lObzuOXkw@^Hd<M z-h!`}`|zT?58ogU;f{O+pOsq++4Yv>dH6=T19#`7ACx!XU3nXRNZy6_Lg;j!F>pO#nQW4Q-EBX7a2&n2&GK72;rhv(!W ze2shrx8>GScD-xmd3av#z}Lyk@PfPsUoUUKS9E-};d|s=_+EJcFRK3#zCj+t9eM7? zY!0*X0=y(I!8gh)a93W3Z<06RWqAiaC-1>4@&SBa9>J^fF?_S!zA2kSO&&+m*7X_6?h=8!;i|F@PWJoKPK+qxUCVU|8z>mp$@K8R0AD2h)p?nNKA-BD34w1YFKPh+N zBY72mO76j9c?*78?!(9OKKzV4gj=82`5!(bw_cuIZ%&?vuaP_O%|A@~m*KYRYw)%5 z20Sls!`I2X@Pa&mua^(uMR^S0AkXc}=HSQ+@L72YUXoYf8|8JlD{sO#$vg0}ya%6? z58xGf1fQ3W;rsOcl)XEfLsj)f_-45a->*JZcun;ld{N$l*R`*F_=>y_KcI0#cwPNR z@Pn$iR{h#-i9~SzYE_Z58y}DX9#bqK8Ej==Wfa7 za7=v)@RsUJ@O|Jz<@PJGIh;_RBK(l* zU3gDkg*UZtJ@`I(3w~1L`tZZ*--n-4eF*QXegr=vw{Fd@H<0Jyr`6wqpOKg0Gv7;I z7i#b|@&^2<#%;p~@-BR>`ULP}svp8bc?@5tKDkCVhvTX*z=!e@{Diy$kK}duNqG}K zl6TUWTui*Wg8Y1HM7thCA{ud{!R7OY$LnqdbPY z^4zPkIc$;_;AMFUJ}0lhH)_At;hW@5ctw3W@OgO;elXqt@VRvV!>j5uhHsYJd$Kvy zpgM0E8d`0fV8}dGUk357oT zBX7VD$lGvV-i05O2k@?Z2tOo`;XQfo)!7^l%M0+nyaYcYufXTi{SOaR--K^geFuJ2 z^*#7NK7b#SNAOTSh98&PugT^xlo#PAuzE&Q?^YYvu zWph|3FTe}(5`4Y90^g(g*WpFgH{p9#-+^yXeGl%)2k=>W1TV?Q@Qrf&wb>k8c@e%z z?!wFRDtu1v!S`vsEqF!sK73x@hwoRP5Pl@x|M05%Sg*^jce6YXugM+wqPz^R%WLpu zc?0gr+wcQgR~Npb`T*XL58->{F}x|y?ak(}S6+a(~SJcJ+A{u#j! z$*tFC*Bh!&9)4Wzz=!fO{Diy)kK_&bNqHMSl6T>!!8F>M2 z9Zg=>O7IzZ1)h`F;cMhgxGnF%*UEeFynFy(Cy(F-`53-lZnv^I6y-(u2DuA&FUv>pIl1-5?0PHmJbYg6z^n2ye6zd;ugM$mMR^-umv`aI z@&NA1hwv473~$JDf0E5%kGueH%1iLQ@(R2qufzAroA9>01K%(2!8`H+{D3@y`|>gT zpxk~_Hixdf2tOou;XQd3epv3o`|=k2h}?$<@;>~iJcJMABlt17wJ*EgP@ab$mpkyG zybM1fufZdE1AbE8hL7Z3_$he+kL5%7X?YAE%X4qe=5R({fLmYG`5!(bufTKiI(&`1 z3Ag1P_*!`no|g~c>*Nu`|wTj z5MGv#;B#{8t=aWfHbpD6W$Sd%iybfO@Z^CVP2fkL`gXiS~_&Rw6 zFUZI6^>TZEHix3T2;U%g;f}ltpOt&?lDq}qDEHy6ybs?b58-9`2tFsb{w%xRiaZaW zmpkyPybRwgufc2b27FQ8hS%j?__92Jd-5TCMIOT&^4#0AIqZ=a;7xf6zE@s>x8!yB zK6w+~mUrO$X19&VS!cWU%_*kBM zXEuj3@&eqtL+5|^jJyKR$?Nbn@+RDtci?N~J$PO|fUlEB@Pd2{UoW@cmCd0jFTyv- zUAQB!!e`|kyd-bIH_CmuEAPWM$wPQqK7!B5t#@bFTaoAC^Ku7Xm6ze0?B2wsqn;p^qLpUt5tFTyv-UAQB!!e`|kyd-bIH_Cmu zEAPWM$wPQqK7!B5t-r{ww<6EO=j9H(Dlfw~%WLqOya8X7x8Zep7rrbH;GTR4Uy;Y~ zhCKI|*&O!B3-G4A1m7#Kz+3V&&(m*9Kl z6?jWthwqa&;ca;bzF*#hcjN>30eJ-Xen{@Zd-5v$u-t?9%N_XP?|V20toqz>mq>@PYpS?JoSdJfN%35Pm`)!%xa{f1S-? zsPz`$C*&n~B(K0v%Iolvya_)g@4#bu4}MxcfRE)7{EU1Ix4xV_kM`eWbC{7A;W@br zUn8%=ZMg?uD{sN`av#1<-iH_DA$+}j1TV_1k7w7rL7s;@atA&uFT+dn8hoR?0e9tX z_$GN5UX};&Ir$J?k;m|PdG2sFhpM~)-z+b|Yw`+wQC^4F+&XiS>Azr@*aFeK7cpm5qyt)3~$QqPiJ%3D=)%Z zau>c&UWK>i9(=#N1@Fjx_yKtz?#n~?LHP*Ym0L%$>pdjT!+UZEepp_H_vJPC5qSe1 z$lLIv@-BQJ58%h-LwG2U;m75<&t!8L$_wxl@)A6fSKue*b@)i$grAak;IX_1KP?}? z$MOh%Mm~mHU(xyhv)LSG zeJ;D+_3}KtD0ko+K4R}f3hHsR2;jTP@Z;}t;WqAyrljnkL4i$L;J})o9 ztMUqbv%C(k$(!&+c?VvX_u$L&0o;>E@D=$O-jLg$&*rd4UW7O0E_|=N3UA3h_&#|H z-j@6D{qjD%BM;#RL-I1bC$GT|%Ny{%ybV7h@4^Fl06!`p z!UysgeoUVG`)m%OyZ}EgFTscM3jBn;4v*wb_(^#OK9cv~XLQ~hz)z_@g2(bP{IuLY zn$2M>FT&5rUAXntx~EOYr0J3VbN9!%xVY@JQZ)pOp9DBl!S+N*=*u`51m$Zr_>BVJt7g&&XZ4b(hZn z@EN%W&&gZxHF6(r%lq)P@(`YvkKpU%)|a#EEy(lm^>PPZl$YTfZ@_2eZFouE zg>RGxa92KrZ<5FGvOISzo5P&E0I$eP@OgO!UX|D3o8?V-P2Pbo%6ss-d;njTM{rL* zhOfx&uViy*$cykjau?o|SK)i*9=s)Q!S~61cw63w@0W-0j(h|^Ah*7nU9T_C!woXOL-VkT5qzE8`dW6q1$iF6Uhcq)@-lpbyaspV4fw3Q4KK;N@Qv~S?#hSoP4XCCmgl~n z&0$VnfLG)t_`JLVugdH2&GII^Chx!(-FV%_(8b?@5;;YL-HEDCvU(H%iHk2 zybC`f58#1(2tO*1;RAW@o7o(W$qVpMUVaC|5G-HhP(*hBX{9Vc@@4_?!jB~7JQ%FhqvW@ z_tD0$T_?}O3vvg(US5V5J zln>x_c?4gUkKr@lPxhhx@7WyI$cyl`au@EYe-*wW_uvhA3%*D0!<+Ixe6Ku&x8x)E zKDiZT*V~ro;rrzdydy8e56Ek9U*3Qpl(*qsc^7_29>9C@A^fmBhWF*UZ)I~hA}_!L zc?o`0UV#tfb@(xP6CTPt@Z<6xd?+8lPsk&9Bp<_1%I*Ki<}i{M;iu#-JeF7Cr{x}e zEN{Wj$bGo=jpR7(!)N3nJSQK)*T}7JXV+`X^YFED2cDOg;p^lzctPHPua~#sMR^y# zK_0*z`4B!UkKrYG?qoKHjq(EAm6za~=+~D{>FskhkD_{E$3=_vAzPVR;Pi%X8n&=5Rz_fCust{HVMFAIR(QWAY|E zly~6A9z{EXa(TgP?&htJ4E zcuqcouaR5d%dXd!=izJR4m>X}!`I1c@PfPnUoUUNi}EgfgFJvc@*#Xy9>YuW-1oCN zY?K$^uDk@_B(K2B@;ZD@-h@}=9r(Pw2d~Np@XhiFUXzdEi*ox1*&OQfB79ly!aaEv zz9RSF4S5T`NAAO$@;-d8JcPI8BlteKbt=2wwmc8tFL&S_c^Q5{UW5Dc2K=DB4e!dk z@I&$d-jfgEhvhN6FVFojo5K-#0UpRp@T2kyd?2sGkI9?xP~L$bm-pa9`2c=G9>F8| z7=BW2|0tWoNM3}WlDqI&UWK2Qd+@Qm1wSMA;np{G{)f-VLwHU;g0GQVKhCb#mgnJX zZ#Yw&`+0beg~!;A7Re1klIJMtlXRvyDk@?4zFVWYeNcjYDcCV2&3me=8P z@+Q0@@4)BfJ$O|;VW_v-jKK8d*nX6DeuGg z%0qZdK7#L)TR+LJw=K`Z_sbo4M_z^>kk{b8ya7KbZ^OItF8q)@fcNA>_+fbr@5^)l zmCfOZyZ{g6CHPT!1wN41;m71ncqs3{kIQ@Tp?m;8A&=mZd<;J+w@+tt7|Dz9Q*sv` z%d7Cyat}V1x8P^wKHR!n=YRN&JcQ@uBlsG*_0#NnZFwHPR_?&_@-lp#yaq4G8}Rk= zHoPeB!Z*kRxFa9JXXP=xB+vbKHiwP!0^F6C;G5(Xcv)VD&&iwcio64#m-paR`2fCI z9>Ht!F?>;O|16tBU0#GQ%U!r9ufkX49=suM!S~30cvIeo@0Ew}mV5-?C%4Ag^|s}C z_3;eC1T=h+;N$P4g5UVZi$;a@Ma{CwA97ggY{FK~<$MP!twA_P_A#wa712!2l5j9sJsFn$m{T9@+Lf#ci_k6J@`;Q zfS-^@@JK#}pOo9yQ?2PGxB9zfjpRl6DY*-ea5h|@)S45F3>bBJ0R41#YfYTON08#--MmWB>O zw@%b>r+2p%Ix5wzrtP>JtO_0Ew5y_~1bb^RNZIyISw!z|E10%Xzw4LJ>z(VG=hgMc zxh{|Eb-nj9@16O~e)p64YzW^i-+&L~34D)y43FeFe6M^4-;h`EeexwdmN)SI^6qWg zeHh9E_yPFv0TiVB)$$j`yc^^KJNAP3v zAv~9-@Z<6cd@3*CC**T@A+O;l<(u%C+`DbN52xfkcqtFzr{x>)xjccNk&oe(Jcpl^ z&)^Gr1wSWW!fSa0KQHgzuHA>FJb+)258x|#48JHJ!8hd@{E~bMZ{#KXvU~yWe8Jr> zR`4zI&JVSZ*OUA3t@1v+E05sY!@IYR}cgi>6eYxkg`>;#i zgNO1EzFWQlAIKB<9{Cs^$#eK#`3$}xui*RSOL#1A;QQs>+qe5Lln3ww@&P=N$MA#l z5qu=i;D_W>cq%X9hvf_SSiXWEk$3LUKHg03!;i}Q@QFNvACnK^xjcm*mrvkRc>zBm zpTi4z4L>R0gwN#O9ov04CGWvYc?dr(-+<5M3H*$F46o!l{H%NiU&t%?Ir$P^%NzK4 zd3S5O4@-FfzaSsLSMnHsQ9goi$}{*S`4ryBOZa8^0^a$e=70DWdFM{;~`s?_SgH!%!Z;56B1bL>|Kr%17{#JcA#SPvNP&gddhK z;A8m;enj5swvRWH`|zXkK71mN;K$@ccrH)j$K@0FR9?VO$mj4vUc*nyH{mn6cb9e_ zPRV=lQXaxj%QxV2c>+HpAHyqo4nHfO!58uheonrG*YXB_Uf#WHyAMlw0KXs~z*q7Z zeo;PxZ^|?HCHWNI$V>QT`2ybglIDN-7J28|_VIdhAHG%Ihj-->e4Bg-_vI;kyL5Q>=fAHySg4&N)E!8ha;e4l&?kL3+~zr1^ob{~fF0DeF| zfG6@8eo#JwkK`HrkbDYH_N$@9t>#VJQ#b z7vux@N*=>6%17``c?Q2EpTZk?3BN2~z&l^o{14wE@BC={cs;of-zx9JyYdLWO+JMC z@)W*ZK7se-1$>8m4iDrte5ZU9-j{nfw)?P4-h+qo5WZW!0UyW{_#XKf9?5g~Uil2Z zA+O;3rg1inW;hDY)ozE?hjZ^$e7KKT+J%NzK9dG~?s zJ`Cjn{D6D_PvkNDpnL=$$usyN`4pbYOZZ{=0zQ_n;78=0A8Q|PCime-<$d@>9>I^v zhwxmU!jH=*@Tt6jpODYtg}jEJlyAaka_>RyKAe*G;H5l-pO$aH=kf%8Mm~mD@*I9v zK7%jh75toh39sc1{Jgx|Z}(v-58xN%1NcfF!!OE5@J)FJza*c+8+i%8EMLGoOU?iA zE%MHT+sEt4efU;+AKsNm@NM!T+?S{D?eYn{CokYTXunNAOK~2EQbq!W(%Bzbs$CJO8NpAHGH2d3gJHJ-H9xD(}O) z@(8|7K7{-76uwRQ+WwLEMLIK z@)i7uyz_|m@n&)#epKFvPvjB&n0yG&elKK!V> z51+^*_%Znqp377Carp#3l^5_6@;SVa*YK0_P54aiJ*M4*~ui)q8 zOL#4B;OFJtsNIL9Jb+)258x|#48JHJ!8hd@{E~bMZ{#KXvU~yWT+sXv-y-ikv31pXgo3e2GvR650t0y zhsZPdZg~#3d;x#F@+I8HQ^B9Ed<}nwdj{gjX4cDxCEr}8P>jyHopNckN8czFSTuDpaFkXP_ul-KY>@)dk6 zZ{WWr_ny}7|6Aog+{Pck-==&BpUNZn&6<~DxQ#!7zfW~i_{ZfL{2%2x{JZi3ey2OQ z^R|TFTVBC$lGpH`mapJ1lsE93<=#fS|GzKy;qR6Q@V}LZaC?6vc%^&{KQB+<{~}M} zx4on5TL!I+t=>@&&z$d;lHVT3BTHZc&2;}-#Df38Q^bFzJXibdq%tex$-{z599%S zCJ*87lSlAe=WPr>qkIDYj68*ZUY^0v%X7Gmr+|M$`4Vp9so>vJzJ^UxRcmQUcFuDf1RxQ!=+-$wZyetUTVzeZld?<%k0_mtOg%dggVtSs#CyI zc?q}iSMXm~zJ^=<75sOUZ{UU8``LE?KP>m*pOXjhLdP4@m5<;jt^aVVpTNJRIw^cp zp27c9p2NMnxa+8ZUnei&*UKyTjq)1)0Qn03aCrl_ytlvI|Hmrt!)-hP{He-^@Mp;* z`19p4-1?Ki6XjF5)z9F+qeZN$}e@FQmZr?Ah;JNY*+`eD(p55;M zA1m*}?fazw{x0Q1xP8AA!9SpU47cx>68J}zPvQ3cQU+frpTq6@r2_sXN6IVs)EO|K}<1!w<*<_$%ch-0~58 zqI?X0lRSaHO`gInpTURv9w3KXe+u|a_oEViN?yVLOyjBHrSdEIJCtwWC*|I--T%|7 ziYKVA70ZvD*QFIGN>TR#i<5#>v`^|OM%QTZBf{anHSQ27RK{qzpB z`~ObmeYo{AfWKe)5Pn7;!T(+!!!4h{zo2{y|B5_=e@mXjEnmR@L-`VZ$Gf@nxq{yy zui=(o!SADd1HV!3J-^-m$I5-Uw?4@(^zM2!2xe z7;fW9;O|mCh2Nt2IfKuX&*3&d6z~r!U&5_^1^jzq~yo2rj*K!|zYv0Xd0sOr6AO1tiNARWcF?_4?3H*ZcDf}+VXYiHsIsAu} zFW~o(m+*_KQ^9XgzJ_lqzk=UO`38PTdGA-+{okRy4{wwY;P+KNgkM%Zg5O{H7~c73 z_wNT1_yd(s;ail?;C$`u#Kfsj9Pr z$MObldGAH-{`d8|KK$zZ1OGYI58*byMetp!AH!|FO5maLDct6(48B|W9B%Vf0Usz| z!fn2);Lq3b)^MAzR`A6754ZWs8@2oY%gXz3o38@+3$6cfo3A4Hi*K`|x+j1Ni&oA^b1p5&Y`&NASN=K7nuQ@1qocM)?f>S$Ph(`L=+6UilJk^2;gXJmw(ee!b zRCx~Xyw82UD&WsnzJ&jhyn?6l8gBU&{8h>~@Hfc4L+$=|HBKL%D<8n$ArIkpK1c9( zD<8u@EKlI)R0f8RKA9POTL2thrEIR(B0kn?7g(z z|GUY3cu((30KZQ85PrQpf(P;#et&raf0#Ul+juhgeEBK$wYxqg|3jP=J25$3}_iOF`pHkk3+w~Q|->-ZKFXa*Zj68;0KNI+8 zluzMyycxV!K8J7E`49h^@+I6ppH%RR%GYqaepm4CDc``YKVI7I|4`%f;kUkr?!WMB za_PvMa~ga52Nhu=Z-Z~=d|@+G`u^AG&F%GYrFJhp;APx%IJ z_fzlX?f(C~@;=<|j{*Go%7<`!zascAC?CV^^IHOcvGOV0&gTsNtIFqadw&afs(cBz zdo{$CnT2=8on_lpRAYk3UcB2VDC?CTgB~Rc_mZxybXK?#_Er&l- zbqaVaFX8_~UcvXvYxr~IEBH{}z<*xuy}I3h`#k2u?ekawe}U?R@I)TLUnGy=mQUdJ z@2OI_{rjj4ZvUPthg-gY+x@77zf9w-;C8>L;g(;)e?xT|_-o|euebYuv)qRtkq7YC z%0sxlUlIIu%E$0G$P@T)$y506$}{+0`~Dwp`2xOS-~YpZPxUMKKILn8EMLLjp*jux z7P&WW_kX|Y`0&ptAHZvQ2)BF$KV<9g@Wb*1KGb+p_(-46Gx!1JbGX$n;9u7GOZY#_ zD|n*%HQe$m_(A0x_z@ki_Z#j0AC>!X%Lnk0>WA=)8h-@;S9uJ#d;&kD`YHUos*}Nw zY5Y0d@&)_{s#C(Z+|zx2sNkvfA8z>-{IK;OetXsTUeoS>SMI|tAHc6uK7{w=5qzxi z#Bj?e@FUiL_;K|)gP*Ye!!2LHGu1EQ_tW?*_=Ds%-0~~TRwsxQ9g#-{VsvON%<6R{mI}bH2xf3$P4&Mc?mzQ^Q3~G zk=Jm`ui$Uh_#612$i3IL`#-b(!z~}c->o_!{C)BWeoFOY_=l8F;2)N!aLZ@#QuTBA z$5f|)e@b4$KQFJ~OL+~q`YZTZyp%RZ z)_=I=SMZD0fB3Ik|6kwk|EBUj{HXE){F3q^{4L5y@IREtaLXs~M)gzppQug-KPk`Q zmM`G%RlbCm@(O-gvd!J z4tWCKqVc3~%V+Svv;M=+$_u!s`X&6+)_?eC`-Ex&?qxBkO- z+WCL9-Tz&3A8z>o-n0J0?faz&zC-yKeuIuHfd|T`@cS#D!FMX3!w1S2@V@dTe4p|a z{CV;kZr?|(=*lw=&c?h4&Bls@sKm2pbC-Bhv58tixB!lmf z=Wxpx@TFe2gnw0D!FOvsHGHpLcLg6P-@x}N@BOcK|1auwefYQK0ervegm9}L!T&>b zVz`~334CkM%`++7jyHqv(edW+YgMO!-&0<~EnmU!t9%U)HG@-^J9mlb@! z@(uieo&S?||9@4-<-=bl58y-958;-N;4fF582)N`0zaVoDctfIJW)P}->mut{Ppq@ zZuts+Q1xs0Z>!D58;-N;3usA@Uhl&6L_I~3V)tnH-o=ap2Mwv0Y9RA z3Ag$c{G|0CK9jHDr{oRXt}pLR?f#d_`|#8906v$8@ZZyMMewOShFd;?zeD*Heo~&n z&uBb3-0}syQoe-S`B}lwDqq9jt?{hj@0T}l%X@Eb_x~2IQgYp@CCC}lOFW|NHAO3CCso>|W|8N`s3cggnfnSh&zt`^nmV3K-*oWUq z9>9Hh2)~Csg0J+tG2HSA{G#$H{07y};P;W|aLX6)K=~4G-)~g#O^v69?^c}^{4w$d zZh0?n_y6(A`|zXsJQKh#X*?m^@)7(gsvpDc`<4X$Eag+UeP5Bme@^)vZs&6Wf3fl< z{1x&Feneiw|Cf9Pe~Y|<|FPVAOS}K?lKb#mLdO-2S~s0Piaw!tML*2!2rGiQzAiC-7aWpTaGl!9(S9 zxUC-*@Kmo`!e1q?;Ja17hFg9Ge~s!i@c$|I{-E9ef$IBk%Lnj1%7<{fUqtXo`51mo z<4NFelc#XYXYjqMpTi5)Dd6vwm+%kDEBJ@yHN29q;2RoG1Gl{Qws!ybDeuE=JOMmb zK7@Z-gRAf-U5Eq`VY6i_bd2sYdkgln0y7FsD1;ty!ZBY|Bor}!{4I% z0sIf;A>8s2JXie~ZtqtDKdyWV{}a7#2LE$;4!3*(pQ?TdKV|1X{Dkr~{P}v_6}(Ws zfxkz2?~mL4|4X?Kw|oFUsrn&&CXe8^sD2DTBTwL`R40X7K7*fD-*UM1vw)YXU&243 z@l^27$ZNReSMbxS-@wmkoZg9c|If;Oxa9-*7c`y_zLZDsugPQhH{=O?ZvBT_K7)Tt zb#nN(EWXzkyrc`_p#+m&*Ha z`+GKkUr;`TUw!@pzEVDh+x3#bA87rDKS-Xzcgb_OgFiv_bNCbG1^nvs z@9?Y7zr$_+t{T3f`YX8o-dO{;yjQgQ{|wdf;m?!@@aM=wxaA}G^OcX`)}I7!&*P_X zd%in^+kP)O{GeX9fWK5;!YyCHU#)x%AIn$po8=AM^4>ez{r^qneRw7h;BSzJ@T2kw zZolsk!!4h{e@Asv_#5RJ+C={ObJkzuW!) zQ`Pa|?~w=aTjU|!@)7*%{wHwT&m@6g-Twq``3!zg&&A|$+s~wcU)}!%Zuts+b^jB% z?Ps!rU)}!%Zh7y|+x@>f|HJ><`VYT4|HH4&|L{+!P7JsD3Eb`%Dg5gE54Zb84!3*( zxBXa3xb3e}!EHa58gBU&{PQ~A2L6BL-pO|VH|0Lu@&Wwcln>$i^mjxA-!G5hmQUbz zKTY9wy=QQ{pXPA8pBC^-8h;7@uDpW(KwiVW8{OXpEBIaH4g5N}_s(|z?ZkAr$uqd^-72MhUmi4;9=#Z`5$hui*Cip@G}| z#d}w~|F++b54Zhu0=VtB6T&SY!5^l+#qh_-6ZliC|8UD^@TV!C!(({?zk2_LTYoCJ z?VnS_ZU3AV{I&X>ng+i5W%d2t?f%c+tLN|Fwy$>pe~apeaN7qhg4^%1#qi|U-8d4s z{mxnnw|oY--%ZQmiT?g6;PyK^CEPyuRB-!UgBpH7@9zq3zYEa74?M`tSKh4MfBRhk zA3oLJ*8$vq7a)Z1>AU(7+|Ox z`Gl@~3O}d&cn0q&pTlQ5e+u{xLGB*5y}l+jqBtUwXF7d+%-c-}agG;d7gR;I@xz2;cD>S3iPxR40a? zRX%~+KGi9_b30c*gTF!h4CnCu&vp3%ZuKd$;2e2>jP@H3hE54U}HOZd6p zarp{v`>5COb8mF{72NhwZ{Vlj)#gZ8Iw;Aj5c z<-PZ}`)~X1`tXZ7KLhxe^?M~D{LmL%{RnRRTF3CC$|rE!XE=rL`--cd!EGPl9DYps z0&e>}m+-U7S8&_6xrXogXZ0W6*ZJ1K4_;LN|FYeG+qc<=pH@DA4^%&dU$~Xu`TzeH z!EGPT7=G?rVljgaNGB3qxuiGeYZmRIprg`JqHoPM>;^3kOn^1@p^yN?!WyzA0K{J^#iy) zXB5IGsvp7aIh`0jwf@6*>v&W6$-wn9gWEnZIee!21>E+TDdA^Szk=I3T@BxPKR2Eg ze6OAV@VWK>uiO2%eTRJbA=MAywy#bIUs(U)wvSK@KdAdl0=IpEQutE!Gq^oxk;6Bw z|8UzUr-Yx<@m6r#_ojw#y}$Yow|#6H_|^xwyf<(6-}a^P;X9NM;0N{ohVZ@0NAN?+ z$M6HnC-B4e{3*Qim+tdw2Dg1+a`;}&R|VYmbt&Op)vw@3H2xaC&+fl)+jpgb?^oXY zaJ&DuFN6<2ZvBVbJ}M!+r}0N{+ZQE<_mxlJw$DilKd13zaNFl3hhJ2_fZM($CH#Q; zQ^9Rth#J0Qr_O)4?L*SQ2RdHwZ`%DI>we+GcdLE?w|zoF_(6?7g4=VQF?^ruCve;M zBZY5h{2AQ#@yOu^Rlk7Sz8od|gz^>K_RXl_rw|x(C_;HP=fZIL>CH$n0w}RWg1U3AW^&fsr=W_!;ZTR!h6bBaC;8A zhM%$i!|gfi27cE1|FL%e?Kx;4-q&~nxIG6Q!p~d(;r5(z48Np&0=MUmQ}{WJKZDzI z!8v?j{fFCgz9sy;>Q``kZnuVSsQwCW&*3)k3)cUt-G6(o)`y?9@896|++PSEYCIAA zxPAVI@BcA(zewQr+-M3PseT4Op!zxdkm?uklggLyvFcaw*Xp^j8a}c9!|gfK27X-Q z@&2yee|v7!hhMb*!|l1w5We$4y8puMIm#G*QRhPfx92ER_|*Cjx91{r_)(3gfS
lh+Q4o7)cZuc|F(YW!!PN5 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

6}aD4+aBhJd zN7$Rl3TH#POg$HXJqP ~$KgH-uY|DyHhuP;Je>+MVms=GXKj2Z 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

wsE3K814{FmKi@Uc>uox!^)HEZE1HAQ`RAG!7 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

{z(IH@NUG^eo>3lx~>i-ni6w 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%0AXYDA8U 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