diff --git a/.vscode/settings.json b/.vscode/settings.json index 9da323c..b83c5ff 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -41,7 +41,7 @@ "custom" ], "doxdocgen.file.fileTemplate": "@file {name}", - "doxdocgen.file.versionTag": "@version 0.3.0", + "doxdocgen.file.versionTag": "@version 0.3.1", "doxdocgen.generic.authorEmail": "mannymsp@gmail.com", "doxdocgen.generic.authorName": "Manny Peterson", "doxdocgen.generic.authorTag": "@author {author} ({email})", diff --git a/README.md b/README.md index 586f093..36b0851 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ ![HeliOS](/extras/HeliOS_OG_Logo.png) [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://github.com/MannyPeterson/HeliOS/blob/master/LICENSE.md) ![GitHub last commit](https://img.shields.io/github/last-commit/MannyPeterson/HeliOS) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/MannyPeterson/HeliOS) [![PlatformIO Registry](https://badges.registry.platformio.org/packages/mannypeterson/library/HeliOS.svg)](https://registry.platformio.org/libraries/mannypeterson/HeliOS) [![arduino-library-badge](https://www.ardu-badge.com/badge/HeliOS.svg?)](https://www.ardu-badge.com/HeliOS) ![GitHub stars](https://img.shields.io/github/stars/MannyPeterson/HeliOS?style=social) ![GitHub watchers](https://img.shields.io/github/watchers/MannyPeterson/HeliOS?style=social) +*** # Overview HeliOS is an embedded operating system that is free for everyone to use. While called an operating system, HeliOS is a multitasking kernel for use in embedded applications. Its rich, fully documented, API allows the user to control every aspect of the system and access kernel services for task (process) management, scheduler management, inter-process communication, memory management and more while maintaining a tiny footprint for a broad range of low-power embedded devices. HeliOS is also easily customized to fit the user’s specific needs through a single header file ([config.h](/src/config.h)). @@ -17,13 +18,31 @@ The HeliOS kernel includes built-in memory management that improves the safety m HeliOS is built to be robust. Each HeliOS release (0.3.0 and later) undergoes static analysis testing using a commercially licensed static analysis tool as well as MISRA C:2012 checks. While HeliOS is NOT certified for nor should be used (in full or in part) in any safety-critical application where a risk to life exists, user’s can be confident they are building their embedded application on a robust embedded operating system. Lastly, for PlatformIO and Arduino users, HeliOS is easily added to their embedded application. The current release of HeliOS is available directly through the [PlatformIO Registry](https://registry.platformio.org/libraries/mannypeterson/HeliOS) and the [Arduino Library Manager](https://www.arduino.cc/reference/en/libraries/helios/). For users of other embedded platforms and/or tool-chains, simply download the current [release](https://github.com/MannyPeterson/HeliOS/releases) of HeliOS from GitHub and add the sources to your project. +*** # What's Happening -The HeliOS 0.3.x series kernel was recently released and replaces the 0.2.x series kernel. With the 0.3.x series kernel, there have been significant changes to both the kernel internals and the API rendering it incompatible with applications built on 0.2.x. While the changes are significant, updating an application built with 0.2.x requires a minimal amount of time as all of the features of HeliOS 0.2.x have been retained in 0.3.x. The key difference is the breadth of features offered by the 0.3.x series kernel has been expanded and existing features rewritten. Along with 0.3.x is a complete [HeliOS Developer's Guide](/doc/HeliOS_Developers_Guide.pdf) to assist the user in building applications on 0.3.x. The focus for HeliOS development going forward will be expanding available example code, further enhancing documentation and addressing any quality issues. As always, contributions are welcome and anyone wishing to contribute to HeliOS should refer to the “Contributing” section. +The HeliOS 0.3.x series kernel was recently released and replaces the 0.2.x series kernel. With the 0.3.x series kernel, there have been changes to both the kernel internals and the API rendering it incompatible with applications built on 0.2.x. While the changes are not insignificant, updating an application built with 0.2.x requires a minimal amount of time as all of the features of HeliOS 0.2.x have been retained in 0.3.x. The key difference is the breadth of features offered by the 0.3.x series kernel has been expanded and existing features rewritten. Along with 0.3.x is a complete [HeliOS Developer's Guide](/doc/HeliOS_Developers_Guide.pdf) to assist the user in building applications on 0.3.x. Going forward, development will be focused on refactoring, expanding the API, expanding configurable settings, improving documentation and addressing issues. **As always, contributions are welcome and anyone wishing to contribute to HeliOS should refer to the “Contributing” section.** +*** +# HeliOS Around The Web + +* **[HeliOS is a Tiny Embedded OS Designed for Arduino Boards](https://www.cnx-software.com/2020/08/14/helios-is-a-tiny-embedded-os-designed-for-arduino-boards/)** + +* **[HeliOS for Arduino](https://linuxhint.com/linux_on_arduino/)** + +* **[Newly-Launched "Embedded OS" HeliOS Brings Simple Multitasking to Arduino Microcontrollers](https://www.hackster.io/news/newly-launched-embedded-os-helios-brings-simple-multitasking-to-arduino-microcontrollers-11f6b137b75c)** + +* **[New HeliOS, an embedded OS for Arduino Boards](https://iot-industrial-devices.com/new-helios-an-embedded-os-for-arduino-boards/)** + +* **[HeliOS is a small and simple embedded operating system for Arduino](https://twitter.com/arduino/status/1293910675312357376)** + +* **[Arduino Operating System: Best Options of 2021](https://all3dp.com/2/best-arduino-operating-system/)** + +* **[HeliOS is a Tiny Embedded OS Designed for Arduino Boards](https://news.knowledia.com/US/en/articles/helios-is-a-tiny-embedded-os-designed-for-arduino-boards-f35f44fe6c88759fa13d8781ce09ac985b2fdd3a)** +*** # Getting Started ## Documentation -The HeliOS API is documented in the [HeliOS Developer's Guide](/doc/HeliOS_Developers_Guide.pdf), which is available in PDF format in the HeliOS sources tree under “doc”. +The HeliOS API is documented in the [HeliOS Developer's Guide](/doc/HeliOS_Developers_Guide.pdf), which is available in PDF format in the HeliOS sources tree under “doc”. If you are in need of support, please refer to the "Contributing" section. ## Microcontroller Support -Other than four define statements, HeliOS requires zero additional portability code. Currently HeliOS has built-in support for AVR, SAMD, SAM, ESP8266, ESP32 and Teensy 3/4/MM microcontrollers (though the latter is an ARM Cortex-M based development board). If using the Arduino platform/tool-chain, HeliOS should work right out of the box by adding HeliOS to the project from the [PlatformIO Registry](https://registry.platformio.org/libraries/mannypeterson/HeliOS) or [Arduino Library Manager](https://www.arduino.cc/reference/en/libraries/helios/). +Other than four define statements, HeliOS requires zero additional portability code. Currently HeliOS has built-in support for AVR, SAMD, SAM, ESP8266, ESP32 and Teensy 3.x/4.x/MicroMod microcontrollers (though the latter is an ARM Cortex-M based development board). If using the Arduino platform/tool-chain, HeliOS should work right out of the box by adding HeliOS to the project from the [PlatformIO Registry](https://registry.platformio.org/libraries/mannypeterson/HeliOS) or [Arduino Library Manager](https://www.arduino.cc/reference/en/libraries/helios/). ## Adding Support For most users this section is unnecessary to build HeliOS as HeliOS will fallback to a generic Arduino configuration if built-in support for the microcontroller does not exist. However, if HeliOS does not build for the user's specific microcontroller, adding support for other platforms and/or tool-chains only requires the user to define the following four defines (and any required headers for the defines) in [defines.h](/src/defines.h). ```C @@ -37,6 +56,7 @@ development board using the Arduino platform. */ #define TIME_T_TYPE uint32_t ``` Please note, when defining TIME_T_TYPE, use only an unsigned integer type. While 16-bit wide unsigned integers will work, 32-bits (uint32_t) wide or wider is preferred. +*** # Example Many embedded applications implement what is called a "super loop". A super loop is a loop that never exits (i.e., while(1){}) and contains most of the code executed by the microcontroller. The problem with super loops is they can grow out of control and become difficult to manage. This becomes especially challenging given the relatively few options for controlling timing (e.g., delay()). Unfortunately the use of delay() to control timing also means the microcontroller is unable to perform other operations (at least without the help of an ISR) until delay() returns. Below is an example of how easy it is to leverage the event-driven multitasking capabilities within HeliOS to implement the Arduino "Blink" example. ## Arduino "Blink" Example @@ -132,9 +152,11 @@ void loop() { /* The loop function is not used and should remain empty. */ } ``` +*** # Releases All releases, including the current release, can be found [here](https://github.com/MannyPeterson/HeliOS/releases). -* **0.3.0 - First release of the new 0.3.x series kernel (many new features, most of the kernel rewritten, new example code and new documentation)** +* **0.3.1 - A lot of refactoring, code clean-up from the 0.3.0 release and code documentation/readability improvements.** +* 0.3.0 - First release of the new 0.3.x series kernel (many new features, most of the kernel rewritten, new example code and new documentation) * 0.2.7 - Added a contributed example, privatized the list pointers for scheduler and added support for Teensy 3/4 * 0.2.6 - Added built-in support for ESP8266 and minor internal updates * 0.2.5 - Numerous internal enhancements including improved time precision and scheduler now gracefully handles overflow of run-time timer @@ -142,11 +164,15 @@ All releases, including the current release, can be found [here](https://github. * 0.2.3 - Improved protection of system state, new examples, improved code documentation and some maintainability enhancements * 0.2.2 - Additional function calls, minor fixes and documentation enhancements * 0.2.1 - The first official release +*** # Contributing -To contribute, create a pull request with your changes. Please fork from the **develop** branch only, as **master** is kept even with the current release. +To contribute, create a pull request with your changes. Please fork from the **develop** branch only, as **master** is kept even with the current release. If you would like to make a recommendation or are in need of support, please open an issue. If you are seeking support, please include your source code, details about your particular platform and/or tool-chain and a detailed description of the issue you are facing. All pull requests and issues are responded to as quickly as possible. +*** # Copyright & License HeliOS Embedded Operating System Copyright (C) 2020-2022 Manny Peterson HeliOS is copyrighted open source software licensed under the Free Software Foundation's GNU General Public License (GPL) Version 3. The full license text can be found [here](/LICENSE.md). -# Important -HeliOS is **not** certified for use in safety-critical applications. The HeliOS source code, whether in full or in part, must **never** be used in applications where a risk to life exists. In other words, do not use HeliOS in your project if there is even a remote chance someone might get hurt. \ No newline at end of file +*** +# Important Notice +HeliOS is **not** certified for use in safety-critical applications. The HeliOS source code, whether in full or in part, must **never** be used in applications where a risk to life exists. In other words, do not use HeliOS in your project if there is even a remote chance someone might get hurt. +*** \ No newline at end of file diff --git a/doc/HeliOS_Developers_Guide.pdf b/doc/HeliOS_Developers_Guide.pdf index b52983f..aa191be 100644 Binary files a/doc/HeliOS_Developers_Guide.pdf and b/doc/HeliOS_Developers_Guide.pdf differ diff --git a/extras/arduino.cpp b/extras/arduino.cpp new file mode 100644 index 0000000..16deb9c --- /dev/null +++ b/extras/arduino.cpp @@ -0,0 +1,50 @@ +/** + * @file arduino.cpp + * @author Manny Peterson (mannymsp@gmail.com) + * @brief Source code to allow the HeliOS kernel to interface with the C++ Arduino API. + * @version 0.3.1 + * @date 2022-02-25 + * + * @copyright + * HeliOS Embedded Operating System + * Copyright (C) 2020-2022 Manny Peterson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + +#include "config.h" + +#if defined(CONFIG_ENABLE_ARDUINO_CPP_INTERFACE) + +#ifdef __cplusplus + +#include + + +extern "C" void ArduinoAssert(const char *file_, int line_); + + +void ArduinoAssert(const char *file_, int line_) +{ + + Serial.println("assert: " + String(file_) + ":" + String(line_, DEC)); + + return; +} + +#endif + +#endif \ No newline at end of file diff --git a/library.properties b/library.properties index 3d057c7..262997a 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=HeliOS -version=0.3.0 +version=0.3.1 author=Manny Peterson maintainer=Manny Peterson sentence=The free embedded operating system. diff --git a/src/HeliOS.h b/src/HeliOS.h index e97b00f..6b4f251 100644 --- a/src/HeliOS.h +++ b/src/HeliOS.h @@ -1,8 +1,8 @@ /** * @file HeliOS.h * @author Manny Peterson (mannymsp@gmail.com) - * @brief Header file to be included in end-user application code. - * @version 0.3.0 + * @brief Header file for end-user application code + * @version 0.3.1 * @date 2022-01-31 * * @copyright @@ -52,6 +52,24 @@ typedef enum { TaskStateWaiting /**< State a task is in after xTaskWait() is called. */ } TaskState_t; +/** + * @brief Enumerated type for scheduler states. + * + * The scheduler can be in one of four possible states defined in the SchedulerState_t + * enumerated type. The state of the scheduler is changed by calling xTaskSuspendAll() + * and xTaskResumeAll(). The state can be obtained by calling xTaskGetSchedulerState(). + * + * @sa xSchedulerState + * @sa xTaskSuspendAll() + * @sa xTaskResumeAll() + * + */ +typedef enum { + SchedulerStateError, /**< Not used. */ + SchedulerStateSuspended, /**< State the scheduler is in after xTaskSuspendAll() is called. */ + SchedulerStateRunning /**< State the scheduler is in after xTaskResumeAll() is called. */ +} SchedulerState_t; + /** * @brief Type definition for the base data type. * @@ -67,11 +85,11 @@ typedef uint8_t Base_t; /** * @brief Type definition for system time measured in microseconds. - * + * * The Time_t type is used to store system time which is measured in microseconds * from system initialization. Despite its name, this type does not store real time * clock (RTC) time or date information. - * + * */ typedef TIME_T_TYPE Time_t; @@ -92,6 +110,7 @@ typedef TIME_T_TYPE Time_t; * */ typedef struct TaskRunTimeStats_s { + Base_t id; /**< The task identifier which is used by xTaskGetHandleById() to return the task handle. */ Time_t lastRunTime; /**< The runtime duration in microseconds the last time the task was executed by the scheduler. */ Time_t totalRunTime; /**< The total runtime duration in microseconds the task has been executed by the scheduler. */ } TaskRunTimeStats_t; @@ -115,8 +134,8 @@ typedef struct TaskRunTimeStats_s { * */ typedef struct TaskInfo_s { - Base_t id; /**< The task identifier which is used by xTaskGetHandleById() to return the task handle. */ - char name[CONFIG_TASK_NAME_BYTES]; /**< The name of the task which is used by xTaskGetHandleByName() to return the task handle. */ + Base_t id; /**< The task identifier which is used by xTaskGetHandleById() to return the task handle. */ + char name[CONFIG_TASK_NAME_BYTES]; /**< The name of the task which is used by xTaskGetHandleByName() to return the task handle. This is NOT a null terminated string. */ TaskState_t state; /**< The state the task is in which is one of four states specified in the TaskState_t enumerated data type. */ Time_t lastRunTime; /**< The runtime duration in microseconds the last time the task was executed by the scheduler. */ Time_t totalRunTime; /**< The total runtime duration in microseconds the task has been executed by the scheduler. */ @@ -139,7 +158,7 @@ typedef struct TaskInfo_s { */ typedef struct TaskNotification_s { Base_t notificationBytes; /**< The number of bytes in the notificationValue member that makes up the notification value. This cannot exceed CONFIG_NOTIFICATION_VALUE_BYTES. */ - char notificationValue[CONFIG_NOTIFICATION_VALUE_BYTES]; /**< The char array that contains the actual notification value. */ + char notificationValue[CONFIG_NOTIFICATION_VALUE_BYTES]; /**< The char array that contains the actual notification value. This is NOT a null terminated string. */ } TaskNotification_t; /** @@ -159,7 +178,7 @@ typedef struct TaskNotification_s { */ typedef struct QueueMessage_s { Base_t messageBytes; /**< The number of bytes in the messageValue member that makes up the message value. This cannot exceed CONFIG_MESSAGE_VALUE_BYTES. */ - char messageValue[CONFIG_MESSAGE_VALUE_BYTES]; /**< the char array that contains the actual message value. */ + char messageValue[CONFIG_MESSAGE_VALUE_BYTES]; /**< the char array that contains the actual message value. This is NOT a null terminated string. */ } QueueMessage_t; /** @@ -176,7 +195,7 @@ typedef struct QueueMessage_s { * */ typedef struct SystemInfo_s { - char productName[PRODUCTNAME_SIZE]; /**< The name of the operating system or product. This is always HeliOS. */ + char productName[OS_PRODUCT_NAME_SIZE]; /**< The name of the operating system or product. Its length is defined by OS_PRODUCT_NAME_SIZE. This is NOT a null terminated string. */ Base_t majorVersion; /**< The major version number of HeliOS and is Symantec Versioning Specification (SemVer) compliant. */ Base_t minorVersion; /**< The minor version number of HeliOS and is Symantec Versioning Specification (SemVer) compliant. */ Base_t patchVersion; /**< The patch version number of HeliOS and is Symantec Versioning Specification (SemVer) compliant. */ @@ -204,7 +223,7 @@ typedef void Task_t; * The TaskParm_t type is used to pass a parameter to a task at the time of creation using * xTaskCreate(). A task parameter is a pointer of type void and can point to any number * of intrinsic types, arrays and/or user defined structures which can be passed to a - * task. It is up the the end-user to manage allocate and free the memory related to + * task. It is up the the end-user to manage, allocate and free the memory related to * these objects using xMemAlloc() and xMemFree(). The TaskParm_t should be declared * as xTaskParm. * @@ -252,7 +271,7 @@ typedef void Timer_t; * * A simple data type is often needed as an argument for a system call or a return type. * The xBase type is used in such a case where there are no other structural data - * requirements. + * requirements is typically an unsigned 8-bit integer. * * @sa Base_t * @@ -409,7 +428,7 @@ typedef TIME_T_TYPE Time_t; * * The xTime type is used by several of the task and timer related system calls to express time. * The unit of measure for time is always microseconds. - * + * * @sa Time_t * */ @@ -418,7 +437,7 @@ typedef Time_t xTime; /** * @brief Enumerated type for task states. * - * A task can be in one of the four possible states defined in the xTaskState + * A task can be in one of the four possible states defined in the TaskState_t * enumerated type. The state of a task is changed by calling xTaskResume(), * xTaskSuspend() or xTaskWait(). * @@ -430,6 +449,21 @@ typedef Time_t xTime; */ typedef TaskState_t xTaskState; +/** + * @brief Enumerated type for scheduler states. + * + * The scheduler can be in one of four possible states defined in the SchedulerState_t + * enumerated type. The state of the scheduler is changed by calling xTaskSuspendAll() + * and xTaskResumeAll(). The state can be obtained by calling xTaskGetSchedulerState(). + * + * @sa xSchedulerState + * @sa xTaskSuspendAll() + * @sa xTaskResumeAll() + * @sa xTaskGetSchedulerState() + * + */ +typedef SchedulerState_t xSchedulerState; + /** * @brief Data structure for system informaiton. * @@ -447,26 +481,31 @@ typedef SystemInfo_t *xSystemInfo; /** * @brief A C macro to simplify casting and dereferencing a task paramater. - * + * * When a task paramater is passed to a task, it is passed as a pointer of - * type void. To use the paramater it must first be casted to the correct type + * type void. To use the paramater, it must first be cast to the correct type * and dereferenced. The following is an example of how the DEREF_TASKPARM() C * macro simplifies that process. - * - * @code + * + * @code {.c} * void myTask_main(xTask task_, xTaskParm parm_) { * int i; - * + * * i = DEREF_TASKPARM(int, parm_); * + * i++; + * + * DEREF_TASKPARM(int, parm_) = i; + * + * return; * } * @endcode * - * @param t The data type to case the task paramater to (e.g., int). - * @param p The task pointer often named parm_. + * @param t The data type to cast the task paramater to (e.g., int). + * @param p The task pointer, often named parm_. */ #if !defined(DEREF_TASKPARM) -#define DEREF_TASKPARM(t, p) *((t *) p) +#define DEREF_TASKPARM(t, p) *((t *)p) #endif /* In the event HeliOS is compiled with a C++ compiler, make the system calls (written in C) @@ -475,13 +514,30 @@ visible to C++. */ extern "C" { #endif +/** + * @brief System call to handle assertions. + * + * The SystemAssert() system call handles assertions. The SystemAssert() system + * call should not be called directly. Instead, the SYSASSERT() macro should be used. + * The system assertion functionality will only work when the CONFIG_ENABLE_SYSTEM_ASSERT + * and CONFIG_SYSTEM_ASSERT_BEHAVIOR settings are defined. + * + * @sa SYSASSERT + * @sa CONFIG_ENABLE_SYSTEM_ASSERT + * @sa CONFIG_SYSTEM_ASSERT_BEHAVIOR + * + * @param file_ This is automatically defined by the compiler's definition of _FILE_ + * @param line_ This is automatically defined by the compiler's definition of _LINE_ + */ +void SystemAssert(const char *file_, int line_); + /** * @brief System call to allocate memory from the heap. - * - * The xMemAlloc() system call will allocate memory from the heap for HeliOS system - * calls and end-user tasks. The size of the heap in bytes is dependent on the - * CONFIG_HEAP_SIZE_IN_BLOCKS and CONFIG_HEAP_BLOCK_SIZE settings. xMemAlloc() automatically - * clears the memory it allocates. + * + * The xMemAlloc() system call allocates memory from the heap for HeliOS system + * calls and end-user tasks. The size of the heap, in bytes, is dependent on the + * CONFIG_HEAP_SIZE_IN_BLOCKS and CONFIG_HEAP_BLOCK_SIZE settings. xMemAlloc() + * functions similarly to calloc() in that it clears the memory it allocates. * * @sa CONFIG_HEAP_SIZE_IN_BLOCKS * @sa CONFIG_HEAP_BLOCK_SIZE @@ -490,11 +546,11 @@ extern "C" { * @param size_ The amount (size) of the memory to be allocated from the heap in bytes. * @return void* If successful, xMemAlloc() returns a pointer to the newly allocated memory. * If unsuccessful, the system call will return null. - * + * * @note HeliOS technically does not allocate memory from what is traditionally heap memory. * HeliOS uses a private "heap" which is actually static memory allocated at compile time. This * is done to maintain MISRA C:2012 compliance since standard library functions like malloc(), - * cmalloc() and free() are not permitted. + * calloc() and free() are not permitted. */ void *xMemAlloc(size_t size_); @@ -508,24 +564,26 @@ void *xMemAlloc(size_t size_); * * @param ptr_ The pointer to the allocated heap memory to be freed. * - * @warning xMemFree() cannot be used to free memory allocated by xTaskCreate(), - * xTimerCreate() or xQueueCreate(). Memory allocated by those system calls must - * be freed by their respective delete system calls. + * @warning xMemFree() cannot be used to free memory allocated for kernel objects. + * Memory allocated by xTaskCreate(), xTimerCreate() or xQueueCreate() must + * be freed by their respective delete system calls (i.e., xTaskDelete()). */ void xMemFree(void *ptr_); /** * @brief System call to return the amount of allocated heap memory. * - * The xMemGetUsed() system call returns the amount of heap memory in bytes + * The xMemGetUsed() system call returns the amount of heap memory, in bytes, * that is currently allocated. Calls to xMemAlloc() increases and xMemFree() - * decreases the amount. + * decreases the amount of memory in use. * * @return size_t The amount of memory currently allocated in bytes. If no heap * memory is currently allocated, xMemGetUsed() will return zero. - * - * @note xMemGetUsed() also checks the health of the heap and will return zero if - * it detects a consistency issue with the heap. + * + * @note xMemGetUsed() returns the amount of heap memory that is currently + * allocated to end-user objects AND kernel objects. However, only end-user + * objects may be freed using xMemFree(). Kernel objects must be freed using + * their respective delete system call (e.g., xTaskDelete()). */ size_t xMemGetUsed(void); @@ -537,15 +595,16 @@ size_t xMemGetUsed(void); * xMemGetSize() will return zero bytes. * * @param ptr_ The pointer to the allocated heap memory to obtain the size of the - * memory that is allocated. + * memory, in bytes, that is allocated. * @return size_t The amount of memory currently allocated to the specific pointer in bytes. If * the pointer is invalid or null, xMemGetSize() will return zero. - * + * * @note If the pointer ptr_ points to a structure that, for example, is 48 bytes in size * base on sizeof(), xMemGetSize() will return the number of bytes allocated by the block(s) * that contain the structure. Assuming the default block size of 32, a 48 byte structure would require * TWO blocks so xMemGetSize() would return 64 - not 48. xMemGetSize() also checks the health of the - * heap and will return zero if it detects a consistency issue with the heap. + * heap and will return zero if it detects a consistency issue with the heap. Thus, xMemGetSize() + * can be used to validate pointers before the objects they reference are accessed. */ size_t xMemGetSize(void *ptr_); @@ -564,7 +623,7 @@ size_t xMemGetSize(void *ptr_); * on the setting CONFIG_QUEUE_MINIMUM_LIMIT. * @return xQueue A queue is returned if successful, otherwise null is returned if unsuccessful. * - * @warning The message queue memory should only be freed by xQueueDelete() and NOT xMemFree(). + * @warning The message queue memory can only be freed by xQueueDelete(). */ xQueue xQueueCreate(xBase limit_); @@ -573,7 +632,8 @@ xQueue xQueueCreate(xBase limit_); * * The xQueueDelete() system call will delete a message queue created by xQueueCreate(). xQueueDelete() * will delete a queue regardless of how many messages the queue contains at the time xQueueDelete() - * is called. + * is called. Any messages the message queue contains will be deleted in the process of deleting the + * message queue. * * @sa xQueueCreate() * @@ -590,9 +650,6 @@ void xQueueDelete(xQueue queue_); * @param queue_ The queue to return the length of. * @return xBase The number of messages in the queue. If unsuccessful or if the queue is empty, * xQueueGetLength() returns zero. - * - * @note The xQueueGetLength() system call will also check the health of the queue and returns - * zero if a consistency issues is detected. */ xBase xQueueGetLength(xQueue queue_); @@ -605,9 +662,6 @@ xBase xQueueGetLength(xQueue queue_); * @param queue_ The queue to determine whether it is empty. * @return xBase True if the queue is empty. False if the queue has one or more messages. xQueueIsQueueEmpty() * will also return false if the queue parameter is invalid. - * - * @note The xQueueIsQueueEmpty() will also check the health of the queue and return false - * if a consistency issue is detected. */ xBase xQueueIsQueueEmpty(xQueue queue_); @@ -621,9 +675,6 @@ xBase xQueueIsQueueEmpty(xQueue queue_); * @param queue_ The queue to determine whether it is full. * @return xBase True if the queue is full. False if the queue has zero. xQueueIsQueueFull() * will also return false if the queue parameter is invalid. - * - * @note The xQueueIsQueueFull() will also check the health of the queue and return false - * if a consistency issue is detected. */ xBase xQueueIsQueueFull(xQueue queue_); @@ -643,7 +694,7 @@ xBase xQueueMessagesWaiting(xQueue queue_); * @brief System call to send a message using a message queue. * * The xQueueSend() system call will send a message using the specified message queue. The size of the message - * value is passed in the message bytes parameter. The maximum message value size in byes is dependent + * value is passed in the message bytes parameter. The maximum message value size in bytes is dependent * on the CONFIG_MESSAGE_VALUE_BYTES setting. * * @sa CONFIG_MESSAGE_VALUE_BYTES @@ -654,9 +705,10 @@ xBase xQueueMessagesWaiting(xQueue queue_); * @param messageBytes_ The number of bytes contained in the message value. The number of bytes must be greater than * zero and less than or equal to the setting CONFIG_MESSAGE_VALUE_BYTES. * @param messageValue_ The message value. If the message value is greater than defined in CONFIG_MESSAGE_VALUE_BYTES, - * only the number of bytes defined in CONFIG_MESSAGE_VALUE_BYTES will be copied into the message value. - * @return xBase xQueueSend() returns true if the message was sent to the queue successfully. Otherwise - * false if unsuccessful. + * only the number of bytes defined in CONFIG_MESSAGE_VALUE_BYTES will be copied into the message value. The message + * value is NOT a null terminated string. + * @return xBase xQueueSend() returns RETURN_SUCCESS if the message was sent to the queue successfully. Otherwise + * RETURN_FAILURE if unsuccessful. */ xBase xQueueSend(xQueue queue_, xBase messageBytes_, const char *messageValue_); @@ -709,15 +761,15 @@ xQueueMessage xQueueReceive(xQueue queue_); * * The xTaskStartScheduler() system call passes control to the HeliOS scheduler. This system * call will not return until xTaskSuspendAll() is called. If xTaskSuspendAll() is called, xTaskResumeAll() - * must be called before xTaskStartScheduler() can be called again. + * must be called before xTaskStartScheduler() can be called again to continue executing tasks. * */ void xTaskStartScheduler(void); /** - * @brief System call to set scheduler running system flag to true. + * @brief System call to set scheduler state to running. * - * The xTaskResumeAll() system call will set the scheduler system flag so the next + * The xTaskResumeAll() system call will set the scheduler state to running so the next * call to xTaskStartScheduler() will resume execute of all tasks. The state of each task * is not altered by xTaskSuspendAll() or xTaskResumeAll(). * @@ -726,9 +778,9 @@ void xTaskStartScheduler(void); void xTaskResumeAll(void); /** - * @brief System call to set the scheduler running system flag to false. + * @brief System call to set the scheduler state to suspended. * - * The xTaskSuspendAll() system call will set the scheduler running system flag to false + * The xTaskSuspendAll() system call will set the scheduler state to suspended * so the scheduler will stop and return. The state of each task is not altered by * xTaskSuspendAll() or xTaskResumeAll(). * @@ -759,7 +811,7 @@ xSystemInfo xSystemGetSystemInfo(void); * a task. They MUST be called outside of the scope of the HeliOS scheduler. * * @param name_ The ASCII name of the task which can be used by xTaskGetHandleByName() to obtain the task pointer. The - * length of the name is depended on the CONFIG_TASK_NAME_BYTES. The task name is NOT a null terminated char array. + * length of the name is depended on the CONFIG_TASK_NAME_BYTES. The task name is NOT a null terminated char string. * @param callback_ The callback pointer to the task main function. This is the function that will be invoked * by the scheduler when a task is scheduled for execution. * @param taskParameter_ A pointer to any type or structure that the end-user wants to pass into the task as @@ -773,7 +825,7 @@ xSystemInfo xSystemGetSystemInfo(void); * @sa CONFIG_TASK_NAME_BYTES * * @warning xTaskCreate() MUST be called outside the scope of the HeliOS scheduler (i.e., not from a task's main). - * The task memory should only be freed by xTaskDelete() and NOT xMemFree(). + * The task memory can only be freed by xTaskDelete(). */ xTask xTaskCreate(const char *name_, void (*callback_)(xTask, xTaskParm), xTaskParm taskParameter_); @@ -799,7 +851,7 @@ void xTaskDelete(xTask task_); * * @sa CONFIG_TASK_NAME_BYTES * - * @param name_ The ASCII name of the task to return the handle pointer for. + * @param name_ The ASCII name of the task to return the handle pointer for. The task name is NOT a null terminated string. * @return xTask A pointer to the task handle. xTaskGetHandleByName() returns null if the * name cannot be found. */ @@ -821,7 +873,7 @@ xTask xTaskGetHandleById(xBase id_); /** * @brief System call to return task runtime statistics for all tasks. - * + * * The xTaskGetAllRunTimeStats() system call will return the runtime statistics for all * of the tasks regardless of their state. The xTaskGetAllRunTimeStats() system call returns * the xTaskRunTimeStats type. An xBase variable must be passed by reference to xTaskGetAllRunTimeStats() @@ -831,67 +883,82 @@ xTask xTaskGetHandleById(xBase id_); * * @sa xTaskRunTimeStats * @sa xMemFree() - * - * @param tasks_ An variable of type xBase passed by reference which will contain the number of tasks + * + * @param tasks_ A variable of type xBase passed by reference which will contain the number of tasks * upon return. If no tasks currently exist, this variable will not be modified. * @return xTaskRunTimeStats The runtime stats returned by xTaskGetAllRunTimeStats(). If there are * currently no tasks then this will be null. This memory must be freed by xMemFree(). - * + * * @warning The memory allocated by xTaskGetAllRunTimeStats() must be freed by xMemFree(). - * - * @note The xTaskGetAllRuntTimeStats() system call will also check the health of the task list and - * will return null if a consistency issue is detected. */ xTaskRunTimeStats xTaskGetAllRunTimeStats(xBase *tasks_); /** * @brief System call to return task runtime statistics for the specified task. - * + * * The xTaskGetTaskRunTimeStats() system call returns the task runtime statistics for * one task. The xTaskGetTaskRunTimeStats() system call returns the xTaskRunTimeStats type. * The memory must be freed by calling xMemFree() after it is no longer needed. * * @sa xTaskRunTimeStats * @sa xMemFree() - * + * * @param task_ The task to get the runtime statistics for. * @return xTaskRunTimeStats The runtime stats returned by xTaskGetTaskRunTimeStats(). * xTaskGetTaskRunTimeStats() will return null of the task cannot be found. - * + * * @warning The memory allocated by xTaskGetTaskRunTimeStats() must be freed by xMemFree(). */ xTaskRunTimeStats xTaskGetTaskRunTimeStats(xTask task_); /** * @brief System call to return the number of tasks regardless of their state. - * + * * The xTaskGetNumberOfTasks() system call returns the current number of tasks * regardless of their state. * * @return xBase The number of tasks. - * - * @note The xTaskGetNumberOfTasks() system call will also check the health of the task list and - * will return zero if a consistency issue is detected. */ xBase xTaskGetNumberOfTasks(void); /** - * @brief The xTaskGetTaskInfo() system call returns the xTaskInfo structure containing + * @brief System call to return the details of a task. + * + * The xTaskGetTaskInfo() system call returns the xTaskInfo structure containing * the details of the task including its identifier, name, state and runtime statistics. * + * @sa xTaskInfo + * * @param task_ The task to return the details of. * @return xTaskInfo The xTaskInfo structure containing the task details. xTaskGetTaskInfo() * returns null if the task cannot be found. - * + * * @warning The memory allocated by xTaskGetTaskInfo() must be freed by xMemFree(). */ xTaskInfo xTaskGetTaskInfo(xTask task_); /** - * @brief System call to return the state of a task. + * @brief System call to return the details of all tasks. * + * The xTaskGetAllTaskInfo() system call returns the xTaskInfo structure containing + * the details of ALL tasks including their identifier, name, state and runtime statistics. + * + * @sa xTaskInfo + * + * @param tasks_ A variable of type xBase passed by reference which will contain the number of tasks + * upon return. If no tasks currently exist, this variable will not be modified. + * @return xTaskInfo The xTaskInfo structure containing the tasks details. xTaskGetAllTaskInfo() + * returns null if there no tasks or if a consistency issue is detected. + * + * @warning The memory allocated by xTaskGetAllTaskInfo() must be freed by xMemFree(). + */ +xTaskInfo *xTaskGetAllTaskInfo(xBase *tasks_); + +/** + * @brief System call to return the state of a task. + * * The xTaskGetTaskState() system call will return the state of the task. - * + * * @sa xTaskState * * @param task_ The task to return the state of. @@ -902,26 +969,26 @@ xTaskState xTaskGetTaskState(xTask task_); /** * @brief System call to return the ASCII name of a task. - * + * * The xTaskGetName() system call returns the ASCII name of the task. The size of the * task is dependent on the setting CONFIG_TASK_NAME_BYTES. The task name is NOT a null - * terminated char array. The memory allocated for the char array must be freed by + * terminated char string. The memory allocated for the char array must be freed by * xMemFree() when no longer needed. - * + * * @sa CONFIG_TASK_NAME_BYTES * @sa xMemFree() * * @param task_ The task to return the name of. * @return char* A pointer to the char array containing the ASCII name of the task. The task name - * is NOT a null terminated char array. xTaskGetName() will return null if the task cannot be found. - * + * is NOT a null terminated char string. xTaskGetName() will return null if the task cannot be found. + * * @warning The memory allocated by xTaskGetName() must be free by xMemFree(). */ char *xTaskGetName(xTask task_); /** * @brief System call to return the task identifier for a task. - * + * * The xTaskGetId() system call returns the task identifier for the task. * * @param task_ The task to return the identifier of. @@ -932,7 +999,7 @@ xBase xTaskGetId(xTask task_); /** * @brief System call to clear a waiting direct to task notification. - * + * * The xTaskNotifyStateClear() system call will clear a waiting direct to task notification if one * exists without returning the notification. * @@ -942,7 +1009,7 @@ void xTaskNotifyStateClear(xTask task_); /** * @brief System call to check if a direct to task notification is waiting. - * + * * The xTaskNotificationIsWaiting() system call will return true or false depending * on whether there is a direct to task notification waiting for the task. * @@ -954,33 +1021,33 @@ xBase xTaskNotificationIsWaiting(xTask task_); /** * @brief System call to give another task a direct to task notification. - * + * * The xTaskNotifyGive() system call will give a direct to task notification to the specified task. The * task notification bytes is the number of bytes contained in the notification value. The number of * notification bytes must be between one and the CONFIG_NOTIFICATION_VALUE_BYTES setting. The notification * value must contain a pointer to a char array containing the notification value. If the task already * has a waiting task notification, xTaskNotifyGive() will NOT overwrite the waiting task notification. * xTaskNotifyGive() will return true if the direct to task notification was successfully given. - * + * * @sa CONFIG_NOTIFICATION_VALUE_BYTES * @sa xTaskNotifyTake() * * @param task_ The task to send the task notification to. * @param notificationBytes_ The number of bytes contained in the notification value. The number must be * between one and the CONFIG_NOTIFICATION_VALUE_BYTES setting. - * @param notificationValue_ A char array containing the notification value. - * @return xBase True if the direct to task notification was successfully given, false if not. + * @param notificationValue_ A char array containing the notification value. The notification value is NOT a null terminated string. + * @return xBase RETURN_SUCCESS if the direct to task notification was successfully given, RETURN_FAILURE if not. */ Base_t xTaskNotifyGive(xTask task_, xBase notificationBytes_, const char *notificationValue_); /** * @brief System call to take a direct to task notification from another task. - * + * * The xTaskNotifyTake() system call will return the waiting direct to task notification if there * is one. The xTaskNotifyTake() system call will return an xTaskNotification structure containing * the notification bytes and its value. The memory allocated by xTaskNotifyTake() must be freed * by xMemFree(). - * + * * @sa xTaskNotification * @sa xTaskNotifyGive() * @sa xMemFree() @@ -990,18 +1057,18 @@ Base_t xTaskNotifyGive(xTask task_, xBase notificationBytes_, const char *notifi * @return xTaskNotification The xTaskNotification structure containing the notification bytes * and value. xTaskNotifyTake() will return null if no waiting task notification exists or if * the task cannot be found. - * + * * @warning The memory allocated by xTaskNotifyTake() must be freed by xMemFree(). */ xTaskNotification xTaskNotifyTake(xTask task_); /** * @brief System call to resume a task. - * + * * The xTaskResume() system call will resume a suspended task. Tasks are suspended on creation * so either xTaskResume() or xTaskWait() must be called to place the task in a state that the scheduler * will execute. - * + * * @sa xTaskState * @sa xTaskSuspend() * @sa xTaskWait() @@ -1012,10 +1079,10 @@ void xTaskResume(xTask task_); /** * @brief System call to suspend a task. - * + * * The xTaskSuspend() system call will suspend a task. A task that has been suspended * will not be executed by the scheduler until xTaskResume() or xTaskWait() is called. - * + * * @sa xTaskState * @sa xTaskResume() * @sa xTaskWait() @@ -1026,7 +1093,7 @@ void xTaskSuspend(xTask task_); /** * @brief System call to place a task in a waiting state. - * + * * The xTaskWait() system call will place a task in the waiting state. A task must * be in the waiting state for event driven multitasking with either direct to task * notifications OR setting the period on the task timer with xTaskChangePeriod(). A task @@ -1035,21 +1102,21 @@ void xTaskSuspend(xTask task_); * @sa xTaskState * @sa xTaskResume() * @sa xTaskSuspend() - * + * * @param task_ The task to place in the waiting state. */ void xTaskWait(xTask task_); /** * @brief System call to set the task timer period. - * + * * The xTaskChangePeriod() system call will change the period (microseconds) on the task timer * for the specified task. The timer period must be greater than zero. To have any effect, the task * must be in the waiting state set by calling xTaskWait() on the task. Once the timer period is set * and the task is in the waiting state, the task will be executed every timerPeriod_ microseconds. * Changing the period to zero will prevent the task from being executed even if it is in the waiting state * unless it were to receive a direct to task notification. - * + * * @sa xTaskWait() * @sa xTaskGetPeriod() * @sa xTaskResetTimer() @@ -1061,10 +1128,10 @@ void xTaskChangePeriod(xTask task_, xTime timerPeriod_); /** * @brief System call to get the task timer period. - * + * * The xTaskGetPeriod() will return the period for the timer for the specified task. See * xTaskChangePeriod() for more information on how the task timer works. - * + * * @sa xTaskWait() * @sa xTaskChangePeriod() * @sa xTaskResetTimer() @@ -1077,45 +1144,59 @@ xTime xTaskGetPeriod(xTask task_); /** * @brief System call to reset the task timer. - * + * * The xTaskResetTimer() system call will reset the task timer. xTaskResetTimer() does not change * the timer period or the task state when called. See xTaskChangePeriod() for more details on task timers. * * @sa xTaskWait() * @sa xTaskChangePeriod() * @sa xTaskGetPeriod() - * + * * @param task_ The task to reset the task timer for. */ void xTaskResetTimer(xTask task_); +/** + * @brief System call to get the state of the scheduler. + * + * The xTaskGetSchedulerState() system call will return the state of the scheduler. The + * state of the scheduler can only be changed using xTaskSuspendAll() and xTaskResumeAll(). + * + * @sa xSchedulerState + * @sa xTaskSuspendAll() + * @sa xTaskResumeAll() + * + * @return xSchedulerState The state of the scheduler. + */ +xSchedulerState xTaskGetSchedulerState(void); + /** * @brief System call to create a new timer. - * + * * The xTimerCreate() system call will create a new timer. Timers differ from * task timers in that they do not create events that effect the scheduling of a task. * Timers can be used by tasks to initiate various task activities based on a specified * time period represented in microseconds. The memory allocated by xTimerCreate() must * be freed by xTimerDelete(). Unlike tasks, timers may be created and deleted within * tasks. - * + * * @sa xTimer * @sa xTimerDelete() * * @param timerPeriod_ The number of microseconds before the timer expires. * @return xTimer The newly created timer. If the timer period parameter is less than zero * or xTimerCreate() was unable to allocate the required memory, xTimerCreate() will return null. - * - * @warning The timer memory should only be freed by xTimerDelete() and NOT xMemFree(). + * + * @warning The timer memory can only be freed by xTimerDelete(). */ xTimer xTimerCreate(xTime timerPeriod_); /** * @brief System call will delete a timer. - * + * * The xTimerDelete() system call will delete a timer. For more information on timers see the * xTaskTimerCreate() system call. - * + * * @sa xTimerCreate() * * @param timer_ The timer to be deleted. @@ -1124,13 +1205,13 @@ void xTimerDelete(xTimer timer_); /** * @brief System call to change the period of a timer. - * + * * The xTimerChangePeriod() system call will change the period of the specified timer. * The timer period is measured in microseconds. If the timer period is zero, the xTimerHasTimerExpired() * system call will always return false. * * @sa xTimerHasTimerExpired() - * + * * @param timer_ The timer to change the period for. * @param timerPeriod_ The timer period in is microseconds. Timer period must be zero or greater. */ @@ -1138,7 +1219,7 @@ void xTimerChangePeriod(xTimer timer_, xTime timerPeriod_); /** * @brief System call to get the period of a timer. - * + * * The xTimerGetPeriod() system call will return the current timer period * for the specified timer. * @@ -1150,10 +1231,10 @@ xTime xTimerGetPeriod(xTimer timer_); /** * @brief System call to check if a timer is active. - * + * * The xTimerIsTimerActive() system call will return true of the timer has been * started with xTimerStart(). - * + * * @sa xTimerStart() * * @param timer_ The timer to check if active. @@ -1163,11 +1244,11 @@ xBase xTimerIsTimerActive(xTimer timer_); /** * @brief System call to check if a timer has expired. - * + * * The xTimerHasTimerExpired() system call will return true or false dependent on whether * the timer period for the specified timer has elapsed. xTimerHasTimerExpired() will NOT * reset the timer. Timers will not automatically reset. Timers MUST be reset with xTimerReset(). - * + * * @sa xTimerReset() * * @param timer_ The timer to determine if the period has expired. @@ -1177,7 +1258,7 @@ xBase xTimerHasTimerExpired(xTimer timer_); /** * @brief System call to reset a timer. - * + * * The xTimerReset() system call will reset the start time of the timer to zero. * * @param timer_ The timer to be reset. @@ -1186,10 +1267,10 @@ void xTimerReset(xTimer timer_); /** * @brief System call to start a timer. - * + * * The xTimerStart() system call will place the timer in the running (active) state. Neither xTimerStart() nor * xTimerStop() will reset the timer. Timers can only be reset with xTimerReset(). - * + * * @sa xTimerStop() * @sa xTimerReset() * @@ -1200,7 +1281,7 @@ void xTimerStart(xTimer timer_); /** * @brief The xTimerStop() system call will place the timer in the stopped state. Neither xTimerStart() nor * xTimerStop() will reset the timer. Timers can only be reset with xTimerReset(). - * + * * @sa xTimerStart() * @sa xTimerReset() * @@ -1210,13 +1291,19 @@ void xTimerStop(xTimer timer_); /** * @brief The xSystemHalt() system call will halt HeliOS. - * + * * The xSystemHalt() system call will halt HeliOS. Once xSystemHalt() is called, * the system must be reset. - * + * */ void xSystemHalt(void); + +/* For debugging the heap only. */ +#if defined(MEMDUMP_) +void memdump_(void); +#endif + /* In the event HeliOS is compiled with a C++ compiler, make the system calls (written in C) visible to C++. */ #ifdef __cplusplus diff --git a/src/config.h b/src/config.h index 2b4f546..a939ab1 100644 --- a/src/config.h +++ b/src/config.h @@ -1,96 +1,183 @@ /** * @file config.h * @author Manny Peterson (mannymsp@gmail.com) - * @brief Kernel header file for user definable settings in HelIOS - * @version 0.3.0 + * @brief Kernel header file for user definable settings + * @version 0.3.1 * @date 2022-01-31 - * + * * @copyright * HeliOS Embedded Operating System * Copyright (C) 2020-2022 Manny Peterson - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - * + * */ #ifndef CONFIG_H_ #define CONFIG_H_ + + /* The following configurable settings may be changed by the end-user -to customize HeliOS for their specific application. */ +to customize the HeliOS kernel for their specific application. */ /** - * @brief Define the size in bytes of the message queue message value. + * @brief Define to enable the Arduino API C++ interface. + * + * Because HeliOS kernel is written in C, the Arduino API cannot + * be called directly from the kernel. For example, assertions are + * unable to be written to the serial bus in applications using the + * Arduino platform/tool-chain. The CONFIG_ENABLE_ARDUINO_CPP_INTERFACE + * builds the included arduino.cpp file to allow the kernel to call the + * Arduino API through wrapper functions such as ArduinoAssert(). The + * arduino.cpp file can be found in the /extras directory. It must + * be copied into the /src directory to be built. + * * + * @note On some MCU's like the 8-bit AVRs, it is necessary to undefine + * the DISABLE_INTERRUPTS() macro because interrupts must be enabled + * to write to the serial bus. + * + */ +/* +#define CONFIG_ENABLE_ARDUINO_CPP_INTERFACE +*/ + + + +/** + * @brief Define to enable system assertions. + * + * The CONFIG_ENABLE_SYSTEM_ASSERT setting allows the end-user to + * enable system assertions in HeliOS. Once enabled, the end-user + * must define CONFIG_SYSTEM_ASSERT_BEHAVIOR for there + * to be an effect. By default the CONFIG_ENABLE_SYSTEM_ASSERT + * setting is not defined. + * + * @sa CONFIG_SYSTEM_ASSERT_BEHAVIOR + * + */ +/* +#define CONFIG_ENABLE_SYSTEM_ASSERT +*/ + + + +/** + * @brief Define the system assertion behavior. + * + * The CONFIG_SYSTEM_ASSERT_BEHAVIOR setting allows the end-user + * to specify the behavior (code) of the assertion which is called + * when CONFIG_ENABLE_SYSTEM_ASSERT is defined. Typically some sort + * of output is generated over a serial or other interface. By default + * the CONFIG_SYSTEM_ASSERT_BEHAVIOR is not defined. + * + * @note In order to use the ArduinoAssert() functionality, the + * CONFIG_ENABLE_ARDUINO_CPP_INTERFACE setting must be enabled. + * + * @sa CONFIG_ENABLE_SYSTEM_ASSERT + * @sa CONFIG_ENABLE_ARDUINO_CPP_INTERFACE + * + * @code {.c} + * #define CONFIG_SYSTEM_ASSERT_BEHAVIOR(f, l) ArduinoAssert( f , l ) + * @endcode + * + */ +/* +#define CONFIG_SYSTEM_ASSERT_BEHAVIOR(f, l) ArduinoAssert( f , l ) +*/ + + + +/** + * @brief Define the size in bytes of the message queue message value. + * * Setting the CONFIG_MESSAGE_VALUE_BYTES allows the end-user to define * the size of the message queue message value. The larger the size of the * message value, the greater impact there will be on system performance. - * The default size is 16 bytes. The literal must be appended with "u" to + * The default size is 8 bytes. The literal must be appended with "u" to * maintain MISRA C:2012 compliance. - * + * * @sa xQueueMessage - * + * */ -#define CONFIG_MESSAGE_VALUE_BYTES 16u +#define CONFIG_MESSAGE_VALUE_BYTES 8u + + + /** * @brief Define the size in bytes of the direct to task notification value. - * + * * Setting the CONFIG_NOTIFICATION_VALUE_BYTES allows the end-user to define * the size of the direct to task notification value. The larger the size of the * notification value, the greater impact there will be on system performance. - * The default size is 16 bytes. The literal must be appended with "u" to + * The default size is 8 bytes. The literal must be appended with "u" to * maintain MISRA C:2012 compliance. - * + * * @sa xTaskNotification */ -#define CONFIG_NOTIFICATION_VALUE_BYTES 16u +#define CONFIG_NOTIFICATION_VALUE_BYTES 8u + + + /** * @brief Define the size in bytes of the ASCII task name. - * + * * Setting the CONFIG_TASK_NAME_BYTES allows the end-user to define * the size of the ASCII task name. The larger the size of the task * name, the greater impact there will be on system performance. - * The default size is 16 bytes. The literal must be appended with "u" + * The default size is 8 bytes. The literal must be appended with "u" * to maintain MISRA C:2012 compliance. - * + * * @sa xTaskInfo - * + * */ -#define CONFIG_TASK_NAME_BYTES 16u +#define CONFIG_TASK_NAME_BYTES 8u + + + /** * @brief Define the number of blocks in the heap. - * + * * Setting CONFIG_HEAP_SIZE_IN_BLOCKS allows the end-user to * define the size of the heap in blocks. The size of a block * in the heap is determined by the CONFIG_HEAP_BLOCK_SIZE which * is represented in bytes. The size of the heap needs to be * adjusted to fit the memory requirements of the end-user's - * application. The default value is 512 blocks. The literal - * must be appended with "u" to maintain MISRA C:2012 compliance. - * + * application. By default the CONFIG_HEAP_SIZE_IN_BLOCKS + * setting is not defined. The literal must be appended with "u" + * to maintain MISRA C:2012 compliance. + * * @sa xMemAlloc() * @sa xMemFree() * @sa CONFIG_HEAP_BLOCK_SIZE - * + * + * @note To use the platform and/or tool-chain defaults, + * leave CONFIG_HEAP_SIZE_IN_BLOCKS undefined. + * */ +/* #define CONFIG_HEAP_SIZE_IN_BLOCKS 512u +*/ + + /** * @brief Define the heap block size in bytes. - * + * * Setting CONFIG_HEAP_BLOCK_SIZE allows the end-user to * define the size of a heap block in bytes. The block size * should be set to achieve the best possible utilization @@ -98,17 +185,20 @@ to customize HeliOS for their specific application. */ * heap for smaller requests for heap. A block size that is too small * will waste heap on entries. The default value is 32 bytes. The * literal must be appended with "u" to maintain MISRA C:2012 compliance. - * + * * @sa xMemAlloc() * @sa xMemFree() * @sa CONFIG_HEAP_SIZE_IN_BLOCKS - * + * */ #define CONFIG_HEAP_BLOCK_SIZE 32u + + + /** * @brief Define the minimum value for a message queue limit. - * + * * Setting the CONFIG_QUEUE_MINIMUM_LIMIT allows the end-user to define * the MINIMUM length limit a message queue can be created with xQueueCreate(). * When a message queue length equals its limit, the message queue will @@ -116,11 +206,11 @@ to customize HeliOS for their specific application. */ * A full queue will also not accept messages from xQueueSend(). The default * value is 5. The literal must be appended with "u" to maintain MISRA C:2012 * compliance. - * + * * @sa xQueueIsQueueFull() * @sa xQueueSend() * @sa xQueueCreate() - * + * */ #define CONFIG_QUEUE_MINIMUM_LIMIT 5u #endif \ No newline at end of file diff --git a/src/defines.h b/src/defines.h index d4a7405..98947bd 100644 --- a/src/defines.h +++ b/src/defines.h @@ -1,8 +1,8 @@ /** * @file defines.h * @author Manny Peterson (mannymsp@gmail.com) - * @brief Kernel header file containing all of the macros required to build HeliOS - * @version 0.3.0 + * @brief Kernel header for macros and definitions + * @version 0.3.1 * @date 2022-01-31 * * @copyright @@ -29,208 +29,382 @@ #include #include + + /* Check that the system HeliOS is being targeted for has an 8-bit wide byte. */ -#if defined(CHAR_BIT) +#if !defined(CHAR_BIT) +#pragma message("WARNING: Unable to determine if system has an 8-bit wide byte. CHAR_BIT not defined?") +#else #if CHAR_BIT != 8 #pragma message("WARNING: System may not have an 8-bit wide byte!") #endif -#else -#pragma message("WARNING: Unable to determine if system has an 8-bit wide byte. CHAR_BIT not defined?") #endif + + /* Definition blocks for embedded platform and/or tool-chain specific headers and functions to compile and run HeliOS. When a new embedded platform and/or tool-chain is added, the following -defines must be included: +defines (with the exception of CONFIG_HEAP_SIZE_IN_BLOCKS) must +be included: * CURRENTTIME() * DISABLE_INTERRUPTS() * ENABLE_INTERRUPTS() * TIME_T_TYPE +* CONFIG_HEAP_SIZE_IN_BLOCKS <- Optional. However, if defined + must be enclosed in an #if !defined(CONFIG_HEAP_SIZE_IN_BLOCKS) + block. If no definition block for the embedded platform and/or tool-chain is matched, the "else" block will print a compiler warning and attempt to default to the Arduino platform and/or tool-chain. */ #if defined(ARDUINO_ARCH_AVR) + #include + #define CURRENTTIME() micros() + #define DISABLE_INTERRUPTS() noInterrupts() + #define ENABLE_INTERRUPTS() interrupts() + #define TIME_T_TYPE uint32_t + +#if !defined(CONFIG_HEAP_SIZE_IN_BLOCKS) +#define CONFIG_HEAP_SIZE_IN_BLOCKS 0x20u /* 32u */ +#endif + #elif defined(ARDUINO_ARCH_SAM) + #include + #define CURRENTTIME() micros() + #define DISABLE_INTERRUPTS() noInterrupts() + #define ENABLE_INTERRUPTS() interrupts() + #define TIME_T_TYPE uint32_t + +#if !defined(CONFIG_HEAP_SIZE_IN_BLOCKS) +#define CONFIG_HEAP_SIZE_IN_BLOCKS 0x80u /* 128u */ +#endif + #elif defined(ARDUINO_ARCH_SAMD) + #include + #define CURRENTTIME() micros() + #define DISABLE_INTERRUPTS() noInterrupts() + #define ENABLE_INTERRUPTS() interrupts() + #define TIME_T_TYPE uint32_t + +#if !defined(CONFIG_HEAP_SIZE_IN_BLOCKS) +#define CONFIG_HEAP_SIZE_IN_BLOCKS 0x80u /* 128u */ +#endif + #elif defined(ARDUINO_ARCH_ESP8266) + #include + #define CURRENTTIME() micros() + #define DISABLE_INTERRUPTS() noInterrupts() + #define ENABLE_INTERRUPTS() interrupts() + #define TIME_T_TYPE uint32_t + +#if !defined(CONFIG_HEAP_SIZE_IN_BLOCKS) +#define CONFIG_HEAP_SIZE_IN_BLOCKS 0x80u /* 128u */ +#endif + #elif defined(OTHER_ARCH_LINUX) + +#include #include + #define CURRENTTIME() CurrentTime() + #define DISABLE_INTERRUPTS() + #define ENABLE_INTERRUPTS() + #define TIME_T_TYPE uint64_t + +#if !defined(CONFIG_HEAP_SIZE_IN_BLOCKS) +#define CONFIG_HEAP_SIZE_IN_BLOCKS 0x200u /* 512u */ +#endif + +/* Enable memdump_() on Linux for debugging. */ +#if !defined(MEMDUMP_) +#define MEMDUMP_ +#endif + #elif defined(ARDUINO_TEENSY_MICROMOD) || defined(ARDUINO_TEENSY40) || defined(ARDUINO_TEENSY41) || defined(ARDUINO_TEENSY36) || defined(ARDUINO_TEENSY35) || defined(ARDUINO_TEENSY31) || defined(ARDUINO_TEENSY32) || defined(ARDUINO_TEENSY30) || defined(ARDUINO_TEENSYLC) + #include + #define CURRENTTIME() micros() + #define DISABLE_INTERRUPTS() noInterrupts() + #define ENABLE_INTERRUPTS() interrupts() + #define TIME_T_TYPE uint32_t + +#if !defined(CONFIG_HEAP_SIZE_IN_BLOCKS) +#define CONFIG_HEAP_SIZE_IN_BLOCKS 0x200u /* 512u */ +#endif + #elif defined(ESP32) + #include + #define CURRENTTIME() micros() + #define DISABLE_INTERRUPTS() noInterrupts() + #define ENABLE_INTERRUPTS() interrupts() + #define TIME_T_TYPE uint32_t + +#if !defined(CONFIG_HEAP_SIZE_IN_BLOCKS) +#define CONFIG_HEAP_SIZE_IN_BLOCKS 0x200u /* 512u */ +#endif + #else -#pragma message("WARNING: This embedded platform and/or tool-chain is not currently supported by HeliOS - proceed with caution.") + +#pragma message("WARNING: This embedded platform and/or tool-chain is may not be supported.") + #include + #define CURRENTTIME() micros() + #define DISABLE_INTERRUPTS() noInterrupts() + #define ENABLE_INTERRUPTS() interrupts() + #define TIME_T_TYPE uint32_t + +#if !defined(CONFIG_HEAP_SIZE_IN_BLOCKS) +#define CONFIG_HEAP_SIZE_IN_BLOCKS 0x20u /* 32u */ +#endif #endif + + + /* Define "true" if not defined. */ #if !defined(true) #define true 0x1u #endif + + + /* Define "false" if not defined. */ #if !defined(false) #define false 0x0u #endif + + + /* Define "NULL" if not defined. */ #if !defined(NULL) -#ifdef __cplusplus -#define NULL 0 +#if !defined(__cplusplus) +#define NULL ((void *)0x0) #else -#define NULL ((void *)0) +#define NULL 0x0 #endif #endif + + + /* Define "zero" if not defined. */ #if !defined(zero) #define zero 0x0u #endif + + + +/* Define a general return failure for +return values. */ +#if !defined(RETURN_FAILURE) +#define RETURN_FAILURE 0x0u +#endif + + + + +/* Define a general return success +for return values. */ +#if !defined(RETURN_SUCCESS) +#define RETURN_SUCCESS 0x1u +#endif + + + + /* Define the raw size of the heap in bytes based on the number of blocks the heap contains and the size of each block in bytes. */ #if !defined(HEAP_RAW_SIZE) #define HEAP_RAW_SIZE CONFIG_HEAP_SIZE_IN_BLOCKS *CONFIG_HEAP_BLOCK_SIZE #endif -/* Define the size in bytes of the product name which is accessible through + + + +/* Define the size in bytes of the OS product name which is accessible through xSystemGetSystemInfo(). */ -#if !defined(PRODUCTNAME_SIZE) -#define PRODUCTNAME_SIZE 0x6u +#if !defined(OS_PRODUCT_NAME_SIZE) +#define OS_PRODUCT_NAME_SIZE 0x6u #endif -/* Define the product name which is accessible through xSystemGetSystemInfo(). */ -#if !defined(PRODUCT_NAME) -#define PRODUCT_NAME "HeliOS" + + + +/* Define the OS product name which is accessible through xSystemGetSystemInfo(). */ +#if !defined(OS_PRODUCT_NAME) +#define OS_PRODUCT_NAME "HeliOS" #endif -/* Define the product major version number which is accessible through + + + +/* Define the OS product major version number which is accessible through xSystemGetSystemInfo(). */ -#if !defined(MAJOR_VERSION_NO) -#define MAJOR_VERSION_NO 0x0u +#if !defined(OS_MAJOR_VERSION_NO) +#define OS_MAJOR_VERSION_NO 0x0u #endif -/* Define the product minor version number which is accessible through + + + +/* Define the OS product minor version number which is accessible through xSystemGetSystemInfo(). */ -#if !defined(MINOR_VERSION_NO) -#define MINOR_VERSION_NO 0x3u +#if !defined(OS_MINOR_VERSION_NO) +#define OS_MINOR_VERSION_NO 0x3u #endif -/* Define the product patch version number which is accessible through + + + +/* Define the OS product patch version number which is accessible through xSystemGetSystemInfo(). */ -#if !defined(PATCH_VERSION_NO) -#define PATCH_VERSION_NO 0x0u +#if !defined(OS_PATCH_VERSION_NO) +#define OS_PATCH_VERSION_NO 0x1u #endif -/* Define the macro which sets the critical flag to true when -entering a critical section of code. */ -#if !defined(ENTER_CRITICAL) -#define ENTER_CRITICAL() sysFlags.critical = true -#endif -/* Define the macro which sets the critical flag to false when -exiting a critical section of code. */ -#if !defined(EXIT_CRITICAL) -#define EXIT_CRITICAL() sysFlags.critical = false -#endif -/* Define a macro which makes using the system flags more -readable. The critical system flag is used by xTaskCreate() -and xTaskDelete() to determine when within the scope of the -scheduler. */ -#if !defined(SYSFLAG_CRITICAL) -#define SYSFLAG_CRITICAL() sysFlags.critical -#endif -/* Define the macro which sets the protect flag to true when +/* Define the macro which sets the privileged flag to true when the next call to xMemAlloc() and xMemFree() is for protected memory. */ -#if !defined(ENTER_PROTECT) -#define ENTER_PROTECT() sysFlags.protect = true +#if !defined(ENTER_PRIVILEGED) +#define ENTER_PRIVILEGED() sysFlags.privileged = true #endif -/* Define the macro which sets the protect flag to false to + + + +/* Define the macro which sets the privileged flag to false to exit protected memory mode. NOTE: This should only be called from within xMemAlloc() and xMemFree() since both system calls will automatically exit -protected mode after they are done. */ -#if !defined(EXIT_PROTECT) -#define EXIT_PROTECT() sysFlags.protect = false +privileged mode before they return. */ +#if !defined(EXIT_PRIVILEGED) +#define EXIT_PRIVILEGED() sysFlags.privileged = false #endif -/* Define a macro which makes using the system flags more -readable. The protect system flag is used by xMemAlloc() -and xMemFree() to determine when to set an entry in the heap -to protected. */ -#if !defined(SYSFLAG_PROTECT) -#define SYSFLAG_PROTECT() sysFlags.protect + + + +/* Define a macro to access the privileged system flag which is +used by xMemAlloc() and xMemFree() to determine when to set an +entry in the heap to protected. */ +#if !defined(SYSFLAG_PRIVILEGED) +#define SYSFLAG_PRIVILEGED() sysFlags.privileged #endif -/* Define a macro which makes using the system flags more -readable. The running system flag is used by xTaskSuspendAll() -and xTaskResumeAll() to control the scheduler. */ + + + +/* Define a macro to access the running system flag which is used +by xTaskStartScheduler() to indicate whether the scheduler is +running. */ #if !defined(SYSFLAG_RUNNING) #define SYSFLAG_RUNNING() sysFlags.running #endif -/* Define a macro which makes using the system flags more -readable. The overflow flag is used by the scheduler to determine -when a task runtime has overflowed and all runtimes need to be -reset. */ + + + +/* Define a macro to access the overflow system flag which is used +by the scheduler to determine when a task's runtime has overflowed +and all runtimes need to be reset. */ #if !defined(SYSFLAG_OVERFLOW) #define SYSFLAG_OVERFLOW() sysFlags.overflow #endif + + + /* Define a marco which makes null pointer checks more readable and concise */ #if !defined(ISNOTNULLPTR) -#define ISNOTNULLPTR(p) ((NULL) != (p)) +#define ISNOTNULLPTR(p) ((NULL) != ( p )) #endif + + + /* Define a marco which makes null pointer checks more readable and concise */ #if !defined(ISNULLPTR) -#define ISNULLPTR(p) ((NULL) == (p)) +#define ISNULLPTR(p) ((NULL) == ( p )) +#endif + + + + +/* Define a macro to assert if assertions are enabled through +the CONFIG_ENABLE_SYSTEM_ASSERT setting. */ +#if !defined(SYSASSERT) +#if !defined(CONFIG_ENABLE_SYSTEM_ASSERT) +#define SYSASSERT(x) +#else +#define SYSASSERT(x) \ + if (false == ( x )) \ + SystemAssert(__FILE__, __LINE__) +#endif +#endif + + + + +/* Define a macro for the CheckHeapHealth() option to only check + health of the heap and NOT at the same time check a pointer. */ +#if !defined(HEAP_CHECK_HEALTH_ONLY) +#define HEAP_CHECK_HEALTH_ONLY 0x1u +#endif + + + + +/* Define a macro for the CheckHeapHealth() option to check the +health of the heap AND at the same time check a pointer.. */ +#if !defined(HEAP_CHECK_HEALTH_AND_POINTER) +#define HEAP_CHECK_HEALTH_AND_POINTER 0x2u #endif #endif \ No newline at end of file diff --git a/src/mem.c b/src/mem.c index 6ede193..bc0bfd2 100644 --- a/src/mem.c +++ b/src/mem.c @@ -1,8 +1,8 @@ /** * @file mem.c * @author Manny Peterson (mannymsp@gmail.com) - * @brief Kernel sources for the management of heap memory in HeliOS - * @version 0.3.0 + * @brief Kernel sources for memory management + * @version 0.3.1 * @date 2022-01-31 * * @copyright @@ -26,8 +26,10 @@ #include "mem.h" + + + extern SysFlags_t sysFlags; -extern TaskList_t *taskList; static Byte_t heap[HEAP_RAW_SIZE]; @@ -35,48 +37,67 @@ static HeapEntry_t *start = (HeapEntry_t *)heap; static Word_t entryBlocksNeeded = zero; + + + /* The xMemAlloc() system call will allocate heap memory and return a pointer to the newly allocated memory. */ void *xMemAlloc(size_t size_) { + + /* Disable interrupts because we can't be interrupted while modifying the heap. */ DISABLE_INTERRUPTS(); void *ret = NULL; - Word_t blockCount = zero; - Word_t requestedBlocks = zero; /* Requested blocks with overhead is the requested blocks + the number of blocks required for the heap entry. */ Word_t requestedBlocksWithOverhead = zero; + /* To get the maximum value of Word_t, we underflow the unsigned type. */ Word_t leastBlocks = -1; + HeapEntry_t *entryCursor = NULL; + HeapEntry_t *entryCandidate = NULL; - /* Confirm the requested size in bytes is greater than zero. If not, just head toward - return and return null. */ - if (size_ > zero) { + + /* Assert if the end-user tries to allocate zero bytes of heap memory. */ + SYSASSERT(zero < size_); + + + /* We can't allocate zero bytes of heap memory so just head toward the + return statement. */ + if (zero < size_) { + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * PHASE I: Determine how many blocks a heap entry requires. One block is generally sufficient but we shouldn't assume. This only needs to be done once. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - /* If we haven't calculated how many blocks a heap entry requires, calculate - it now. */ - if (entryBlocksNeeded == zero) { - /* Calculate the quotient portion of the blocks needed for the heap entry. */ + /* Figure out how many blocks are needed to store a heap entry by performing some + division. */ + if (zero == entryBlocksNeeded) { + + + /* Calculate the quotient portion of the blocks needed to store a heap entry. */ entryBlocksNeeded = (Word_t)sizeof(HeapEntry_t) / CONFIG_HEAP_BLOCK_SIZE; - /* Calculate the remainder portion of the blocks needed for the heap entry. If there is - a remainder, add one more block to the blocks needed. */ - if ((sizeof(HeapEntry_t) % CONFIG_HEAP_BLOCK_SIZE) > zero) { - /* Add one to the blocks needed since there is a remainder for the blocks - needed. */ + + + /* Calculate the remainder portion of the blocks needed to store a heap entry. + If there is a remainder, then add one block to cover it. */ + if (zero < (sizeof(HeapEntry_t) % CONFIG_HEAP_BLOCK_SIZE)) { + + + /* Add just one more block to cover the remainder. */ entryBlocksNeeded++; } } @@ -86,266 +107,268 @@ void *xMemAlloc(size_t size_) { initializes the heap. This also only needs to be done once. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - /* If the heap entry at the start of the heap has zero blocks then it hasn't - been initialized yet, so do that now. If it has then just move on.*/ - if (start->blocks == zero) { - /* Zero out the entire heap. HEAP_RAW_SIZE equates to HEAP_SIZE_IN_BLOCKS * HEAP_BLOCK_SIZE. */ + /* If the first heap entry contains zero blocks we know the heap has not been + initialized, so do that now. */ + if (zero == start->blocks) { + + + + /* Zero out the entire heap. */ memset_(heap, zero, HEAP_RAW_SIZE); - /* Set the heap entry to free because, it is free. */ + + /* The first heap entry is free at this point so mark it as such. */ start->free = true; - /* Mark the entry unprotected by setting protected to false. An entry is protected if the macro ENTER_PROTECT() - is called before invoking xMemAlloc(). A protected entry cannot be freed by xMemFree() unless ENTER_PROTECT() - is called beforehand calling xMemFree(). - - NOTE: Protected heap memory is ONLY for system calls, not for use by the end-user.*/ + + /* The first heap entry is UN-protected at this point so mark it as such. For an entry to be marked protected, + the system must be in privileged mode by calling ENTER_PRIVILEGED(). Only heap memory allocated to the kernel + can be protected. Heap memory allocated by the end-user cannot be protected. */ start->protected = false; - /* Set the number of blocks in the first entry to the total number of blocks - in the heap heap minus one block which is occupied by the first heap entry. */ + + + /* The first entry will contain all of the blocks in the heap at this point LESS + the blocks required by the first heap entry. */ start->blocks = CONFIG_HEAP_SIZE_IN_BLOCKS - entryBlocksNeeded; - /* There is only one heap entry at this point so set the next to null. */ + + /* There is only one heap entry at this point so set the pointer to the next + entry to null. */ start->next = NULL; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - PHASE III: Check the health of the heap by scanning through all of the heap entries - counting how many blocks are in each entry then comparing that against the - HEAP_SIZE_IN_BLOCKS setting. If the two do not match there is a problem!! + PHASE III: Check the health of the heap by calling HeapCheck(). * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - /* To scan the heap, set the heap entry cursor to the start of the heap. */ - entryCursor = start; - /* While the heap entry cursor is not null, keep scanning. */ - while (ISNOTNULLPTR(entryCursor)) { - /* Continue to sum the blocks while keeping in mind that the heap entries - consume blocks too. */ - blockCount += entryCursor->blocks + entryBlocksNeeded; + /* Assert if the heap is NOT healthy (i.e., contains consistency errors). */ + SYSASSERT(RETURN_SUCCESS == HeapCheck(HEAP_CHECK_HEALTH_ONLY, NULL)); + + + /* If the heap is healthy, then proceed to phase IV. */ + if (RETURN_SUCCESS == HeapCheck(HEAP_CHECK_HEALTH_ONLY, NULL)) { - /* Move on to the next heap entry. */ - entryCursor = entryCursor->next; - } - /* If the block count does not match HEAP_SIZE_IN_BLOCKS then we need - to return because the heap is corrupt. Otherwise continue to phase IV. */ - if (blockCount == CONFIG_HEAP_SIZE_IN_BLOCKS) { /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * PHASE IV: Calculate how many blocks are needed for the requested size in bytes. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - /* Calculate the quotient portion of the requested blocks by dividing the requested size - paramater by the heap block size also in bytes. */ + + /* Calculate the quotient portion of the requested blocks by performing some division. */ requestedBlocks = (Word_t)size_ / CONFIG_HEAP_BLOCK_SIZE; - /* Calculate the remainder portion of the requested blocks. If there is a remainder we - need to add one more block. */ - if ((size_ % CONFIG_HEAP_SIZE_IN_BLOCKS) > zero) { + /* Calculate the remainder portion of the requested blocks by performing some division. If + there is a remainder then add just one more block. */ + if (zero < (size_ % CONFIG_HEAP_SIZE_IN_BLOCKS)) { + + /* There was a remainder for the requested blocks so add one more block. */ requestedBlocks++; } + /* Because the requested blocks also requires an additional heap entry (if not the first), calculate how many blocks are needed inclusive of the heap entry (i.e., the overhead). */ requestedBlocksWithOverhead = requestedBlocks + entryBlocksNeeded; + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * PHASE V: Scan the heap entries to find a heap entry that would be a good candidate for the requested blocks. This may be the last entry in the heap OR an entry that was recently freed by xMemFree(). * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - /* To scan the heap, need to set the heap entry cursor to the start of the heap. */ + /* Start off at the start of the heap. */ entryCursor = start; - /* While the heap entry cursor is not null, keep scanning. */ + /* While there is a heap entry, continue to traverse the heap. */ while (ISNOTNULLPTR(entryCursor)) { + + /* See if there is a candidate heap entry for the requested blocks by checking: 1) The entry at the cursor is free. - 2) The entry has enough blocks to cover the requested blocks with overhead. - 3) The entry has the fewest possible number of blocks based on our need.*/ - if ((entryCursor->free == true) && (entryCursor->blocks >= requestedBlocksWithOverhead) && (entryCursor->blocks < leastBlocks)) { - /* Seems like a good candidate so update the least blocks in case - there is an entry with fewer blocks that is free yet will fit - the requested blocks with overhead. */ + 2) The entry has enough blocks to cover the requested blocks. + 3) The entry has the fewest possible number of blocks based on our need (i.e., we don't + want to use 12 free blocks if we just need 3 and there is 4 available somewhere else + in the heap). */ + if ((true == entryCursor->free) && (requestedBlocks <= entryCursor->blocks) && (leastBlocks > entryCursor->blocks)) { + + + /* Seems like a good candidate so update the least blocks in case there is + an even BETTER candidate. */ leastBlocks = entryCursor->blocks; + /* Keep a copy of the entry cursor as the best entry candidate in case we find out that the candidate is the winner. */ entryCandidate = entryCursor; } - /* Move on to the next entry. */ + /* Keep on move'n on. */ entryCursor = entryCursor->next; } - /* If the entry candidate is null, well.... we can't fulfill the request so - move on and return null. */ + + /* If we found a candidate, then let's claim in for France. Otherwise just head + toward the exit. */ if (ISNOTNULLPTR(entryCandidate)) { + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * PHASE VI: Found a good candidate so either reuse a free entry OR split the last - entry in the heap. We will also clear the memory at this time. + entry in the heap. Oh, and we need to clear the memory at the same time. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - /* If the entry candidate's next is null, then this is the last entry in the heap - so split the entry blocks in two. */ + + /* Check if the candidate entry is the last entry in the heap, if it is we will + need to split the entry into two. */ if (ISNULLPTR(entryCandidate->next)) { - /* Set the entry candidate "next" to the new entry that will contain the remaining - unused blocks. */ + + + /* Let's update our candidate entry to point to the next entry which will contain + the remain blocks after we perform the split. */ entryCandidate->next = (HeapEntry_t *)((Byte_t *)entryCandidate + (requestedBlocksWithOverhead * CONFIG_HEAP_BLOCK_SIZE)); - /* Mark the new entry as free. */ + /* Our next entry is free so mark it as such. */ entryCandidate->next->free = true; - /* Mark the new entry as unprotected. */ + /* Our next entry is also UN-protected so mark it as such. */ entryCandidate->next->protected = false; - /* Calculate how many remaining blocks there are and update the new entry. */ + /* Perform the split by calculating how many blocks the next entry will contain + after we take what we need. */ entryCandidate->next->blocks = entryCandidate->blocks - requestedBlocksWithOverhead; - /* Set the new entry's "next" to null since it is now the last entry in the heap. */ + /* Our next entry doesn't have a entry after it so set its "next" to null. */ entryCandidate->next->next = NULL; - /* Mark the candidate entry as no longer free. */ + /* Since we will be using the candidate entry, mark it as no longer free. */ entryCandidate->free = false; - /* Set the entry protection based on the protect system flag. */ - entryCandidate->protected = SYSFLAG_PROTECT(); + /* If we are in privileged mode, then mark the candidate entry as protected. + Otherwise, mark it as UN-protected. */ + if (true == SYSFLAG_PRIVILEGED()) { - /* Store how many blocks the entry contains. */ + entryCandidate->protected = true; + + } else { + + entryCandidate->protected = false; + } + + /* Update the candidate entry with how many blocks it contains - this is just the + number of blocks requested by the end-user. */ entryCandidate->blocks = requestedBlocks; - /* Clear the memory by mem-setting it to all zeros. */ + /* Clear the memory. */ memset_((void *)((Byte_t *)entryCandidate + (entryBlocksNeeded * CONFIG_HEAP_BLOCK_SIZE)), zero, requestedBlocks * CONFIG_HEAP_BLOCK_SIZE); - /* Return the address of the memory but make sure we move it forward - enough so the end-user doesn't write to the heap entry. */ + + /* Since the heap entry sits in the block prior to the blocks allocated for the und user, + we want to return a pointer to the start of the allocated space and NOT the heap entry + itself. */ ret = (void *)((Byte_t *)entryCandidate + (entryBlocksNeeded * CONFIG_HEAP_BLOCK_SIZE)); } else { - /* Looks like we found a candidate that is NOT the last entry in the heap, - so simply claim it for France. */ + + + /* Looks like the candidate entry is not at the end so we just need to mark it + as taken. */ entryCandidate->free = false; - /* Set the entry protection based on the protect system flag. */ - entryCandidate->protected = SYSFLAG_PROTECT(); - /* Clear the memory by mem-setting it to all zeros. */ + + /* If we are in privileged mode, then mark the candidate entry as protected. + Otherwise, mark it as UN-protected. */ + if (true == SYSFLAG_PRIVILEGED()) { + + entryCandidate->protected = true; + + } else { + + entryCandidate->protected = false; + } + + + /* Clear the memory. */ memset_((void *)((Byte_t *)entryCandidate + (entryBlocksNeeded * CONFIG_HEAP_BLOCK_SIZE)), zero, requestedBlocks * CONFIG_HEAP_BLOCK_SIZE); - /* Return the address of the memory but make sure we move it forward - enough so the end-user doesn't write to the heap entry. */ + + + /* Since the heap entry sits in the block prior to the blocks allocated for the und user, + we want to return a pointer to the start of the allocated space and NOT the heap entry + itself. */ ret = (void *)((Byte_t *)entryCandidate + (entryBlocksNeeded * CONFIG_HEAP_BLOCK_SIZE)); } } } } - /* Exit protect and enable interrupts before returning. */ + /* Exit privileged mode and enable interrupts before returning. */ - EXIT_PROTECT(); + EXIT_PRIVILEGED(); ENABLE_INTERRUPTS(); return ret; } + + /* The xMemFree() system call will free heap memory pointed to by the pointer parameter. */ void xMemFree(void *ptr_) { + + /* Disable interrupts because we can't be interrupted while modifying the heap. */ DISABLE_INTERRUPTS(); - Word_t blockCount = zero; - - HeapEntry_t *entryCursor = NULL; HeapEntry_t *entryToFree = NULL; - /* Check to make sure the end-user passed a pointer that is at least not null. If it is null, - then move on and return. */ - if (ISNOTNULLPTR(ptr_)) { - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - PHASE I: Determine if the first heap entry has been created. If it hasn't then - just return. - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - /* Check if the entry at the start of the heap is un-initialized by looking - at the blocks member. If it is zero, then the heap has not been initialized so - just thrown in the towel. */ - if (start->blocks != zero) { - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - PHASE II: Check the health of the heap by scanning through all of the heap entries - counting how many blocks are in each entry then comparing that against the - HEAP_SIZE_IN_BLOCKS setting. If the two do not match there is a problem!! - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - /* To scan the heap, set the heap entry cursor to the start of the heap. */ - entryCursor = start; + /* Assert if the heap doesn't pass its health check OR if the pointer the end-user + passed to us isn't a good one. */ + SYSASSERT(RETURN_SUCCESS == HeapCheck(HEAP_CHECK_HEALTH_AND_POINTER, ptr_)); - /* While the heap entry cursor is not null, keep scanning. */ - while (ISNOTNULLPTR(entryCursor)) { - blockCount += entryCursor->blocks + entryBlocksNeeded; /* Assuming entry blocks needed has been - calculated if the heap has been initialized. */ - /* Move on to the next heap entry. */ - entryCursor = entryCursor->next; - } - /* Check if the counted blocks matches the HEAP_SIZE_IN_BLOCKS setting, - if it doesn't return (i.e., Houston, we've had a problem.) */ - if (blockCount == CONFIG_HEAP_SIZE_IN_BLOCKS) { - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - PHASE III: Check if the pointer paramater actually points to a heap entry that - by scanning the heap for it. If it exists, free the entry. + /* Check if the heap is healthy and the pointer the end-user passed to us + is a good one. If everything checks out, proceed with freeing the memory. Otherwise, + head toward the exit. */ + if (RETURN_SUCCESS == HeapCheck(HEAP_CHECK_HEALTH_AND_POINTER, ptr_)) { - Don't ever just directly check that the pointer references what APPEARS to be a - heap entry - always traverse the heap!! - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - /* Determine the heap entry to free by moving back from the pointer by the byte size of one - heap entry. */ - entryToFree = (HeapEntry_t *)((Byte_t *)ptr_ - (entryBlocksNeeded * CONFIG_HEAP_BLOCK_SIZE)); - /* To scan the heap, set the heap entry cursor to the start of the heap. */ - entryCursor = start; + /* End-user gave us a pointer to the start of their allocated space in the heap, we + need to move back one block to get to the heap entry. */ + entryToFree = (HeapEntry_t *)((Byte_t *)ptr_ - (entryBlocksNeeded * CONFIG_HEAP_BLOCK_SIZE)); - /* While the heap entry cursor is not null, keep scanning. */ - while (ISNOTNULLPTR(entryCursor)) { - /* If the entry cursor equals the entry we want to free, then break out of the loop. */ - if (entryCursor == entryToFree) { - break; - } - /* Move on to the next heap entry. */ - entryCursor = entryCursor->next; - } + /* Assert if the heap entry is protected and we are not in privileged mode. */ + SYSASSERT((false == entryToFree->protected) || ((true == entryToFree->protected) && (true == SYSFLAG_PRIVILEGED()))); - /* Check if the entry cursor is null, if it is we couldn't find the entry to be freed. If it is - not null then proceed with marking the entry free. */ - if (ISNOTNULLPTR(entryCursor)) { - /* Check one last time if the entry cursor equals the entry we want to free, if it does, - mark it free. */ - if (entryCursor == entryToFree) { - /* If the entry is marked protected and the protect system flag is false, - then return because a protected entry cannot be freed while the protect - system flag is false. */ - if ((entryCursor->protected == false) || ((entryCursor->protected == true) && (SYSFLAG_PROTECT() == true))) { - /* Make the entry free by setting free to true. */ - entryCursor->free = true; - - /* Mark the entry as unprotected. */ - entryCursor->protected = false; - } - } - } - } + /* Check if we are in privileged mode if the heap entry is protected. If it is not protected, that + is fine too. */ + if ((false == entryToFree->protected) || ((true == entryToFree->protected) && (true == SYSFLAG_PRIVILEGED()))) { + + + /* Mark the entry as free. */ + entryToFree->free = true; + + /* Mark the entry as UN-protected. */ + entryToFree->protected = false; + + /* Never change the entry's blocks!! */ } } + /* Exit protect and enable interrupts before returning. */ - EXIT_PROTECT(); + EXIT_PRIVILEGED(); ENABLE_INTERRUPTS(); @@ -355,169 +378,284 @@ void xMemFree(void *ptr_) { /* The xMemGetUsed() system call returns the amount of memory in bytes that is currently allocated. */ size_t xMemGetUsed(void) { - size_t ret = zero; - Word_t blockCount = zero; - Word_t usedBlockCount = zero; + size_t ret = zero; HeapEntry_t *entryCursor = NULL; - /* Check if the entry at the start of the heap is un-initialized by looking - at the number of blocks it contains. If it is zero, then the heap has not been initialized so - just thrown in the towel. */ - if (start->blocks != zero) { - /* To scan the heap, set the heap entry cursor to the start of the heap. */ + Word_t usedBlocks = zero; + + + /* Assert if the heap does not pass its health check. */ + SYSASSERT(RETURN_SUCCESS == HeapCheck(HEAP_CHECK_HEALTH_ONLY, NULL)); + + + /* If the heap is healthy, we can proceed with calculating heap + memory in use. Otherwise, just head toward the exit. */ + if (RETURN_SUCCESS == HeapCheck(HEAP_CHECK_HEALTH_ONLY, NULL)) { + entryCursor = start; - /* While the heap entry cursor is not null, keep scanning. */ + /* While we have a heap entry to read, keep traversing the heap. */ while (ISNOTNULLPTR(entryCursor)) { - blockCount += entryCursor->blocks + entryBlocksNeeded; /* Assuming entry blocks needed has been - calculated if the heap has been initialized. */ - /* At each entry, check to see if it is in use. If it is, add the number - of blocks it contains plus the number of blocks consumed by the heap entry - block to the used block count. */ + /* If the heap entry we come across is free then let's add its blocks + to the used blocks. */ if (entryCursor->free == false) { + /* Sum the number of used blocks for each heap entry in use. */ - usedBlockCount += entryCursor->blocks + entryBlocksNeeded; + usedBlocks += entryCursor->blocks + entryBlocksNeeded; } /* Move on to the next heap entry. */ entryCursor = entryCursor->next; } - /* Check if the counted blocks matches the HEAP_SIZE_IN_BLOCKS setting, - if it doesn't return. */ - if (blockCount == CONFIG_HEAP_SIZE_IN_BLOCKS) { - ret = (size_t)usedBlockCount * CONFIG_HEAP_BLOCK_SIZE; - } + /* End-user is expecting bytes, so calculate it based on the + block size. */ + ret = usedBlocks * CONFIG_HEAP_BLOCK_SIZE; } + return ret; } + + + /* The xMemGetSize() system call returns the amount of memory in bytes that is currently allocated to a specific pointer. */ size_t xMemGetSize(void *ptr_) { + + size_t ret = zero; - Word_t blockCount = zero; + + HeapEntry_t *entryToSize = NULL; + + + + + /* Assert if the heap failed its health check OR if the end-user scammed + us on the pointer. */ + SYSASSERT(RETURN_SUCCESS == HeapCheck(HEAP_CHECK_HEALTH_AND_POINTER, ptr_)); + + + + /* If the heap passes its health check and the pointer the end-user passed + us is valid, then continue. */ + if (RETURN_SUCCESS == HeapCheck(HEAP_CHECK_HEALTH_AND_POINTER, ptr_)) { + + + + /* The end-user's pointer points to the start of their allocated space, we + need to move back one block to read the entry. */ + entryToSize = (HeapEntry_t *)((Byte_t *)ptr_ - (entryBlocksNeeded * CONFIG_HEAP_BLOCK_SIZE)); + + + /* The entry should not be free, also check if it is protected because if it is + then we must be in privileged mode. */ + SYSASSERT((false == entryToSize->free) && ((false == entryToSize->protected) || ((false == entryToSize->free) && (true == entryToSize->protected) && (true == SYSFLAG_PRIVILEGED())))); + + + /* The entry should not be free, also check if it is protected because if it is + then we must be in privileged mode. */ + if ((false == entryToSize->free) && ((false == entryToSize->protected) || ((false == entryToSize->free) && (true == entryToSize->protected) && (true == SYSFLAG_PRIVILEGED())))) { + + + + /* The end-user is expecting us to return the number of bytes in used. So + perform some advanced multiplication. */ + ret = entryToSize->blocks * CONFIG_HEAP_BLOCK_SIZE; + } + } + + + EXIT_PRIVILEGED(); + + return ret; +} + + + + +/* The CheckHeapHealth() function checks the health of the heap and optionally +will check that a pointer is valid at the same time. CheckHeapHealth() does +not respect the entry protected flag because it isn't changing anything. */ +Base_t HeapCheck(const Base_t option_, const void *ptr_) { + HeapEntry_t *entryCursor = NULL; - HeapEntry_t *entryToSize = NULL; + HeapEntry_t *entryToCheck = NULL; - /* Check to make sure the end-user passed a pointer that is at least not null. */ - if (ISNOTNULLPTR(ptr_)) { - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - PHASE I: Determine if the first heap entry has been created. If it hasn't, then - just return. - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + Base_t ptrFound = false; + + Word_t blocks = zero; + + Base_t ret = RETURN_FAILURE; + + + /* Assert if there is an invalid combination of arguments + passed to function. */ + SYSASSERT(((HEAP_CHECK_HEALTH_ONLY == option_) && (ISNULLPTR(ptr_))) || ((HEAP_CHECK_HEALTH_AND_POINTER == option_) && (ISNOTNULLPTR(ptr_)))); - /* Check if the entry at the start of the heap is un-initialized by looking - at the blocks number of blocks it contains. If it is zero, then the heap has - not been initialized so just thrown in the towel. */ - if (start->blocks != zero) { - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - PHASE II: Check the health of the heap by scanning through all of the heap entries - counting how many blocks are in each entry then comparing that against the - HEAP_SIZE_IN_BLOCKS setting. If the two do not match there is a problem!! - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - /* To scan the heap, set the heap entry cursor to the start of the heap. */ + /* Check if there is an invalid combination of arguments passed + to function before proceeding with checks. */ + if (((HEAP_CHECK_HEALTH_ONLY == option_) && (ISNULLPTR(ptr_))) || ((HEAP_CHECK_HEALTH_AND_POINTER == option_) && (ISNOTNULLPTR(ptr_)))) { + + + /* Assert if the heap has not been initialized. */ + SYSASSERT(zero != start->blocks); + + + /* Check if the heap has been initialized, if it has then + proceed with the checks. */ + if (zero != start->blocks) { + entryCursor = start; - /* While the heap entry cursor is not null, keep scanning. */ + + /* If we need to also check that the end-user's pointer is valid at the same time, + then we must calculate where its heap entry would be. */ + if (HEAP_CHECK_HEALTH_AND_POINTER == option_) { + + entryToCheck = (HeapEntry_t *)((Byte_t *)ptr_ - (entryBlocksNeeded * CONFIG_HEAP_BLOCK_SIZE)); + } + + /* Traverse the heap and sum the blocks from + each entry. */ while (ISNOTNULLPTR(entryCursor)) { - blockCount += entryCursor->blocks + entryBlocksNeeded; /* Assuming entry blocks needed has been - calculated if the heap has been initialized. */ + + blocks += entryCursor->blocks + entryBlocksNeeded; + + /* At the same time if we are checking for a pointer, let's find it. If + found then set the pointer found variable to true. */ + if ((HEAP_CHECK_HEALTH_AND_POINTER == option_) && (entryCursor == entryToCheck) && (false == entryCursor->free)) { + + ptrFound = true; + } /* Move on to the next heap entry. */ entryCursor = entryCursor->next; } - /* Check if the counted blocks matches the HEAP_SIZE_IN_BLOCKS setting, - if it doesn't return. */ - if (blockCount == CONFIG_HEAP_SIZE_IN_BLOCKS) { - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - PHASE III: Check if the pointer paramater actually points to a heap entry that - by scanning the heap for it. If it exists, free the entry. - Don't ever just directly check that the pointer references what APPEARS to be a - heap entry - always traverse the heap!! - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + /* Assert if the blocks we summed while traversing the heap + do not match what is expected. */ + SYSASSERT(CONFIG_HEAP_SIZE_IN_BLOCKS == blocks); - /* Determine the heap entry to get the size of by moving back from the pointer by the byte size of one - heap entry. */ - entryToSize = (HeapEntry_t *)((Byte_t *)ptr_ - (entryBlocksNeeded * CONFIG_HEAP_BLOCK_SIZE)); + /* Check if the blocks we summed while traversing the heap + matches what we expected, if so proceed. */ + if (CONFIG_HEAP_SIZE_IN_BLOCKS == blocks) { - /* To scan the heap, set the heap entry cursor to the start of the heap. */ - entryCursor = start; - /* While the heap entry cursor is not null, keep scanning. */ - while (ISNOTNULLPTR(entryCursor)) { - /* If the entry cursor equals the entry we want to free, then break out of the loop. */ - if (entryCursor == entryToSize) { - break; - } + /* Assert if the pointer was not found if we + were looking for it. */ + SYSASSERT((HEAP_CHECK_HEALTH_ONLY == option_) || ((HEAP_CHECK_HEALTH_AND_POINTER == option_) && (true == ptrFound))); - /* Move on to the next heap entry. */ - entryCursor = entryCursor->next; - } - /* If the entry cursor is null we didn't find the entry we were looking for so return, - otherwise return the about of bytes consumed by the entry at the pointer. */ - if (ISNOTNULLPTR(entryCursor)) { - /* We want to return the amount of BYTES in use by the pointer so multiply the - blocks consumed by the entry by the HEAP_BLOCK_SIZE. */ - ret = entryCursor->blocks * CONFIG_HEAP_BLOCK_SIZE; + + /* If we only have to check the heap health then set the + return value to success OR if we are also checking that + the pointer was valid then set the return value to success + if the pointer's heap entry was found. */ + if ((HEAP_CHECK_HEALTH_ONLY == option_) || ((HEAP_CHECK_HEALTH_AND_POINTER == option_) && (true == ptrFound))) { + + ret = RETURN_SUCCESS; } } } } + return ret; } + /* A memory utility to copy memory between the source and destination pointers. */ void memcpy_(void *dest_, const void *src_, size_t n_) { + char *src = (char *)src_; + char *dest = (char *)dest_; for (size_t i = zero; i < n_; i++) { + dest[i] = src[i]; } + return; } /* A memory utility to set the memory pointed to by the destination pointer to the specified value. */ void memset_(void *dest_, uint16_t val_, size_t n_) { + char *dest = (char *)dest_; for (size_t i = zero; i < n_; i++) { + dest[i] = (char)val_; } + return; } /* A memory utility to compare the contents of memory at two locations pointed to by the pointers s1 and s2. */ uint16_t memcmp_(const void *s1_, const void *s2_, size_t n_) { + uint16_t ret = zero; char *s1 = (char *)s1_; + char *s2 = (char *)s2_; for (size_t i = zero; i < n_; i++) { + if (*s1 != *s2) { + ret = *s1 - *s2; + break; /* Typically memcmp() just returns here but we can't do that for MISRA C:2012 compliance. */ } + s1++; + s2++; } + + return ret; -} \ No newline at end of file +} + + +/* For debugging the heap only. */ +#if defined(MEMDUMP_) +void memdump_(void) { + + int k = 0; + + for (int i = 0; i < (int)(HEAP_RAW_SIZE / 16); i++) { + printf("%p:", (heap + k)); + + for (int j = 0; j < 16; j++) { + if (*(heap + k) == 0) { + printf(" --"); + } else { + printf(" %02X", *(heap + k)); + } + + k++; + } + + printf("\n"); + } + + return; +} +#endif \ No newline at end of file diff --git a/src/mem.h b/src/mem.h index 1dc6735..4759e29 100644 --- a/src/mem.h +++ b/src/mem.h @@ -1,8 +1,8 @@ /** * @file mem.h * @author Manny Peterson (mannymsp@gmail.com) - * @brief Kernel sources for the management of heap memory in HeliOS - * @version 0.3.0 + * @brief Kernel sources for memory management + * @version 0.3.1 * @date 2022-01-31 * * @copyright @@ -30,7 +30,7 @@ #include "defines.h" #include "types.h" #include "queue.h" -#include "sched.h" +#include "sys.h" #include "task.h" #include "timer.h" @@ -42,10 +42,17 @@ void *xMemAlloc(size_t size_); void xMemFree(void *ptr_); size_t xMemGetUsed(void); size_t xMemGetSize(void *ptr_); +Base_t HeapCheck(const Base_t option_, const void *ptr_); void memcpy_(void *dest_, const void *src_, size_t n_); void memset_(void *dest_, uint16_t val_, size_t n_); uint16_t memcmp_(const void *s1_, const void *s2_, size_t n_); + +/* For debugging the heap only. */ +#if defined(MEMDUMP_) +void memdump_(void); +#endif + #ifdef __cplusplus } // extern "C" { #endif diff --git a/src/queue.c b/src/queue.c index 1b56776..3a1b3d9 100644 --- a/src/queue.c +++ b/src/queue.c @@ -1,8 +1,8 @@ /** * @file queue.c * @author Manny Peterson (mannymsp@gmail.com) - * @brief Kernel sources for interprocess communication queues in HeliOS - * @version 0.3.0 + * @brief Kernel sources for message queues + * @version 0.3.1 * @date 2022-01-31 * * @copyright @@ -26,21 +26,49 @@ #include "queue.h" + + extern SysFlags_t sysFlags; -extern TaskList_t *taskList; + + /* The xQueueCreate() system call creates a message queue for inter-task communication. */ Queue_t *xQueueCreate(Base_t limit_) { + + Queue_t *ret = NULL; - /* Check to make sure the limit parameter is greater than or equal to the - setting CONFIG_QUEUE_MINIMUM_LIMIT. */ - if (limit_ >= CONFIG_QUEUE_MINIMUM_LIMIT) { + + /* Assert if the end-user attempted to create a queue with a limit + that is less than the configured minimum limit. */ + SYSASSERT(CONFIG_QUEUE_MINIMUM_LIMIT <= limit_); + + + /* Check if the end-user attempted to create a queue with a limit + that is less than the configured minimum limit, if they did then + just head toward the exit. */ + if (CONFIG_QUEUE_MINIMUM_LIMIT <= limit_) { + + + /* Creating a kernel object so put ourselves in privileged + mode. */ + ENTER_PRIVILEGED(); + + ret = (Queue_t *)xMemAlloc(sizeof(Queue_t)); - /* Check if queue was successfully allocated by xMemAlloc(). */ + + /* Assert if xMemAlloc() didn't return our requested + heap memory. */ + SYSASSERT(ISNOTNULLPTR(ret)); + + + /* Check if xMemAlloc() returned our requested + heap memory. */ if (ISNOTNULLPTR(ret)) { + + ret->length = zero; ret->limit = limit_; @@ -57,104 +85,196 @@ Queue_t *xQueueCreate(Base_t limit_) { /* The xQueueDelete() system call will delete a queue created by xQueueCreate(). xQueueDelete() will delete a queue regardless of how many messages the queue contains at the time xQueueDelete() is called. */ void xQueueDelete(Queue_t *queue_) { - /* Check if the queue parameter is null */ - if (ISNOTNULLPTR(queue_)) { - /* If the queue has a head it contains messages, iterate through the queue and drop - all of the messages. */ + + + /* Assert if the heap fails its health check or if the queue pointer the end-user + passed is invalid. */ + SYSASSERT(RETURN_SUCCESS == HeapCheck(HEAP_CHECK_HEALTH_AND_POINTER, queue_)); + + + + /* Check if the heap is health and the queue pointer the end-user passed is valid. + If so, continue. Otherwise, head toward the exit. */ + if (RETURN_SUCCESS == HeapCheck(HEAP_CHECK_HEALTH_AND_POINTER, queue_)) { + + + /* If the queue contains messages, traverse the queue and drop the + messages as we go. */ while (ISNOTNULLPTR(queue_->head)) { + + + /* Drop the next message in the queue. */ xQueueDropMessage(queue_); } + + /* Freeing a kernel object so enter privileged mode. */ + ENTER_PRIVILEGED(); + + + /* Free the memory for the queue. */ xMemFree(queue_); } + return; } + + + /* The xQueueGetLength() system call returns the length of the queue (the number of messages the queue currently contains). */ Base_t xQueueGetLength(Queue_t *queue_) { + + Base_t ret = zero; Base_t messages = zero; Message_t *messageCursor = NULL; - /* Check if the queue parameter is not null. */ - if (ISNOTNULLPTR(queue_)) { + /* Assert if the heap fails its health check or if the queue pointer the end-user + passed is invalid. */ + SYSASSERT(RETURN_SUCCESS == HeapCheck(HEAP_CHECK_HEALTH_AND_POINTER, queue_)); + + + /* Check if the heap is health and the queue pointer the end-user passed is valid. + If so, continue. Otherwise, head toward the exit. */ + if (RETURN_SUCCESS == HeapCheck(HEAP_CHECK_HEALTH_AND_POINTER, queue_)) { + + messageCursor = queue_->head; - /* If the queue has a head, iterate through the queue and count the number of messages. */ + /* Traverse the queue and count the number of messages it contains. */ while (ISNOTNULLPTR(messageCursor)) { + messages++; messageCursor = messageCursor->next; } - /* Check to make sure the number of messages counted matches the length attribute of the queue. - This is to confirm the integrity of the queue before returning its length. */ - if (queue_->length == messages) { + + /* Assert if the number of messages we counted disagrees with the queue's + length. This could indicate a problem. */ + SYSASSERT(messages == queue_->length); + + + /* Check if the number of messages we counted agrees with the queue's + length. */ + if (messages == queue_->length) { + ret = messages; } } + return ret; } + + + /* The xQueueIsEmpty() system call will return a true or false dependent on whether the queue is empty or contains one or more messages. */ Base_t xQueueIsQueueEmpty(Queue_t *queue_) { + + + Base_t ret = false; Base_t messages = zero; Message_t *messageCursor = NULL; - /* Check if the queue pointer is not null. */ - if (ISNOTNULLPTR(queue_)) { + + /* Assert if the heap fails its health check or if the queue pointer the end-user + passed is invalid. */ + SYSASSERT(RETURN_SUCCESS == HeapCheck(HEAP_CHECK_HEALTH_AND_POINTER, queue_)); + + + /* Check if the heap is health and the queue pointer the end-user passed is valid. + If so, continue. Otherwise, head toward the exit. */ + if (RETURN_SUCCESS == HeapCheck(HEAP_CHECK_HEALTH_AND_POINTER, queue_)) { + + messageCursor = queue_->head; - /* If the queue has a head, iterate through the queue and count the number of messages. */ + /* Traverse the queue and count the number of messages it contains. */ while (ISNOTNULLPTR(messageCursor)) { + + messages++; messageCursor = messageCursor->next; } - /* Check to make sure the number of messages counted matches the length attribute of the queue - and if the number of messages equals zero. */ - if ((messages == zero) && (queue_->length == messages)) { + + + /* Assert if the number of messages we counted disagrees with the queue's + length. This could indicate a problem. */ + SYSASSERT(messages = queue_->length); + + + /* Check if the number of messages we counted agrees with the queue's + length. Also, for the queue to be empty there must be zero messages. */ + if ((zero == messages) && (messages == queue_->length)) { + ret = true; } } + return ret; } + + /* The xQueueIsFull() system call will return a true or false dependent on whether the queue is full or contains zero messages. A queue is considered full if the number of messages in the queue is equal to the queue's length limit. */ Base_t xQueueIsQueueFull(Queue_t *queue_) { + + Base_t ret = false; Base_t messages = zero; Message_t *messageCursor = NULL; - /* Check if the queue parameter is not null. */ - if (ISNOTNULLPTR(queue_)) { + + /* Assert if the heap fails its health check or if the queue pointer the end-user + passed is invalid. */ + SYSASSERT(RETURN_SUCCESS == HeapCheck(HEAP_CHECK_HEALTH_AND_POINTER, queue_)); + + + /* Check if the heap is health and the queue pointer the end-user passed is valid. + If so, continue. Otherwise, head toward the exit. */ + if (RETURN_SUCCESS == HeapCheck(HEAP_CHECK_HEALTH_AND_POINTER, queue_)) { + messageCursor = queue_->head; - /* If the queue has a head, iterate through the queue and count the number of messages. */ + + + /* Traverse the queue and count the number of messages it contains. */ while (ISNOTNULLPTR(messageCursor)) { + messages++; messageCursor = messageCursor->next; } - /* Check to make sure the number of messages counted matches the length attribute of the queue - and if the number of messages is greater than or equal to the queue length limit. */ - if ((messages >= queue_->limit) && (queue_->length == messages)) { + + /* Assert if the number of messages we counted disagrees with the queue's + length. This could indicate a problem. */ + SYSASSERT(messages == queue_->length); + + /* Check if the number of messages we counted agrees with the queue's + length. Also, for the queue to be full the number of messages must be + equal to the queue limit. */ + if ((messages >= queue_->limit) && (messages == queue_->length)) { + + ret = true; } } @@ -162,40 +282,72 @@ Base_t xQueueIsQueueFull(Queue_t *queue_) { return ret; } + + + /* The xQueueMessageWaiting() system call returns true or false dependent on whether there is at least one message waiting. The queue does not have to be full to return true. */ Base_t xQueueMessagesWaiting(Queue_t *queue_) { + + + Base_t ret = false; Base_t messages = zero; Message_t *messageCursor = NULL; - /* Check if the queue parameter is not null. */ - if (ISNOTNULLPTR(queue_)) { + + /* Assert if the heap fails its health check or if the queue pointer the end-user + passed is invalid. */ + SYSASSERT(RETURN_SUCCESS == HeapCheck(HEAP_CHECK_HEALTH_AND_POINTER, queue_)); + + + + /* Check if the heap is health and the queue pointer the end-user passed is valid. + If so, continue. Otherwise, head toward the exit. */ + if (RETURN_SUCCESS == HeapCheck(HEAP_CHECK_HEALTH_AND_POINTER, queue_)) { + messageCursor = queue_->head; - /* If the queue has a head, iterate through the queue and count the number of messages. */ + /* Traverse the queue and count the number of messages it contains. */ while (ISNOTNULLPTR(messageCursor)) { + messages++; messageCursor = messageCursor->next; } - /* Check to make sure the number of messages counted matches the length attribute of the queue - and if the number of messages is greater than zero. */ - if ((messages > zero) && (queue_->length == messages)) { + + /* Assert if the number of messages we counted disagrees with the queue's + length. This could indicate a problem. */ + SYSASSERT(messages == queue_->length); + + + /* Check if the number of messages we counted agrees with the queue's + length. Also, for there to be waiting messages look to make sure the + count is greater than zero. */ + if ((zero < messages) && (messages == queue_->length)) { + + ret = true; } } + return ret; } + + + /* The xQueueSend() system call will send a message to the queue. The size of the message value is passed in the message bytes parameter. */ Base_t xQueueSend(Queue_t *queue_, Base_t messageBytes_, const char *messageValue_) { - Base_t ret = false; + + + + Base_t ret = RETURN_FAILURE; Message_t *message = NULL; @@ -203,47 +355,95 @@ Base_t xQueueSend(Queue_t *queue_, Base_t messageBytes_, const char *messageValu Message_t *messageCursor = NULL; - /* Check if the queue parameter is not null, message bytes is between one and CONFIG_MESSAGE_VALUE_BYTES and the message value parameter - is not null. */ - if ((ISNOTNULLPTR(queue_)) && (messageBytes_ > zero) && (messageBytes_ <= CONFIG_MESSAGE_VALUE_BYTES) && (ISNOTNULLPTR(messageValue_))) { - messageCursor = queue_->head; - /* If the queue has a head, iterate through the queue and count the number of messages. */ - while (ISNOTNULLPTR(messageCursor)) { - messages++; + /* Assert if the end-user passed zero message bytes. A message + must have at least one byte in it. */ + SYSASSERT(zero < messageBytes_); - messageCursor = messageCursor->next; - } - /* Check if the length of the queue is less than the limit and the length of the queue matches the number of messages - counted. */ - if ((queue_->length < queue_->limit) && (queue_->length == messages)) { - message = (Message_t *)xMemAlloc(sizeof(Message_t)); + /* Assert if the end-user passed a number of message bytes that + exceeds the size of the message value. */ + SYSASSERT(CONFIG_MESSAGE_VALUE_BYTES >= messageBytes_); - /* Check if the message was successfully allocated by xMemAlloc(). */ - if (ISNOTNULLPTR(message)) { - message->messageBytes = messageBytes_; - memcpy_(message->messageValue, messageValue_, CONFIG_MESSAGE_VALUE_BYTES); + /* Assert if the end-user passed a null pointer for the message + value. */ + SYSASSERT(ISNOTNULLPTR(messageValue_)); - message->next = NULL; + /* Check if the message bytes is within parameters and the message value is not null. */ + if ((zero < messageBytes_) && (CONFIG_MESSAGE_VALUE_BYTES >= messageBytes_) && (ISNOTNULLPTR(messageValue_))) { - /* If the queue tail is not null then it already contains messages and append the new message, otherwise - set the head and tail to the new message. */ - if (ISNOTNULLPTR(queue_->tail)) { - queue_->tail->next = message; - queue_->tail = message; + /* Assert if the heap fails its health check or if the queue pointer the end-user + passed is invalid. */ + SYSASSERT(RETURN_SUCCESS == HeapCheck(HEAP_CHECK_HEALTH_AND_POINTER, queue_)); - } else { - queue_->head = message; + /* Check if the heap is health and the queue pointer the end-user passed is valid. + If so, continue. Otherwise, head toward the exit. */ + if (RETURN_SUCCESS == HeapCheck(HEAP_CHECK_HEALTH_AND_POINTER, queue_)) { - queue_->tail = message; - } + messageCursor = queue_->head; + + /* If the queue has a head, iterate through the queue and count the number of messages. */ + while (ISNOTNULLPTR(messageCursor)) { + + messages++; + + messageCursor = messageCursor->next; + } + + + /* Assert if the queue is full. */ + SYSASSERT(queue_->limit > queue_->length); + + + /* Assert if the messages counted disagrees with the queue length. If + so there is a problem. */ + SYSASSERT(messages == queue_->length); + + + /* Check if the queue is not full and that the messages counted agrees with the + queue length. */ + if ((queue_->limit > queue_->length) && (messages == queue_->length)) { + + + /* Going to create a kernel object so enter privileged mode. */ + ENTER_PRIVILEGED(); + + message = (Message_t *)xMemAlloc(sizeof(Message_t)); + + + /* Assert if xMemAlloc() did not allocate our requested memory. */ + SYSASSERT(ISNOTNULLPTR(message)); + + /* Check if the message was successfully allocated by xMemAlloc(). */ + if (ISNOTNULLPTR(message)) { + + message->messageBytes = messageBytes_; + + memcpy_(message->messageValue, messageValue_, CONFIG_MESSAGE_VALUE_BYTES); + + message->next = NULL; + + /* If the queue tail is not null then it already contains messages and append the new message, otherwise + set the head and tail to the new message. */ + if (ISNOTNULLPTR(queue_->tail)) { + + queue_->tail->next = message; + + queue_->tail = message; - queue_->length++; + } else { - ret = true; + queue_->head = message; + + queue_->tail = message; + } + + queue_->length++; + + ret = RETURN_SUCCESS; + } } } } @@ -256,14 +456,32 @@ dropping the message. */ QueueMessage_t *xQueuePeek(Queue_t *queue_) { QueueMessage_t *ret = NULL; - /* Check if the queue parameter is not null. */ - if (ISNOTNULLPTR(queue_)) { - /* Check if the head of the queue is not null. */ + + /* Assert if the heap fails its health check or if the queue pointer the end-user + passed is invalid. */ + SYSASSERT(RETURN_SUCCESS == HeapCheck(HEAP_CHECK_HEALTH_AND_POINTER, queue_)); + + + /* Check if the heap is health and the queue pointer the end-user passed is valid. + If so, continue. Otherwise, head toward the exit. */ + if (RETURN_SUCCESS == HeapCheck(HEAP_CHECK_HEALTH_AND_POINTER, queue_)) { + + /* If the head is not null, then there is a message waiting for us to + peek at. */ if (ISNOTNULLPTR(queue_->head)) { + + ret = (QueueMessage_t *)xMemAlloc(sizeof(QueueMessage_t)); - /* Check if a new message was successfully allocated by xMemAlloc(). */ + + /* Assert if xMemAlloc() didn't do its job. */ + SYSASSERT(ISNOTNULLPTR(ret)); + + /* If xMemAlloc() allocated the heap memory then copy the message into the + queue message we will return. Otherwise, head toward the exit. */ if (ISNOTNULLPTR(ret)) { + + ret->messageBytes = queue_->head->messageBytes; memcpy_(ret->messageValue, queue_->head->messageValue, CONFIG_MESSAGE_VALUE_BYTES); @@ -271,50 +489,98 @@ QueueMessage_t *xQueuePeek(Queue_t *queue_) { } } + return ret; } /* The xQueueDropMessage() system call will drop the next message from the queue without returning the message. */ void xQueueDropMessage(Queue_t *queue_) { + + Message_t *message = NULL; - /* Check if the queue parameter is not null. */ - if (ISNOTNULLPTR(queue_)) { - /* Check if the head of the queue is null, if so enable interrupts and - return because there is nothing to drop. */ + + + /* Assert if the heap fails its health check or if the queue pointer the end-user + passed is invalid. */ + SYSASSERT(RETURN_SUCCESS == HeapCheck(HEAP_CHECK_HEALTH_AND_POINTER, queue_)); + + + + /* Check if the heap is health and the queue pointer the end-user passed is valid. + If so, continue. Otherwise, head toward the exit. */ + if (RETURN_SUCCESS == HeapCheck(HEAP_CHECK_HEALTH_AND_POINTER, queue_)) { + + + + /* Check if there is a message in the queue, if there isn't then + we have nothing to drop so head toward the exit. */ if (ISNOTNULLPTR(queue_->head)) { + + message = queue_->head; queue_->head = queue_->head->next; - /* Again check if the head of the queue is null, if so set the tail - of the queue to null. */ + + /* If the head is now null, then let's set the tail to null + too because the queue is now empty. */ if (ISNULLPTR(queue_->head)) { + queue_->tail = NULL; } queue_->length--; + + /* To free the message kernel object we must go into + privileged mode. */ + ENTER_PRIVILEGED(); + xMemFree(message); } } + return; } /* The xQueueReceive() system call will return the next message in the queue and drop it from the queue. */ QueueMessage_t *xQueueReceive(Queue_t *queue_) { + + QueueMessage_t *ret = NULL; - /* Check if the queue parameter is not null. */ - if (ISNOTNULLPTR(queue_)) { + + + /* Assert if the heap fails its health check or if the queue pointer the end-user + passed is invalid. */ + SYSASSERT(RETURN_SUCCESS == HeapCheck(HEAP_CHECK_HEALTH_AND_POINTER, queue_)); + + + + /* Check if the heap is health and the queue pointer the end-user passed is valid. + If so, continue. Otherwise, head toward the exit. */ + if (RETURN_SUCCESS == HeapCheck(HEAP_CHECK_HEALTH_AND_POINTER, queue_)) { + + + /* Re-use some code and peek to see if there is a message + waiting in the queue. + + NOTE: We don't need to allocate any heap memory since xQueuePeek() + has already done that for us. */ ret = xQueuePeek(queue_); - /* Check if the message returned from xQueuePeek() is not null. If so, drop the message from the - queue and return the message. */ + + /* See if xQueuePeek() returned a message, if so we need to drop it + before we return the message. */ if (ISNOTNULLPTR(ret)) { + + + /* Re-use some code and just call xQueueDropMessage() to drop + the message we just received. */ xQueueDropMessage(queue_); } } diff --git a/src/queue.h b/src/queue.h index da8a108..1763393 100644 --- a/src/queue.h +++ b/src/queue.h @@ -1,8 +1,8 @@ /** * @file queue.h * @author Manny Peterson (mannymsp@gmail.com) - * @brief Kernel sources for interprocess communication queues in HeliOS - * @version 0.3.0 + * @brief Kernel sources for message queues + * @version 0.3.1 * @date 2022-01-31 * * @copyright @@ -30,7 +30,7 @@ #include "defines.h" #include "types.h" #include "mem.h" -#include "sched.h" +#include "sys.h" #include "task.h" #include "timer.h" diff --git a/src/sched.c b/src/sched.c deleted file mode 100644 index 77c3ca5..0000000 --- a/src/sched.c +++ /dev/null @@ -1,229 +0,0 @@ -/** - * @file sched.c - * @author Manny Peterson (mannymsp@gmail.com) - * @brief Kernel sources for the HeliOS scheduler - * @version 0.3.0 - * @date 2022-01-31 - * - * @copyright - * HeliOS Embedded Operating System - * Copyright (C) 2020-2022 Manny Peterson - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -#include "sched.h" - -/* "You are not expected to understand this." -Thank you for the best OS on Earth, Dennis. -May you forever rest in peace. */ - -extern TaskList_t *taskList; - -/* Declare and set the system flags to their default values. */ -SysFlags_t sysFlags = { - .running = true, - .critical = false, - .overflow = false, - .protect = false}; - -/* The xTaskStartScheduler() system call passes control to the HeliOS scheduler. */ -void xTaskStartScheduler(void) { - Task_t *runTask = NULL; - - Task_t *taskCursor = NULL; - - /* Underflow unsigned least runtime to get maximum value */ - Time_t leastRunTime = -1; - - /* Disable interrupts and set the critical section flag before entering into the scheduler main - loop. */ - DISABLE_INTERRUPTS(); - - ENTER_CRITICAL(); - - /* Continue to loop while the scheduler running flag is true. */ - while (SYSFLAG_RUNNING()) { - /* If the runtime overflow flag is true. Reset the runtimes on all of the tasks. */ - if (SYSFLAG_OVERFLOW()) { - RunTimeReset(); - } - - /* Check if the task list returned by TaskListGet() is not null before accessing it. */ - if (ISNOTNULLPTR(taskList)) { - taskCursor = taskList->head; - - /* While the task cursor is not null (i.e., there are further tasks in the task list). */ - while (ISNOTNULLPTR(taskCursor)) { - /* If the task pointed to by the task cursor is waiting and it has a notification waiting, then execute it. */ - if ((taskCursor->state == TaskStateWaiting) && (taskCursor->notificationBytes > zero)) { - TaskRun(taskCursor); - - /* If the task pointed to by the task cursor is waiting and its timer has expired, then execute it. */ - } else if ((taskCursor->state == TaskStateWaiting) && (taskCursor->timerPeriod > zero) && ((CURRENTTIME() - taskCursor->timerStartTime) > taskCursor->timerPeriod)) { - TaskRun(taskCursor); - - taskCursor->timerStartTime = CURRENTTIME(); - - /* If the task pointed to by the task cursor is running and it's total runtime is less than the - least runtime from previous tasks, then set the run task pointer to the task cursor. This logic - is used to achieve the runtime balancing. */ - } else if ((taskCursor->state == TaskStateRunning) && (taskCursor->totalRunTime < leastRunTime)) { - leastRunTime = taskCursor->totalRunTime; - - runTask = taskCursor; - } else { - /* Nothing to do here.. Just for MISRA C:2012 compliance. */ - } - - taskCursor = taskCursor->next; - } - - /* If the run task pointer is not null, then there is a running tasks to execute. */ - if (ISNOTNULLPTR(runTask)) { - TaskRun(runTask); - - runTask = NULL; - } - - leastRunTime = -1; - } - } - - /* Enable interrupts and UNset the critical section flag before returning from the scheduler. */ - EXIT_CRITICAL(); - - ENABLE_INTERRUPTS(); -} - -/* If the runtime overflow flag is set, then RunTimeReset() is called to reset all of the -total runtimes on tasks to their last runtime. */ -void RunTimeReset(void) { - Task_t *taskCursor = NULL; - - /* Check if task list is not null before accessing it. */ - if (ISNOTNULLPTR(taskList)) { - taskCursor = taskList->head; - - /* While the task cursor is not null (i.e., there are further tasks in the task list). */ - while (ISNOTNULLPTR(taskCursor)) { - taskCursor->totalRunTime = taskCursor->lastRunTime; - - taskCursor = taskCursor->next; - } - - SYSFLAG_OVERFLOW() = false; - } - return; -} - -/* Used only for when testing HeliOS on Linux, then get the time from clock_gettime(). */ -Time_t CurrentTime(void) { -#if defined(OTHER_ARCH_LINUX) - struct timespec t; - clock_gettime(CLOCK_MONOTONIC_RAW, &t); - return (t.tv_sec * 1000000) + (t.tv_nsec / 1000); -#else - return zero; -#endif -} - -/* Called by the xTaskStartScheduler() system call, TaskRun() executes a task and updates all of its -runtime statistics. */ -void TaskRun(Task_t *task_) { - Time_t taskStartTime = zero; - - Time_t prevTotalRunTime = zero; - - /* Record the total runtime before executing the task. */ - prevTotalRunTime = task_->totalRunTime; - - /* Record the start time of the task. */ - taskStartTime = CURRENTTIME(); - - /* Enable interrupts for the task. */ - ENABLE_INTERRUPTS(); - - /* Call the task from its callback pointer. */ - (*task_->callback)(task_, task_->taskParameter); - - /* Disable interrupts now that the task has returned. */ - DISABLE_INTERRUPTS(); - - /* Calculate the runtime and store it in last runtime. */ - task_->lastRunTime = CURRENTTIME() - taskStartTime; - - /* Add last runtime to the total runtime. */ - task_->totalRunTime += task_->lastRunTime; - - /* Check if the new total runtime is less than the previous total runtime, - if so an overflow has occurred so set the runtime over flow system flag. */ - if (task_->totalRunTime < prevTotalRunTime) { - SYSFLAG_OVERFLOW() = true; - } - - return; -} - -/* The xTaskResumeAll() system call will set the scheduler system flag so the next -call to xTaskStartScheduler() will resume execute of all tasks. */ -void xTaskResumeAll(void) { - SYSFLAG_RUNNING() = true; - return; -} - -/* The xTaskSuspendAll() system call will set the scheduler system flag so the scheduler -will stop and return. */ -void xTaskSuspendAll(void) { - SYSFLAG_RUNNING() = false; - return; -} - -/* The xSystemHalt() system call stops the system by putting HeliOS into an infinite loop. */ -void xSystemHalt(void) { - DISABLE_INTERRUPTS(); - for (;;) { - } -} - -/* TO-DO: Implement xTaskStopScheduler(). */ -void xTaskStopScheduler(void) { - return; -} - -/* The xSystemGetSystemInfo() system call will return the type xSystemInfo containing -information about the system including the OS (product) name, its version and how many tasks -are currently in the running, suspended or waiting states. */ -SystemInfo_t *xSystemGetSystemInfo(void) { - SystemInfo_t *ret = NULL; - - ret = (SystemInfo_t *)xMemAlloc(sizeof(SystemInfo_t)); - - /* Check if system info is not null to make sure xMemAlloc() successfully allocated - the memory. */ - if (ISNOTNULLPTR(ret)) { - memcpy_(ret->productName, PRODUCT_NAME, PRODUCTNAME_SIZE); - - ret->majorVersion = MAJOR_VERSION_NO; - - ret->minorVersion = MINOR_VERSION_NO; - - ret->patchVersion = PATCH_VERSION_NO; - - ret->numberOfTasks = xTaskGetNumberOfTasks(); - } - - return ret; -} \ No newline at end of file diff --git a/src/sys.c b/src/sys.c new file mode 100644 index 0000000..260fa21 --- /dev/null +++ b/src/sys.c @@ -0,0 +1,119 @@ +/** + * @file sys.c + * @author Manny Peterson (mannymsp@gmail.com) + * @brief Kernel sources system related calls + * @version 0.3.1 + * @date 2022-01-31 + * + * @copyright + * HeliOS Embedded Operating System + * Copyright (C) 2020-2022 Manny Peterson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "sys.h" + +/* "You are not expected to understand this." +Thank you for the best OS on Earth, Dennis. */ + + + + +/* Declare and set the system flags to their default values. */ +SysFlags_t sysFlags = { + .running = false, + .overflow = false, + .privileged = false}; + + + + +/* The SystemAssert() system call will be called when +the SYSASSERT() macro evaluates false. In order for there +to be any effect, CONFIG_ENABLE_SYSTEM_ASSERT and +CONFIG_SYSTEM_ASSERT_BEHAVIOR must be defined. + +SystemAssert() should NOT be called directly. Instead +use the SYSASSERT() C macro. */ +void SystemAssert(const char *file_, int line_) { + +/* Do not modify this system call directly. Define +the behavior (code) through the CONFIG_SYSTEM_ASSERT_BEHAVIOR +setting in the config.h header file. */ + +#if defined(CONFIG_SYSTEM_ASSERT_BEHAVIOR) + CONFIG_SYSTEM_ASSERT_BEHAVIOR(file_, line_); +#endif + + + + return; +} + +/* The xSystemHalt() system call halts the system. */ +void xSystemHalt(void) { + + + /* Don't want to service interrupts anymore so disable them. */ + DISABLE_INTERRUPTS(); + + + + /* Put the processor into an infinite loop. */ + for (;;) { + /* Do nothing - literally. */ + } + + + +} + +/* The xSystemGetSystemInfo() system call will return the type xSystemInfo containing +information about the system including the OS (product) name, its version and how many tasks +are currently in the running, suspended or waiting states. */ +SystemInfo_t *xSystemGetSystemInfo(void) { + + SystemInfo_t *ret = NULL; + + + + ret = (SystemInfo_t *)xMemAlloc(sizeof(SystemInfo_t)); + + + + /* Assert if xMemAlloc() failed to allocate the heap memory. */ + SYSASSERT(ISNOTNULLPTR(ret)); + + + + /* Check if system info is not null to make sure xMemAlloc() successfully allocated + the memory. */ + if (ISNOTNULLPTR(ret)) { + + + memcpy_(ret->productName, OS_PRODUCT_NAME, OS_PRODUCT_NAME_SIZE); + + ret->majorVersion = OS_MAJOR_VERSION_NO; + + ret->minorVersion = OS_MINOR_VERSION_NO; + + ret->patchVersion = OS_PATCH_VERSION_NO; + + ret->numberOfTasks = xTaskGetNumberOfTasks(); + } + + return ret; +} \ No newline at end of file diff --git a/src/sched.h b/src/sys.h similarity index 76% rename from src/sched.h rename to src/sys.h index 7b91d8c..08cc366 100644 --- a/src/sched.h +++ b/src/sys.h @@ -1,30 +1,30 @@ /** - * @file sched.h + * @file sys.h * @author Manny Peterson (mannymsp@gmail.com) - * @brief Kernel sources for the HeliOS scheduler - * @version 0.3.0 + * @brief Kernel sources system related calls + * @version 0.3.1 * @date 2022-01-31 - * + * * @copyright * HeliOS Embedded Operating System * Copyright (C) 2020-2022 Manny Peterson - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - * + * */ -#ifndef SCHED_H_ -#define SCHED_H_ +#ifndef SYS_H_ +#define SYS_H_ #include "config.h" #include "defines.h" @@ -38,16 +38,14 @@ extern "C" { #endif -void xTaskStartScheduler(void); -void RunTimeReset(void); -Time_t CurrentTime(void); -void TaskRun(Task_t *task_); -void xTaskResumeAll(void); -void xTaskSuspendAll(void); +void SystemAssert(const char *file_, int line_); void xSystemHalt(void); -void xTaskStopScheduler(void); SystemInfo_t *xSystemGetSystemInfo(void); +#if defined(CONFIG_ENABLE_ARDUINO_CPP_INTERFACE) +void ArduinoAssert(const char *file_, int line_); +#endif + #ifdef __cplusplus } // extern "C" { #endif diff --git a/src/task.c b/src/task.c index ca04407..27bf7b5 100644 --- a/src/task.c +++ b/src/task.c @@ -1,8 +1,8 @@ /** * @file task.c * @author Manny Peterson (mannymsp@gmail.com) - * @brief Kernel sources for tasks and task management in HeliOS - * @version 0.3.0 + * @brief Kernel sources for task management + * @version 0.3.1 * @date 2022-01-31 * * @copyright @@ -26,35 +26,83 @@ #include "task.h" + + extern SysFlags_t sysFlags; + + /* Declare and initialize the task list to null. */ TaskList_t *taskList = NULL; + +/* Declare and initialize the scheduler state to +running. This is controlled with xTaskResumeAll() +and xTaskSuspendAll(). */ +SchedulerState_t schedulerState = SchedulerStateRunning; + + + /* The xTaskCreate() system call will create a new task. The task will be created with its state set to suspended. The xTaskCreate() and xTaskDelete() system calls cannot be called within a task. They MUST be called outside of the scope of the HeliOS scheduler. */ Task_t *xTaskCreate(const char *name_, void (*callback_)(Task_t *, TaskParm_t *), TaskParm_t *taskParameter_) { + + Task_t *ret = NULL; Task_t *taskCursor = NULL; - /* Check if not in a critical section from CRITICAL_ENTER() and make sure the name and callback parameters - are not null. */ - if ((SYSFLAG_CRITICAL() == false) && (ISNOTNULLPTR(name_)) && (ISNOTNULLPTR(callback_))) { - /* Check if the task list is null, if it is call xMemAlloc() to allocate - the dynamic memory for it. */ + SYSASSERT(false == SYSFLAG_RUNNING()); + + SYSASSERT(ISNOTNULLPTR(name_)); + + SYSASSERT(ISNOTNULLPTR(callback_)); + + + + + /* Make sure we aren't inside the scope of the scheduler and that the end-user didn't + pass any null pointers. + + NOTE: It is okay for the task paramater to be null. */ + if ((false == SYSFLAG_RUNNING()) && (ISNOTNULLPTR(name_)) && (ISNOTNULLPTR(callback_))) { + + + /* See if the task list needs to be created. */ if (ISNULLPTR(taskList)) { + + + /* We are creating a kernel object here so enter privileged mode. */ + ENTER_PRIVILEGED(); + + taskList = (TaskList_t *)xMemAlloc(sizeof(TaskList_t)); } - /* Check if the task list is still null in which case xMemAlloc() was unable to allocate - the required memory. Enable interrupts and return null. */ + + + /* Assert if xMemAlloc() didn't do its job. */ + SYSASSERT(ISNOTNULLPTR(taskList)); + + + /* Check if xMemAlloc() did its job. */ if (ISNOTNULLPTR(taskList)) { + + + /* We are creating a kernel object so enter privileged mode. */ + ENTER_PRIVILEGED(); + ret = (Task_t *)xMemAlloc(sizeof(Task_t)); - /* Check if the task is not null. If it is, then xMemAlloc() was unable to allocate the required - memory. */ + + /* Again, assert if xMemAlloc() didn't do its job. */ + SYSASSERT(ISNOTNULLPTR(ret)); + + + /* Check if xMemAlloc() did its job. If so, populate the task with all the + pertinent details. */ if (ISNOTNULLPTR(ret)) { + taskList->nextId++; ret->id = taskList->nextId; @@ -71,16 +119,23 @@ Task_t *xTaskCreate(const char *name_, void (*callback_)(Task_t *, TaskParm_t *) taskCursor = taskList->head; - /* Check if the task list head is not null, if it is set it to the newly created task. If it - isn't null then append the newly created task to the task list. */ + /* Check if this is the first task in the task list. If it is just set + the head to it. Otherwise we are going to have to traverse the list + to find the end. */ if (ISNOTNULLPTR(taskList->head)) { + + /* If the task cursor is not null, continue to traverse the list to find the end. */ while (ISNOTNULLPTR(taskCursor->next)) { + + taskCursor = taskCursor->next; } taskCursor->next = ret; + } else { + taskList->head = ret; } @@ -92,45 +147,87 @@ Task_t *xTaskCreate(const char *name_, void (*callback_)(Task_t *, TaskParm_t *) return ret; } + + /* The xTaskDelete() system call will delete a task. The xTaskCreate() and xTaskDelete() system calls cannot be called within a task. They MUST be called outside of the scope of the HeliOS scheduler. */ void xTaskDelete(Task_t *task_) { + + Task_t *taskCursor = NULL; Task_t *taskPrevious = NULL; - /* Check if not in a critical section from CRITICAL_ENTER() and make sure the task list is not null - and that the task parameter is also not null. */ - if ((SYSFLAG_CRITICAL() == false) && (ISNOTNULLPTR(taskList)) && (ISNOTNULLPTR(task_))) { - taskCursor = taskList->head; - taskPrevious = NULL; + /* Assert if we are within the scope of the scheduler. */ + SYSASSERT(false == SYSFLAG_RUNNING()); - /* Check if the task cursor is not null and if the task cursor equals the task - to be deleted. */ - if ((ISNOTNULLPTR(taskCursor)) && (taskCursor == task_)) { - taskList->head = taskCursor->next; - xMemFree(taskCursor); + /* Check to make sure we aren't in the scope of the scheduler. */ + if (false == SYSFLAG_RUNNING()) { - taskList->length--; - } else { - /* While the task cursor is not null and the task cursor is not equal to the - task to be deleted. */ - while ((ISNOTNULLPTR(taskCursor)) && (taskCursor != task_)) { - taskPrevious = taskCursor; + /* Assert if we can't find the task in the task list. */ + SYSASSERT(RETURN_SUCCESS == TaskListFindTask(task_)); - taskCursor = taskCursor->next; - } - /* If the task cursor is is not null, then delete the task otherwise return. */ - if (ISNOTNULLPTR(taskCursor)) { - taskPrevious->next = taskCursor->next; + /* Check if the task is in the task list, if not then head toward + the exit. */ + if (RETURN_SUCCESS == TaskListFindTask(task_)) { + + + taskCursor = taskList->head; + + + /* If the task is at the head of the task list then just remove + it and free its heap memory. */ + if ((ISNOTNULLPTR(taskCursor)) && (taskCursor == task_)) { + + + taskList->head = taskCursor->next; + + + /* We are freeing a kernel object so enter privileged mode. */ + ENTER_PRIVILEGED(); xMemFree(taskCursor); taskList->length--; + + } else { + + /* Well, it wasn't at the head of the task list so now we have to + go hunt it down. */ + while ((ISNOTNULLPTR(taskCursor)) && (taskCursor != task_)) { + + taskPrevious = taskCursor; + + taskCursor = taskCursor->next; + } + + + /* Assert if we didn't find it which should be impossible given + TaskListFindTask() found it. Maybe some cosmic rays bit flipped + the DRAM!? */ + SYSASSERT(ISNOTNULLPTR(taskCursor)); + + + /* Check if the task cursor points to something. That something should + be the task we want to free. */ + if (ISNOTNULLPTR(taskCursor)) { + + taskPrevious->next = taskCursor->next; + + + /* We are freeing a kernel object so enter privileged mode. */ + ENTER_PRIVILEGED(); + + + xMemFree(taskCursor); + + + taskList->length--; + } } } } @@ -138,25 +235,44 @@ void xTaskDelete(Task_t *task_) { return; } + + + /* The xTaskGetHandleByName() system call will return the task handle pointer to the task specified by its ASCII name. The length of the task name is dependent on the CONFIG_TASK_NAME_BYTES setting. The name is compared byte-for-byte so the name is case sensitive. */ Task_t *xTaskGetHandleByName(const char *name_) { + + Task_t *ret = NULL; Task_t *taskCursor = NULL; + + /* Assert if the task list has not been initialized. We wouldn't have to do this + if we called TaskListFindTask() but that isn't needed here. */ + SYSASSERT(ISNOTNULLPTR(taskList)); + + /* Assert if the end-user passed a null pointer for the task name. */ + SYSASSERT(ISNOTNULLPTR(name_)); + /* Check if the task list is not null and the name parameter is also not null. */ if ((ISNOTNULLPTR(taskList)) && (ISNOTNULLPTR(name_))) { + taskCursor = taskList->head; /* While the task cursor is not null, scan the task list for the task name. */ while (ISNOTNULLPTR(taskCursor)) { + /* Compare the task name of the task pointed to by the task cursor against the name parameter. */ - if (memcmp_(taskCursor->name, name_, CONFIG_TASK_NAME_BYTES) == zero) { + if (zero == memcmp_(taskCursor->name, name_, CONFIG_TASK_NAME_BYTES)) { + + ret = taskCursor; + + break; } taskCursor = taskCursor->next; @@ -166,23 +282,42 @@ Task_t *xTaskGetHandleByName(const char *name_) { return ret; } + + + /* The xTaskGetHandleById() system call will return a pointer to the task handle specified by its identifier. */ Task_t *xTaskGetHandleById(Base_t id_) { + + Task_t *ret = NULL; Task_t *taskCursor = NULL; + /* Assert if the task list has not been initialized. We wouldn't have to do this + if we called TaskListFindTask() but that isn't needed here. */ + SYSASSERT(ISNOTNULLPTR(taskList)); + + + /* Assert if the task identifier is zero because it shouldn't be. */ + SYSASSERT(zero < id_); + /* Check if the task list is not null and the identifier parameter is greater than zero. */ - if (ISNOTNULLPTR(taskList)) { + if ((ISNOTNULLPTR(taskList)) && (zero < id_)) { + + taskCursor = taskList->head; /* While the task cursor is not null, check the task pointed to by the task cursor and compare its identifier against the identifier parameter being searched for. */ while (ISNOTNULLPTR(taskCursor)) { - if (taskCursor->id == id_) { + + if (id_ == taskCursor->id) { + ret = taskCursor; + + break; } taskCursor = taskCursor->next; @@ -192,11 +327,16 @@ Task_t *xTaskGetHandleById(Base_t id_) { return ret; } + + + /* The xTaskGetAllRunTimeStats() system call will return the runtime statistics for all of the tasks regardless of their state. The xTaskGetAllRunTimeStats() system call returns the xTaskRunTimeStats type. An xBase variable must be passed by reference to xTaskGetAllRunTimeStats() which will contain the number of tasks so the end-user can iterate through the tasks. */ TaskRunTimeStats_t *xTaskGetAllRunTimeStats(Base_t *tasks_) { + + Base_t i = zero; Base_t tasks = zero; @@ -205,30 +345,56 @@ TaskRunTimeStats_t *xTaskGetAllRunTimeStats(Base_t *tasks_) { TaskRunTimeStats_t *ret = NULL; + + /* Assert if the task list has not been initialized. We wouldn't have to do this + if we called TaskListFindTask() but that isn't needed here. */ + SYSASSERT(ISNOTNULLPTR(taskList)); + + + /* Check to make sure the pointer for the pass-by-reference is not null. */ + SYSASSERT(ISNOTNULLPTR(tasks_)); + + /* Check if the task list is not null and the tasks parameter is not null. */ if ((ISNOTNULLPTR(taskList)) && (ISNOTNULLPTR(tasks_))) { + taskCursor = taskList->head; /* While the task cursor is not null, continue to traverse the task list counting the number of tasks in the list. */ while (ISNOTNULLPTR(taskCursor)) { + tasks++; taskCursor = taskCursor->next; } + + /* Assert if the tasks counted does not agree with the length of the task list. */ + SYSASSERT(tasks == taskList->length); + /* Check if the number of tasks is greater than zero and the length of the task list equals the number of tasks just counted (this is done as an integrity check). */ - if ((tasks > zero) && (taskList->length == tasks)) { + if ((zero < tasks) && (tasks == taskList->length)) { + + ret = (TaskRunTimeStats_t *)xMemAlloc(tasks * sizeof(TaskRunTimeStats_t)); + /* Assert if xMemAlloc() didn't do its job. */ + SYSASSERT(ISNOTNULLPTR(ret)); + + /* Check if xMemAlloc() successfully allocated the memory. */ if (ISNOTNULLPTR(ret)) { + taskCursor = taskList->head; /* While the task cursor is not null, continue to traverse the task list adding the runtime statistics of each task to the runtime stats array to be returned. */ while (ISNOTNULLPTR(taskCursor)) { + + ret[i].id = taskCursor->id; + ret[i].lastRunTime = taskCursor->lastRunTime; ret[i].totalRunTime = taskCursor->totalRunTime; @@ -241,6 +407,7 @@ TaskRunTimeStats_t *xTaskGetAllRunTimeStats(Base_t *tasks_) { *tasks_ = tasks; } else { + *tasks_ = zero; } } @@ -249,64 +416,85 @@ TaskRunTimeStats_t *xTaskGetAllRunTimeStats(Base_t *tasks_) { return ret; } + + + /* The xTaskGetTaskRunTimeStats() system call returns the task runtime statistics for one task. The xTaskGetTaskRunTimeStats() system call returns the xTaskRunTimeStats type. The memory must be freed by calling xMemFree() after it is no longer needed. */ TaskRunTimeStats_t *xTaskGetTaskRunTimeStats(Task_t *task_) { - Task_t *taskCursor = NULL; TaskRunTimeStats_t *ret = NULL; - /* Check if the task list and the task parameter is not null. */ - if ((ISNOTNULLPTR(taskList)) && (ISNOTNULLPTR(task_))) { - taskCursor = taskList->head; - /* While the task cursor is not null and the task cursor is not equal to the - task being searched for. */ - while ((ISNOTNULLPTR(taskCursor)) && (taskCursor != task_)) { - taskCursor = taskCursor->next; - } - /* If the task cursor is null, the task could not be found so return null. */ - if (ISNOTNULLPTR(taskCursor)) { - ret = (TaskRunTimeStats_t *)xMemAlloc(sizeof(TaskRunTimeStats_t)); + /* Assert if the task cannot be found in the task list. */ + SYSASSERT(RETURN_SUCCESS == TaskListFindTask(task_)); - /* Check if xMemAlloc() successfully allocated the memory. */ - if (ISNOTNULLPTR(ret)) { - ret->lastRunTime = taskCursor->lastRunTime; + /* Check if the task cannot be found in the task list. */ + if (RETURN_SUCCESS == TaskListFindTask(task_)) { - ret->totalRunTime = taskCursor->totalRunTime; - } + + ret = (TaskRunTimeStats_t *)xMemAlloc(sizeof(TaskRunTimeStats_t)); + + + /* Assert if xMemAlloc() didn't do its job. */ + SYSASSERT(ISNOTNULLPTR(ret)); + + /* Check if xMemAlloc() successfully allocated the memory. */ + if (ISNOTNULLPTR(ret)) { + + ret->id = task_->id; + + ret->lastRunTime = task_->lastRunTime; + + ret->totalRunTime = task_->totalRunTime; } } return ret; } + + + /* The xTaskGetNumberOfTasks() system call returns the current number of tasks regardless of their state. */ Base_t xTaskGetNumberOfTasks(void) { + + Base_t ret = zero; Base_t tasks = zero; Task_t *taskCursor = NULL; - /* Check if the task list is not null. */ + /* Assert if the task list is not initialized. */ + SYSASSERT(ISNOTNULLPTR(taskList)); + + + /* Check if the task list is not initialized. */ if (ISNOTNULLPTR(taskList)) { + taskCursor = taskList->head; /* While the task cursor is not null, continue to count the number of tasks. */ while (ISNOTNULLPTR(taskCursor)) { + tasks++; taskCursor = taskCursor->next; } + /* Assert if the number of tasks counted does not agree with the task list + length. */ + SYSASSERT(tasks == taskList->length); + /* Check if the length of the task list equals the number of tasks counted (this is an integrity check). */ - if (taskList->length == tasks) { + if (tasks == taskList->length) { + ret = tasks; } } @@ -314,432 +502,824 @@ Base_t xTaskGetNumberOfTasks(void) { return ret; } + + + /* The xTaskGetTaskInfo() system call returns the xTaskInfo structure containing the details of the task including its identifier, name, state and runtime statistics. */ TaskInfo_t *xTaskGetTaskInfo(Task_t *task_) { - Task_t *taskCursor = NULL; TaskInfo_t *ret = NULL; - /* Check if the task list is not null and the task parameter is not null. */ - if ((ISNOTNULLPTR(taskList)) && (ISNOTNULLPTR(task_))) { - taskCursor = taskList->head; - /* While the task cursor is not null and the task cursor is not equal - to the task being searched for. */ - while ((ISNOTNULLPTR(taskCursor)) && (taskCursor != task_)) { - taskCursor = taskCursor->next; - } - /* Check if the task cursor is null, if so the task could not be found - so return null. */ - if (ISNOTNULLPTR(taskCursor)) { - ret = (TaskInfo_t *)xMemAlloc(sizeof(TaskInfo_t)); + /* Assert if the task cannot be found. */ + SYSASSERT(RETURN_SUCCESS == TaskListFindTask(task_)); - /* Check if the task info memory has been allocated by xMemAlloc(). if it - has then populate the task info and return. */ - if (ISNOTNULLPTR(ret)) { - ret->id = taskCursor->id; - ret->state = taskCursor->state; + /* Check if the task cannot be found. */ + if (RETURN_SUCCESS == TaskListFindTask(task_)) { - memcpy_(ret->name, taskCursor->name, CONFIG_TASK_NAME_BYTES); + ret = (TaskInfo_t *)xMemAlloc(sizeof(TaskInfo_t)); - ret->lastRunTime = taskCursor->lastRunTime; - ret->totalRunTime = taskCursor->totalRunTime; - } - } - } + /* Assert if xMemAlloc() failed to do its one job in life. */ + SYSASSERT(ISNOTNULLPTR(ret)); - return ret; -} + /* Check if the task info memory has been allocated by xMemAlloc(). if it + has then populate the task info and return. */ + if (ISNOTNULLPTR(ret)) { -/* The xTaskGetTaskState() system call will return the state of the task. */ -TaskState_t xTaskGetTaskState(Task_t *task_) { - TaskState_t ret = TaskStateError; - Task_t *taskCursor = NULL; + ret->id = task_->id; - /* Check if the task list is not null and the task parameter is not null. */ - if ((ISNOTNULLPTR(taskList)) && (ISNOTNULLPTR(task_))) { - taskCursor = taskList->head; + ret->state = task_->state; - /* While the task cursor is not null and the task cursor is not equal - to the task being searched for. */ - while ((ISNOTNULLPTR(taskCursor)) && (taskCursor != task_)) { - taskCursor = taskCursor->next; - } + memcpy_(ret->name, task_->name, CONFIG_TASK_NAME_BYTES); - /* Check if the task cursor is not null, if so return the tasks's state - otherwise return the error task state. */ - if (ISNOTNULLPTR(taskCursor)) { - ret = taskCursor->state; + ret->lastRunTime = task_->lastRunTime; - } else { - ret = TaskStateError; + ret->totalRunTime = task_->totalRunTime; } } return ret; } -/* The xTaskGetName() system call returns the ASCII name of the task. The size of the -task is dependent on the setting CONFIG_TASK_NAME_BYTES. The task name is NOT a null -terminated char array. */ -char *xTaskGetName(Task_t *task_) { - char *ret = NULL; - - Task_t *taskCursor = NULL; - /* Check if the task list is not null and the task parameter is not null. */ - if ((ISNOTNULLPTR(taskList)) && (ISNOTNULLPTR(task_))) { - taskCursor = taskList->head; - /* While the task cursor is not null and the task cursor is not equal - to the task being searched for. */ - while ((ISNOTNULLPTR(taskCursor)) && (taskCursor != task_)) { - taskCursor = taskCursor->next; - } - /* Check if the task cursor is not null, if so allocate memory for the - task name. */ - if (ISNOTNULLPTR(taskCursor)) { - ret = (char *)xMemAlloc(CONFIG_TASK_NAME_BYTES); +/* The xTaskGetAllTaskInfo() system call returns the xTaskInfo structure containing +the details of ALL tasks including its identifier, name, state and runtime statistics. */ +TaskInfo_t *xTaskGetAllTaskInfo(Base_t *tasks_) { - /* Check if the task info memory has been allocated by xMemAlloc(). */ - if (ISNOTNULLPTR(ret)) { - memcpy_(ret, taskCursor->name, CONFIG_TASK_NAME_BYTES); - } - } - } - return ret; -} + Base_t i = zero; -/* The xTaskGetId() system call returns the task identifier for the task. */ -Base_t xTaskGetId(Task_t *task_) { - Base_t ret = zero; + Base_t tasks = zero; Task_t *taskCursor = NULL; - /* Check if the task list is not null and the task parameter is not null. */ - if ((ISNOTNULLPTR(taskList)) && (ISNOTNULLPTR(task_))) { - taskCursor = taskList->head; - - /* While the task cursor is not null and the task cursor is not equal - to the task being searched for. */ - while ((ISNOTNULLPTR(taskCursor)) && (taskCursor != task_)) { - taskCursor = taskCursor->next; - } + TaskInfo_t *ret = NULL; - /* Check if the task cursor is not null, if so set the return value - to the task identifier. */ - if (ISNOTNULLPTR(taskCursor)) { - ret = taskCursor->id; - } - } - return ret; -} + /* Assert if the task list has not been initialized. */ + SYSASSERT(ISNOTNULLPTR(taskList)); -/* TO-DO: Implement xTaskList(). */ -char *xTaskList(void) { - return NULL; -} -/* The xTaskNotifyStateClear() system call will clear a waiting task notification if one -exists without returning the notification. */ -void xTaskNotifyStateClear(Task_t *task_) { - Task_t *taskCursor = NULL; + /* Assert if the end-user passed us a null pointer. */ + SYSASSERT(ISNOTNULLPTR(tasks_)); - /* Check if the task list is not null and the task parameter is not null. */ - if ((ISNOTNULLPTR(taskList)) && (ISNOTNULLPTR(task_))) { - taskCursor = taskList->head; - /* While the task cursor is not null and the task cursor is not equal - to the task being searched for. */ - while ((ISNOTNULLPTR(taskCursor)) && (taskCursor != task_)) { - taskCursor = taskCursor->next; - } + /* Check if the task list is not null and the tasks parameter is not null. */ + if ((ISNOTNULLPTR(taskList)) && (ISNOTNULLPTR(tasks_))) { - /* Check if the task cursor is not null, if so check if there is a waiting - notification. */ - if (ISNOTNULLPTR(taskCursor)) { - /* If the task notification bytes are greater than zero, then there is a notification - to clear. */ - if (taskCursor->notificationBytes > zero) { - taskCursor->notificationBytes = zero; - memset_(taskCursor->notificationValue, zero, CONFIG_NOTIFICATION_VALUE_BYTES); - } - } - } + taskCursor = taskList->head; - return; -} -/* The xTaskNotificationIsWaiting() system call will return true or false depending -on whether there is a task notification waiting for the task. */ -Base_t xTaskNotificationIsWaiting(Task_t *task_) { - Base_t ret = zero; - Task_t *taskCursor = NULL; + /* While the task cursor is not null, continue to traverse the task list counting + the number of tasks in the list. */ + while (ISNOTNULLPTR(taskCursor)) { - /* Check if the task list is not null and the task parameter is not null. */ - if ((ISNOTNULLPTR(taskList)) && (ISNOTNULLPTR(task_))) { - taskCursor = taskList->head; + tasks++; - /* While the task cursor is not null and the task cursor is not equal - to the task being searched for. */ - while ((ISNOTNULLPTR(taskCursor)) && (taskCursor != task_)) { taskCursor = taskCursor->next; } - /* Check if the task cursor is not null, if so check if there is a waiting - task notification. */ - if (ISNOTNULLPTR(taskCursor)) { - /* Check if the notification bytes are greater than zero. If so, there is a notification - waiting so return true. */ - if (taskCursor->notificationBytes > zero) { - ret = true; - } - } - } - return ret; -} + /* Assert if the number of tasks counted disagrees with the length of the task list. */ + SYSASSERT(tasks == taskList->length); -/* The xTaskNotifyGive() system call will send a task notification to the specified task. The -task notification bytes is the number of bytes contained in the notification value. The number of -notification bytes must be between one and the CONFIG_NOTIFICATION_VALUE_BYTES setting. The notification -value must contain a pointer to a char array containing the notification value. If the task already -has a waiting task notification, xTaskNotifyGive() will NOT overwrite the waiting task notification. */ -Base_t xTaskNotifyGive(Task_t *task_, Base_t notificationBytes_, const char *notificationValue_) { - Base_t ret = false; + /* Check if the number of tasks is greater than zero and the length of the task list equals + the number of tasks just counted (this is done as an integrity check). */ + if ((zero < tasks) && (tasks == taskList->length)) { - Task_t *taskCursor = NULL; - /* Check if the task list is not null and the task parameter is not null, the notification bytes are between - one and CONFIG_NOTIFICATION_VALUE_BYTES and that the notification value char array pointer is not null. */ - if ((ISNOTNULLPTR(taskList)) && (ISNOTNULLPTR(task_)) && (notificationBytes_ > zero) && (notificationBytes_ < CONFIG_NOTIFICATION_VALUE_BYTES) && (ISNOTNULLPTR(notificationValue_))) { - taskCursor = taskList->head; - /* While the task cursor is not null and the task cursor is not equal - to the task being searched for. */ - while ((ISNOTNULLPTR(taskCursor)) && (taskCursor != task_)) { - taskCursor = taskCursor->next; - } + ret = (TaskInfo_t *)xMemAlloc(tasks * sizeof(TaskInfo_t)); - /* Check if the task cursor is not null, if so add the task notification to the task. */ - if (ISNOTNULLPTR(taskCursor)) { - /* If the notification bytes are zero then there is not a notification already waiting, - so copy the notification value into the task and set the notification bytes. */ - if (taskCursor->notificationBytes == zero) { - taskCursor->notificationBytes = notificationBytes_; - memcpy_(taskCursor->notificationValue, notificationValue_, CONFIG_NOTIFICATION_VALUE_BYTES); + /* Assert if xMemAlloc() didn't do its job. */ + SYSASSERT(ISNOTNULLPTR(ret)); - ret = true; - } + /* Check if xMemAlloc() successfully allocated the memory. */ + if (ISNOTNULLPTR(ret)) { + + + taskCursor = taskList->head; + + + /* While the task cursor is not null, continue to traverse the task list adding the + runtime statistics of each task to the runtime stats array to be returned. */ + while (ISNOTNULLPTR(taskCursor)) { + + + ret[i].id = taskCursor->id; + + ret[i].state = taskCursor->state; + + memcpy_(ret[i].name, taskCursor->name, CONFIG_TASK_NAME_BYTES); + + ret[i].lastRunTime = taskCursor->lastRunTime; + + ret[i].totalRunTime = taskCursor->totalRunTime; + + taskCursor = taskCursor->next; + + i++; + } + + *tasks_ = tasks; + + } else { + + *tasks_ = zero; + } + } + } + + return ret; +} + + + +/* The xTaskGetTaskState() system call will return the state of the task. */ +TaskState_t xTaskGetTaskState(Task_t *task_) { + + + TaskState_t ret = TaskStateError; + + + + /* Assert if the task cannot be found. */ + SYSASSERT(RETURN_SUCCESS == TaskListFindTask(task_)); + + + /* Check to make sure the task was found. */ + if (RETURN_SUCCESS == TaskListFindTask(task_)) { + + ret = task_->state; + } + + + return ret; +} + + + +/* The xTaskGetName() system call returns the ASCII name of the task. The size of the +task is dependent on the setting CONFIG_TASK_NAME_BYTES. The task name is NOT a null +terminated char array. */ +char *xTaskGetName(Task_t *task_) { + + + char *ret = NULL; + + + + /* Assert if the task cannot be found. */ + SYSASSERT(RETURN_SUCCESS == TaskListFindTask(task_)); + + + /* Check if the task can be found. */ + if (RETURN_SUCCESS == TaskListFindTask(task_)) { + + + ret = (char *)xMemAlloc(CONFIG_TASK_NAME_BYTES); + + + /* Assert if xMemAlloc() didn't do its job. */ + SYSASSERT(ISNOTNULLPTR(ret)); + + + /* Check if the task info memory has been allocated by xMemAlloc(). */ + if (ISNOTNULLPTR(ret)) { + + + + memcpy_(ret, task_->name, CONFIG_TASK_NAME_BYTES); + } + } + + + return ret; +} + + + +/* The xTaskGetId() system call returns the task identifier for the task. */ +Base_t xTaskGetId(Task_t *task_) { + + + Base_t ret = zero; + + + /* Assert if the task cannot be found. */ + SYSASSERT(RETURN_SUCCESS == TaskListFindTask(task_)); + + + /* Check that the task was found. */ + if (RETURN_SUCCESS == TaskListFindTask(task_)) { + + ret = task_->id; + } + + + return ret; +} + + + + +/* The xTaskNotifyStateClear() system call will clear a waiting task notification if one +exists without returning the notification. */ +void xTaskNotifyStateClear(Task_t *task_) { + + + /* Assert if the task cannot be found. */ + SYSASSERT(RETURN_SUCCESS == TaskListFindTask(task_)); + + + /* Check if the task was found. */ + if (RETURN_SUCCESS == TaskListFindTask(task_)) { + + + /* If the notification bytes are greater than zero then there + is a notification to be cleared. */ + if (zero < task_->notificationBytes) { + + task_->notificationBytes = zero; + + memset_(task_->notificationValue, zero, CONFIG_NOTIFICATION_VALUE_BYTES); + } + } + + + return; +} + + + +/* The xTaskNotificationIsWaiting() system call will return true or false depending +on whether there is a task notification waiting for the task. */ +Base_t xTaskNotificationIsWaiting(Task_t *task_) { + Base_t ret = zero; + + + + /* Assert if the task cannot be found. */ + SYSASSERT(RETURN_SUCCESS == TaskListFindTask(task_)); + + + /* Check if the task was found. */ + if (RETURN_SUCCESS == TaskListFindTask(task_)) { + + + /* If there are notification bytes, there is a notification + waiting. */ + if (zero < task_->notificationBytes) { + + + ret = true; + } + } + + + return ret; +} + + + + +/* The xTaskNotifyGive() system call will send a task notification to the specified task. The +task notification bytes is the number of bytes contained in the notification value. The number of +notification bytes must be between one and the CONFIG_NOTIFICATION_VALUE_BYTES setting. The notification +value must contain a pointer to a char array containing the notification value. If the task already +has a waiting task notification, xTaskNotifyGive() will NOT overwrite the waiting task notification. */ +Base_t xTaskNotifyGive(Task_t *task_, Base_t notificationBytes_, const char *notificationValue_) { + + + + Base_t ret = RETURN_FAILURE; + + /* Assert if the notification bytes are zero. */ + SYSASSERT(zero < notificationBytes_); + + /* Assert if the notification bytes exceeds the setting + CONFIG_NOTIFICATION_VALUE_BYTES. */ + SYSASSERT(CONFIG_NOTIFICATION_VALUE_BYTES > notificationBytes_); + + + /* Assert if the end-user passed us a null pointer for the + notification value. */ + SYSASSERT(ISNOTNULLPTR(notificationValue_)); + + /* Check if the task list is not null and the task parameter is not null, the notification bytes are between + one and CONFIG_NOTIFICATION_VALUE_BYTES and that the notification value char array pointer is not null. */ + if ((zero < notificationBytes_) && (CONFIG_NOTIFICATION_VALUE_BYTES > notificationBytes_) && (ISNOTNULLPTR(notificationValue_))) { + + + /* Assert if we can't find the task to receive the notification. */ + SYSASSERT(RETURN_SUCCESS == TaskListFindTask(task_)); + + + /* Check if the task can be found. */ + if (RETURN_SUCCESS == TaskListFindTask(task_)) { + + + /* Make sure there isn't a waiting notification. xTaskNotifyGive will NOT + overwrite a waiting notification. */ + if (zero == task_->notificationBytes) { + + task_->notificationBytes = notificationBytes_; + + memcpy_(task_->notificationValue, notificationValue_, CONFIG_NOTIFICATION_VALUE_BYTES); + + ret = RETURN_SUCCESS; + } } } return ret; } + + /* The xTaskNotifyTake() system call will return the waiting task notification if there is one. The xTaskNotifyTake() system call will return an xTaskNotification structure containing the notification bytes and its value. */ TaskNotification_t *xTaskNotifyTake(Task_t *task_) { - Task_t *taskCursor = NULL; TaskNotification_t *ret = NULL; - /* Check if the task list is not null and the task parameter is not null. */ - if ((ISNOTNULLPTR(taskList)) && (ISNOTNULLPTR(task_))) { - taskCursor = taskList->head; - /* While the task cursor is not null and the task cursor is not equal - to the task being searched for. */ - while ((ISNOTNULLPTR(taskCursor)) && (taskCursor != task_)) { - taskCursor = taskCursor->next; - } - /* Check if the task cursor is not null, if so check if there is a waiting - task notification. */ - if (ISNOTNULLPTR(taskCursor)) { - /* Check if the notification bytes are greater than zero, if so there is a waiting task - notification. */ - if (taskCursor->notificationBytes > zero) { - ret = (TaskNotification_t *)xMemAlloc(sizeof(TaskNotification_t)); + /* Assert if the task cannot be found. */ + SYSASSERT(RETURN_SUCCESS == TaskListFindTask(task_)); - /* Check if xMemAlloc() successfully allocated the memory for the task notification - structure. */ - if (ISNOTNULLPTR(ret)) { - ret->notificationBytes = taskCursor->notificationBytes; - memcpy_(ret->notificationValue, taskCursor->notificationValue, CONFIG_NOTIFICATION_VALUE_BYTES); + /* Check if the task cannot be found. */ + if (RETURN_SUCCESS == TaskListFindTask(task_)) { - taskCursor->notificationBytes = zero; + /* If there are notification bytes, there is a notification + waiting. */ + if (zero < task_->notificationBytes) { - memset_(taskCursor->notificationValue, zero, CONFIG_NOTIFICATION_VALUE_BYTES); - } + + + ret = (TaskNotification_t *)xMemAlloc(sizeof(TaskNotification_t)); + + + /* Assert if xMemAlloc() didn't do its job. */ + SYSASSERT(ISNOTNULLPTR(ret)); + + /* Check if xMemAlloc() successfully allocated the memory for the task notification + structure. */ + if (ISNOTNULLPTR(ret)) { + + ret->notificationBytes = task_->notificationBytes; + + memcpy_(ret->notificationValue, task_->notificationValue, CONFIG_NOTIFICATION_VALUE_BYTES); + + task_->notificationBytes = zero; + + memset_(task_->notificationValue, zero, CONFIG_NOTIFICATION_VALUE_BYTES); } } } + return ret; } + + /* The xTaskResume() system call will resume a suspended task. Tasks are suspended on creation so either xTaskResume() or xTaskWait() must be called to place the task in a state that the scheduler will execute. */ void xTaskResume(Task_t *task_) { - Task_t *taskCursor = NULL; - /* Check if the task list is not null and the task parameter is not null. */ - if ((ISNOTNULLPTR(taskList)) && (ISNOTNULLPTR(task_))) { - taskCursor = taskList->head; - /* While the task cursor is not null and the task cursor is not equal - to the task being searched for. */ - while ((ISNOTNULLPTR(taskCursor)) && (taskCursor != task_)) { - taskCursor = taskCursor->next; - } - /* Check if the task cursor is not null, if so set the task state. */ - if (ISNOTNULLPTR(taskCursor)) { - taskCursor->state = TaskStateRunning; - } + /* Assert if the task cannot be found. */ + SYSASSERT(RETURN_SUCCESS == TaskListFindTask(task_)); + + /* Check if the task can be found. */ + if (RETURN_SUCCESS == TaskListFindTask(task_)) { + + task_->state = TaskStateRunning; } + return; } + + + /* The xTaskSuspend() system call will suspend a task. A task that has been suspended will not be executed by the scheduler until xTaskResume() or xTaskWait() is called. */ void xTaskSuspend(Task_t *task_) { - Task_t *taskCursor = NULL; - /* Check if the task list is not null and the task parameter is not null. */ - if ((ISNOTNULLPTR(taskList)) && (ISNOTNULLPTR(task_))) { - taskCursor = taskList->head; - /* While the task cursor is not null and the task cursor is not equal - to the task being searched for. */ - while ((ISNOTNULLPTR(taskCursor)) && (taskCursor != task_)) { - taskCursor = taskCursor->next; - } - /* Check if the task cursor is not null, if so set the task state. */ - if (ISNOTNULLPTR(taskCursor)) { - taskCursor->state = TaskStateSuspended; - } + /* Assert if the task cannot be found. */ + SYSASSERT(RETURN_SUCCESS == TaskListFindTask(task_)); + + + /* Check if the task can be found. */ + if (RETURN_SUCCESS == TaskListFindTask(task_)) { + + task_->state = TaskStateSuspended; } + return; } + + + /* The xTaskWait() system call will place a task in the waiting state. A task must be in the waiting state for event driven multitasking with either direct to task notifications OR setting the period on the task timer with xTaskChangePeriod(). A task in the waiting state will not be executed by the scheduler until an event has occurred. */ void xTaskWait(Task_t *task_) { - Task_t *taskCursor = NULL; - /* Check if the task list is not null and the task parameter is not null. */ - if ((ISNOTNULLPTR(taskList)) && (ISNOTNULLPTR(task_))) { - taskCursor = taskList->head; - /* While the task cursor is not null and the task cursor is not equal - to the task being searched for. */ - while ((ISNOTNULLPTR(taskCursor)) && (taskCursor != task_)) { - taskCursor = taskCursor->next; - } + /* Assert if the task cannot be found. */ + SYSASSERT(RETURN_SUCCESS == TaskListFindTask(task_)); - /* Check if the task cursor is not null, if so set the task state. */ - if (ISNOTNULLPTR(taskCursor)) { - taskCursor->state = TaskStateWaiting; - } + + /* Check if the task can be found. */ + if (RETURN_SUCCESS == TaskListFindTask(task_)) { + + task_->state = TaskStateWaiting; } + return; } + + + /* The xTaskChangePeriod() system call will change the period (microseconds) on the task timer for the specified task. The timer period must be greater than zero. To have any effect, the task must be in the waiting state set by calling xTaskWait() on the task. Once the timer period is set and the task is in the waiting state, the task will be executed every N microseconds based on the period. Changing the period to zero will prevent the task from being executed even if it is in the waiting state. */ void xTaskChangePeriod(Task_t *task_, Time_t timerPeriod_) { - Task_t *taskCursor = NULL; - /* Check if the task list is not null and the task parameter is not null. */ - if ((ISNOTNULLPTR(taskList)) && (ISNOTNULLPTR(task_))) { - taskCursor = taskList->head; - /* While the task cursor is not null and the task cursor is not equal - to the task being searched for. */ - while ((ISNOTNULLPTR(taskCursor)) && (taskCursor != task_)) { - taskCursor = taskCursor->next; - } + /* Assert if the task cannot be found. */ + SYSASSERT(RETURN_SUCCESS == TaskListFindTask(task_)); - /* Check if the task cursor is not null, if so set the task timer period. */ - if (ISNOTNULLPTR(taskCursor)) { - taskCursor->timerPeriod = timerPeriod_; - } + + /* Check if the task can be found. */ + if (RETURN_SUCCESS == TaskListFindTask(task_)) { + + task_->timerPeriod = timerPeriod_; } + return; } + + + /* The xTaskGetPeriod() will return the period for the timer for the specified task. See xTaskChangePeriod() for more information on how the task timer works. */ Time_t xTaskGetPeriod(Task_t *task_) { + + Time_t ret = zero; + + /* Assert if the task cannot be found. */ + SYSASSERT(RETURN_SUCCESS == TaskListFindTask(task_)); + + + /* Check if the task can be found. */ + if (RETURN_SUCCESS == TaskListFindTask(task_)) { + + ret = task_->timerPeriod; + } + + + return ret; +} + + + +/* TaskListFindTask() is used to search the task list for a +task and returns RETURN_SUCCESS if the task is found. It also +always checks the health of the heap by calling HeapCheck(). */ +Base_t TaskListFindTask(const Task_t *task_) { + + + Base_t ret = RETURN_FAILURE; + Task_t *taskCursor = NULL; - /* Check if the task list is not null and the task parameter is not null. */ + + /* Assert if the task list is not initialized. */ + SYSASSERT(ISNOTNULLPTR(taskList)); + + + /* Assert if the task pointer is null. */ + SYSASSERT(ISNOTNULLPTR(task_)); + + + /* Check if the task list is initialized and the task pointer is not null. */ if ((ISNOTNULLPTR(taskList)) && (ISNOTNULLPTR(task_))) { - taskCursor = taskList->head; - /* While the task cursor is not null and the task cursor is not equal - to the task being searched for. */ - while ((ISNOTNULLPTR(taskCursor)) && (taskCursor != task_)) { - taskCursor = taskCursor->next; - } - /* Check if the task cursor is not null, if so set the return value to the - task timer period. */ - if (ISNOTNULLPTR(taskCursor)) { - ret = taskCursor->timerPeriod; + /* Assert if HeapCheck() fails on the health check of the heap OR if + the task pointer's entry cannot be found in the heap. */ + SYSASSERT(RETURN_SUCCESS == HeapCheck(HEAP_CHECK_HEALTH_AND_POINTER, task_)); + + + /* Check if HeapCheck() passes on the health check and + the task pointer's entry can be found in the heap. */ + if (RETURN_SUCCESS == HeapCheck(HEAP_CHECK_HEALTH_AND_POINTER, task_)) { + + + taskCursor = taskList->head; + + /* Traverse the heap to find the task in the task list. */ + while ((ISNOTNULLPTR(taskCursor)) && (taskCursor != task_)) { + + taskCursor = taskCursor->next; + } + + + /* Assert if the task cannot be found. */ + SYSASSERT(ISNOTNULLPTR(taskCursor)); + + + /* Check if the task was found, if so then + return success! */ + if (ISNOTNULLPTR(taskCursor)) { + + + ret = RETURN_SUCCESS; + } } } return ret; } + + + /* The xTaskResetTimer() system call will reset the task timer. xTaskResetTimer() does not change the timer period or the task state when called. See xTaskChangePeriod() for more details on task timers. */ void xTaskResetTimer(Task_t *task_) { + + + + /* Assert if the task cannot be found. */ + SYSASSERT(RETURN_SUCCESS == TaskListFindTask(task_)); + + + /* Check if the task was found. */ + if (RETURN_SUCCESS == TaskListFindTask(task_)) { + + task_->timerStartTime = CURRENTTIME(); + } + + return; +} + + + + +/* The xTaskStartScheduler() system call passes control to the HeliOS scheduler. */ +void xTaskStartScheduler(void) { + + + + Task_t *runTask = NULL; + Task_t *taskCursor = NULL; - /* Check if the task list is not null and the task parameter is not null. */ - if ((ISNOTNULLPTR(taskList)) && (ISNOTNULLPTR(task_))) { - taskCursor = taskList->head; + /* Underflow unsigned least runtime to get maximum value */ + Time_t leastRunTime = -1; - /* While the task cursor is not null and the task cursor is not equal - to the task being searched for. */ - while ((ISNOTNULLPTR(taskCursor)) && (taskCursor != task_)) { - taskCursor = taskCursor->next; - } + /* Disable interrupts and set the critical section flag before entering into the scheduler main + loop. */ + + + /* Assert if the scheduler is already running. */ + SYSASSERT(false == SYSFLAG_RUNNING()); + + + /* Assert if the task list has not been initialized. */ + SYSASSERT(ISNOTNULLPTR(taskList)); + + + /* Check that the scheduler is not already running and + that the task list is initialized. */ + if ((false == SYSFLAG_RUNNING()) && (ISNOTNULLPTR(taskList))) { + + + + /* Now set the scheduler system flag to running because + the scheduler IS running. */ + SYSFLAG_RUNNING() = true; + + + + /* Continue to loop while the scheduler running flag is true. */ + while (SchedulerStateRunning == schedulerState) { - /* Check if the task cursor is not null, if so set the start time to - the current time. */ - if (ISNOTNULLPTR(taskCursor)) { - taskCursor->timerStartTime = CURRENTTIME(); + + /* If the runtime overflow flag is true. Reset the runtimes on all of the tasks. */ + if (SYSFLAG_OVERFLOW()) { + + RunTimeReset(); + } + + + taskCursor = taskList->head; + + /* While the task cursor is not null (i.e., there are further tasks in the task list). */ + while (ISNOTNULLPTR(taskCursor)) { + + + /* If the task pointed to by the task cursor is waiting and it has a notification waiting, then execute it. */ + if ((TaskStateWaiting == taskCursor->state) && (zero < taskCursor->notificationBytes)) { + + TaskRun(taskCursor); + + /* If the task pointed to by the task cursor is waiting and its timer has expired, then execute it. */ + } else if ((TaskStateWaiting == taskCursor->state) && (zero < taskCursor->timerPeriod) && ((CURRENTTIME() - taskCursor->timerStartTime) > taskCursor->timerPeriod)) { + + + TaskRun(taskCursor); + + taskCursor->timerStartTime = CURRENTTIME(); + + /* If the task pointed to by the task cursor is running and it's total runtime is less than the + least runtime from previous tasks, then set the run task pointer to the task cursor. This logic + is used to achieve the runtime balancing. */ + } else if ((TaskStateRunning == taskCursor->state) && (leastRunTime > taskCursor->totalRunTime)) { + + + leastRunTime = taskCursor->totalRunTime; + + runTask = taskCursor; + + } else { + /* Nothing to do here.. Just for MISRA C:2012 compliance. */ + } + + taskCursor = taskCursor->next; + } + + + /* If the run task pointer is not null, then there is a running tasks to execute. */ + if (ISNOTNULLPTR(runTask)) { + + TaskRun(runTask); + + runTask = NULL; + } + + leastRunTime = -1; } + + + SYSFLAG_RUNNING() = false; + + + } + + return; +} + + + + +/* If the runtime overflow flag is set, then RunTimeReset() is called to reset all of the +total runtimes on tasks to their last runtime. */ +void RunTimeReset(void) { + + + Task_t *taskCursor = NULL; + + + + taskCursor = taskList->head; + + /* While the task cursor is not null (i.e., there are further tasks in the task list). */ + while (ISNOTNULLPTR(taskCursor)) { + + + taskCursor->totalRunTime = taskCursor->lastRunTime; + + taskCursor = taskCursor->next; + } + + SYSFLAG_OVERFLOW() = false; + + return; +} + + + + +/* Used only for when testing HeliOS on Linux, then get the time from clock_gettime(). */ +Time_t CurrentTime(void) { + +#if defined(OTHER_ARCH_LINUX) + + struct timespec t; + + clock_gettime(CLOCK_MONOTONIC_RAW, &t); + + return (t.tv_sec * 1000000) + (t.tv_nsec / 1000); + +#else + + return zero; + +#endif +} + + + + +/* Called by the xTaskStartScheduler() system call, TaskRun() executes a task and updates all of its +runtime statistics. */ +void TaskRun(Task_t *task_) { + + + Time_t taskStartTime = zero; + + Time_t prevTotalRunTime = zero; + + /* Record the total runtime before executing the task. */ + prevTotalRunTime = task_->totalRunTime; + + /* Record the start time of the task. */ + taskStartTime = CURRENTTIME(); + + + + /* Call the task from its callback pointer. */ + (*task_->callback)(task_, task_->taskParameter); + + + /* Calculate the runtime and store it in last runtime. */ + task_->lastRunTime = CURRENTTIME() - taskStartTime; + + /* Add last runtime to the total runtime. */ + task_->totalRunTime += task_->lastRunTime; + + /* Check if the new total runtime is less than the previous total runtime, + if so an overflow has occurred so set the runtime over flow system flag. */ + if (task_->totalRunTime < prevTotalRunTime) { + + SYSFLAG_OVERFLOW() = true; } + + return; +} + + + + +/* The xTaskResumeAll() system call will set the scheduler system flag so the next +call to xTaskStartScheduler() will resume execute of all tasks. */ +void xTaskResumeAll(void) { + + + schedulerState = SchedulerStateRunning; + return; +} + + + + +/* The xTaskSuspendAll() system call will set the scheduler system flag so the scheduler +will stop and return. */ +void xTaskSuspendAll(void) { + + schedulerState = SchedulerStateSuspended; + + + return; +} + +/* The xTaskGetSchedulerState() system call will return the state of the scheduler. */ +SchedulerState_t xTaskGetSchedulerState(void) { + + + return schedulerState; } \ No newline at end of file diff --git a/src/task.h b/src/task.h index bba4417..78c7a6f 100644 --- a/src/task.h +++ b/src/task.h @@ -1,8 +1,8 @@ /** * @file task.h * @author Manny Peterson (mannymsp@gmail.com) - * @brief Kernel sources for tasks and task management in HeliOS - * @version 0.3.0 + * @brief Kernel sources for task management + * @version 0.3.1 * @date 2022-01-31 * * @copyright @@ -31,7 +31,7 @@ #include "types.h" #include "mem.h" #include "queue.h" -#include "sched.h" +#include "sys.h" #include "timer.h" #ifdef __cplusplus @@ -46,10 +46,10 @@ TaskRunTimeStats_t *xTaskGetAllRunTimeStats(Base_t *tasks_); TaskRunTimeStats_t *xTaskGetTaskRunTimeStats(Task_t *task_); Base_t xTaskGetNumberOfTasks(void); TaskInfo_t *xTaskGetTaskInfo(Task_t *task_); +TaskInfo_t *xTaskGetAllTaskInfo(Base_t *tasks_); TaskState_t xTaskGetTaskState(Task_t *task_); char *xTaskGetName(Task_t *task_); Base_t xTaskGetId(Task_t *task_); -char *xTaskList(void); void xTaskNotifyStateClear(Task_t *task_); Base_t xTaskNotificationIsWaiting(Task_t *task_); Base_t xTaskNotifyGive(Task_t *task_, Base_t notificationBytes_, const char *notificationValue_); @@ -60,6 +60,14 @@ void xTaskWait(Task_t *task_); void xTaskChangePeriod(Task_t *task_, Time_t timerPeriod_); Time_t xTaskGetPeriod(Task_t *task_); void xTaskResetTimer(Task_t *task_); +void xTaskStartScheduler(void); +void RunTimeReset(void); +Time_t CurrentTime(void); +void TaskRun(Task_t *task_); +void xTaskResumeAll(void); +void xTaskSuspendAll(void); +SchedulerState_t xTaskGetSchedulerState(void); +Base_t TaskListFindTask(const Task_t *task_); #ifdef __cplusplus } // extern "C" { diff --git a/src/timer.c b/src/timer.c index bf85e49..d9bd592 100644 --- a/src/timer.c +++ b/src/timer.c @@ -1,8 +1,8 @@ /** * @file timer.c * @author Manny Peterson (mannymsp@gmail.com) - * @brief Kernel sources for timers and timer management in HeliOS - * @version 0.3.0 + * @brief Kernel sources for timers + * @version 0.3.1 * @date 2022-01-31 * * @copyright @@ -26,11 +26,19 @@ #include "timer.h" + + + extern SysFlags_t sysFlags; -extern TaskList_t *taskList; + + + /* Declare and initialize the task list to null. */ -static TimerList_t *timerList = NULL; +TimerList_t *timerList = NULL; + + + /* The xTimerCreate() system call will create a new timer. Timers differ from task timers in that they do not create events that effect the scheduling of a task. @@ -39,20 +47,47 @@ static TimerList_t *timerList = NULL; be freed by xTimerDelete(). Unlike tasks, timers may be created and deleted within tasks. */ Timer_t *xTimerCreate(Time_t timerPeriod_) { + + Timer_t *ret = NULL; Timer_t *timerCursor = NULL; - /* Check if the timer list is null, if it is create it. */ + + /* Check if the timer list has been initialized. */ if (ISNULLPTR(timerList)) { + + /* Going to create a kernel object so enter privileged mode. */ + ENTER_PRIVILEGED(); + timerList = (TimerList_t *)xMemAlloc(sizeof(TimerList_t)); } + + + /* Assert if xMemAlloc() didn't do its job. */ + SYSASSERT(ISNOTNULLPTR(timerList)); + + + /* Check if xMemAlloc() did its job. */ if (ISNOTNULLPTR(timerList)) { + + + /* Going to create a kernel object so enter privileged mode. */ + ENTER_PRIVILEGED(); + + ret = (Timer_t *)xMemAlloc(sizeof(Task_t)); + + /* Assert if xMemAlloc() didn't do its job. */ + SYSASSERT(ISNOTNULLPTR(ret)); + + /* Check if xMemAlloc() successfully allocated the memory for the timer. */ if (ISNOTNULLPTR(ret)) { - ret->state = TimerStateStopped; + + + ret->state = TimerStateSuspended; ret->timerPeriod = timerPeriod_; @@ -62,16 +97,21 @@ Timer_t *xTimerCreate(Time_t timerPeriod_) { timerCursor = timerList->head; + /* Check if the head of the timer list is null. If so, iterate through the timer list to find the end otherwise just append the timer to the timer list. */ if (ISNOTNULLPTR(timerList->head)) { + /* While the next timer is not null. */ while (ISNOTNULLPTR(timerCursor->next)) { + timerCursor = timerCursor->next; } timerCursor->next = ret; + } else { + timerList->head = ret; } @@ -82,15 +122,29 @@ Timer_t *xTimerCreate(Time_t timerPeriod_) { return ret; } + + + /* The xTimerDelete() system call will delete a timer. For more information on timers see the xTaskTimerCreate() system call. */ void xTimerDelete(Timer_t *timer_) { Timer_t *timerCursor = NULL; + + Timer_t *timerPrevious = NULL; - /* Check if the timer list is not null and the timer parameter is not null. */ - if ((ISNOTNULLPTR(timerList)) && (ISNOTNULLPTR(timer_))) { + + /* Assert if the timer cannot be found in the timer + list. */ + SYSASSERT(RETURN_SUCCESS == TimerListFindTimer(timer_)); + + + /* Check if the timer was found in the timer list. */ + if (RETURN_SUCCESS == TimerListFindTimer(timer_)) { + + + timerCursor = timerList->head; timerPrevious = NULL; @@ -98,26 +152,49 @@ void xTimerDelete(Timer_t *timer_) { /* Check if the timer cursor is not null a if the timer cursor equals the timer parameter. */ if ((ISNOTNULLPTR(timerCursor)) && (timerCursor == timer_)) { + + timerList->head = timerCursor->next; + + /* Enter privileged mode to create a kernel object. */ + ENTER_PRIVILEGED(); + xMemFree(timerCursor); timerList->length--; } else { + + /* While the timer cursor is not null and the timer cursor is not equal to the timer parameter, continue to scan the timer list. */ while ((ISNOTNULLPTR(timerCursor)) && (timerCursor != timer_)) { + + timerPrevious = timerCursor; timerCursor = timerCursor->next; } - /* If the timer cursor is not null, then remove the timer - from the list and free its memory. */ + + /* Assert if the timer is not found though this + shouldn't ever happen. */ + SYSASSERT(ISNOTNULLPTR(timerCursor)); + + + /* Check if the timer was found, if so drop it from + the timer list and free its memory. */ if (ISNOTNULLPTR(timerCursor)) { + + timerPrevious->next = timerCursor->next; + + /* Going to free a kernel object so enter privileged + mode. */ + ENTER_PRIVILEGED(); + xMemFree(timerCursor); timerList->length--; @@ -125,186 +202,256 @@ void xTimerDelete(Timer_t *timer_) { } } + return; } + + + /* The xTimerChangePeriod() system call will change the period of the specified timer. The timer period is measured in microseconds. If the timer period is zero, the xTimerHasTimerExpired() system call will always return false. */ void xTimerChangePeriod(Timer_t *timer_, Time_t timerPeriod_) { - Timer_t *timerCursor = NULL; - /* Check if the timer list is not null, the timer parameter is not null and the timer period - is zero or greater. */ - if ((ISNOTNULLPTR(timerList)) && (ISNOTNULLPTR(timer_))) { - timerCursor = timerList->head; - /* While timer cursor is not null and the timer cursor does not equal the timer - parameter, keep scanning the timer list. */ - while ((ISNOTNULLPTR(timerCursor)) && (timerCursor != timer_)) { - timerCursor = timerCursor->next; - } - /* If the timer cursor is not null, then set the timer's period. */ - if (ISNOTNULLPTR(timerCursor)) { - timerCursor->timerPeriod = timerPeriod_; - } + /* Assert if the timer cannot be found in the timer list. */ + SYSASSERT(RETURN_SUCCESS == TimerListFindTimer(timer_)); + + + /* Check if the timer was found in the timer list. */ + if (RETURN_SUCCESS == TimerListFindTimer(timer_)) { + + timer_->timerPeriod = timerPeriod_; } + return; } + + + /* The xTimerGetPeriod() system call will return the current timer period for the specified timer. */ Time_t xTimerGetPeriod(Timer_t *timer_) { + Time_t ret = zero; - Timer_t *timerCursor = NULL; - /* Check if the timer list is not null and the timer parameter is not null. */ - if ((ISNOTNULLPTR(timerList)) && (ISNOTNULLPTR(timer_))) { - timerCursor = timerList->head; + /* Assert if the timer cannot be found in the timer list. */ + SYSASSERT(RETURN_SUCCESS == TimerListFindTimer(timer_)); - /* While timer cursor is not null and the timer cursor does not equal the timer - parameter, keep scanning the timer list. */ - while ((ISNOTNULLPTR(timerCursor)) && (timerCursor != timer_)) { - timerCursor = timerCursor->next; - } + /* Check if the timer was found in the timer list. */ + if (RETURN_SUCCESS == TimerListFindTimer(timer_)) { - /* If the timer cursor is not null, then set the return value to the timer's - period. */ - if (ISNOTNULLPTR(timerCursor)) { - ret = timerCursor->timerPeriod; - } + ret = timer_->timerPeriod; } + + return ret; } + + + /* The xTimerIsTimerActive() system call will return true of the timer has been started with xTimerStart(). */ Base_t xTimerIsTimerActive(Timer_t *timer_) { + + Base_t ret = false; - Timer_t *timerCursor = NULL; - /* Check if the timer list is not null and the timer parameter is not null. */ - if ((ISNOTNULLPTR(timerList)) && (ISNOTNULLPTR(timer_))) { - timerCursor = timerList->head; - /* While timer cursor is not null and the timer cursor does not equal the timer - parameter, keep scanning the timer list. */ - while ((ISNOTNULLPTR(timerCursor)) && (timerCursor != timer_)) { - timerCursor = timerCursor->next; - } + /* Assert if the timer cannot be found in the timer list. */ + SYSASSERT(RETURN_SUCCESS == TimerListFindTimer(timer_)); - /* If the timer cursor is not null, then check if the timer is running. */ - if (ISNOTNULLPTR(timerCursor)) { - if (timerCursor->state == TimerStateRunning) { - ret = true; - } + /* Check if the timer was found in the timer list. */ + if (RETURN_SUCCESS == TimerListFindTimer(timer_)) { + + /* Check if the timer state is running, if so + return true. */ + if (TimerStateRunning == timer_->state) { + + ret = true; } } + return ret; } + + + /* The xTimerHasTimerExpired() system call will return true or false dependent on whether the timer period for the specified timer has elapsed. xTimerHasTimerExpired() will NOT reset the timer. Timers must be reset with xTimerReset(). */ Base_t xTimerHasTimerExpired(Timer_t *timer_) { + + Base_t ret = false; - Timer_t *timerCursor = NULL; - /* Check if the timer list is not null and the timer parameter is not null. */ - if ((ISNOTNULLPTR(timerList)) && (ISNOTNULLPTR(timer_))) { - timerCursor = timerList->head; + /* Assert if the timer cannot be found in the timer list. */ + SYSASSERT(RETURN_SUCCESS == TimerListFindTimer(timer_)); - /* While timer cursor is not null and the timer cursor does not equal the timer - parameter, keep scanning the timer list. */ - while ((ISNOTNULLPTR(timerCursor)) && (timerCursor != timer_)) { - timerCursor = timerCursor->next; - } - /* If the timer cursor is not null, check to see if the timer has expired. */ - if (ISNOTNULLPTR(timerCursor)) { - /* If the state is running, timer period is greater than zero and if the elapsed time - is equal to or greater than the timer period, return true. */ - if ((timerCursor->state == TimerStateRunning) && (timerCursor->timerPeriod > zero) && ((CURRENTTIME() - timerCursor->timerStartTime) > timerCursor->timerPeriod)) { - ret = true; - } + /* Check if the timer was found in the timer list. */ + if (RETURN_SUCCESS == TimerListFindTimer(timer_)) { + + + /* Assert if the timer isn't running, it must be for the + timer to expire. */ + SYSASSERT(TimerStateRunning == timer_->state); + + + + /* Assert if the timer period is zero, it must be greater + than zero for the timer to expire. */ + SYSASSERT(zero < timer_->timerPeriod); + + + /* The timer should be running, the timer period should be + greater than zero and the elapsed time is greater than + the timer period. If so, then return true. */ + if ((TimerStateRunning == timer_->state) && (zero < timer_->timerPeriod) && ((CURRENTTIME() - timer_->timerStartTime) > timer_->timerPeriod)) { + + ret = true; } } + return ret; } + + + /* The xTimerReset() system call will reset the start time of the timer to zero. */ void xTimerReset(Timer_t *timer_) { - Timer_t *timerCursor = NULL; - /* Check if the timer list is not null and the timer parameter is not null. */ - if ((ISNOTNULLPTR(timerList)) && (ISNOTNULLPTR(timer_))) { - timerCursor = timerList->head; - /* While timer cursor is not null and the timer cursor does not equal the timer - parameter, keep scanning the timer list. */ - while ((ISNOTNULLPTR(timerCursor)) && (timerCursor != timer_)) { - timerCursor = timerCursor->next; - } - /* If the timer cursor is not null, then set the timer to the current time. */ - if (ISNOTNULLPTR(timerCursor)) { - timerCursor->timerStartTime = CURRENTTIME(); - } + /* Assert if the timer cannot be found in the timer list. */ + SYSASSERT(RETURN_SUCCESS == TimerListFindTimer(timer_)); + + + + /* Check if the timer was found in the timer list. */ + if (RETURN_SUCCESS == TimerListFindTimer(timer_)) { + + timer_->timerStartTime = CURRENTTIME(); } return; } + + + /* The xTimerStart() system call will place the timer in the running state. Neither xTaskStart() nor xTaskStop() will reset the timer. Timers can only be reset with xTimerReset(). */ void xTimerStart(Timer_t *timer_) { - Timer_t *timerCursor = NULL; - /* Check if the timer list is not null and the timer parameter is not null. */ - if ((ISNOTNULLPTR(timerList)) && (ISNOTNULLPTR(timer_))) { - timerCursor = timerList->head; - /* While timer cursor is not null and the timer cursor does not equal the timer - parameter, keep scanning the timer list. */ - while ((ISNOTNULLPTR(timerCursor)) && (timerCursor != timer_)) { - timerCursor = timerCursor->next; - } - /* If the timer cursor is not null, then set the state of the timer to running. */ - if (ISNOTNULLPTR(timerCursor)) { - timerCursor->state = TimerStateRunning; - } + /* Assert if the timer cannot be found in the timer list. */ + SYSASSERT(RETURN_SUCCESS == TimerListFindTimer(timer_)); + + + + /* Check if the timer was found in the timer list. */ + if (RETURN_SUCCESS == TimerListFindTimer(timer_)) { + + timer_->state = TimerStateRunning; } + return; } + + + /* The xTimerStop() system call will place the timer in the stopped state. Neither xTaskStart() nor xTaskStop() will reset the timer. Timers can only be reset with xTimerReset(). */ void xTimerStop(Timer_t *timer_) { + + + + /* Assert if the timer cannot be found in the timer list. */ + SYSASSERT(RETURN_SUCCESS == TimerListFindTimer(timer_)); + + + /* Check if the timer was found in the timer list. */ + if (RETURN_SUCCESS == TimerListFindTimer(timer_)) { + + timer_->state = TimerStateSuspended; + } + + + return; +} + + + + +/* TimerListFindTimer() is used to search the timer list for a +timer and returns RETURN_SUCCESS if the timer is found. It also +always checks the health of the heap by calling HeapCheck(). */ +Base_t TimerListFindTimer(const Timer_t *timer_) { + + + Base_t ret = RETURN_FAILURE; + + Timer_t *timerCursor = NULL; - /* Check if the timer list is not null and the timer parameter is not null. */ + + /* Assert if the timer list is not initialized. */ + SYSASSERT(ISNOTNULLPTR(timerList)); + + + /* Assert if the timer paramater is null. */ + SYSASSERT(ISNOTNULLPTR(timer_)); + + + /* Check if the timer list is initialized and the timer pointer + is not null. */ if ((ISNOTNULLPTR(timerList)) && (ISNOTNULLPTR(timer_))) { - timerCursor = timerList->head; - /* While timer cursor is not null and the timer cursor does not equal the timer - parameter, keep scanning the timer list. */ - while ((ISNOTNULLPTR(timerCursor)) && (timerCursor != timer_)) { - timerCursor = timerCursor->next; - } - /* If the timer cursor is not null, set the state of the timer to stopped. */ - if (ISNOTNULLPTR(timerCursor)) { - timerCursor->state = TimerStateStopped; + /* Assert if the HeapCheck() fails on the health check or is unable + to find the entry for the heap pointer. */ + SYSASSERT(RETURN_SUCCESS == HeapCheck(HEAP_CHECK_HEALTH_AND_POINTER, timer_)); + + + /* Check if HeapCheck() was successful. */ + if (RETURN_SUCCESS == HeapCheck(HEAP_CHECK_HEALTH_AND_POINTER, timer_)) { + + timerCursor = timerList->head; + + + /* Traverse the timer list while there is a timer + and the timer is not the timer we are looking for. */ + while ((ISNOTNULLPTR(timerCursor)) && (timerCursor != timer_)) { + + timerCursor = timerCursor->next; + } + + + /* Assert if the timer was never found. */ + SYSASSERT(ISNOTNULLPTR(timerCursor)); + + /* Check if the timer was found. */ + if (ISNOTNULLPTR(timerCursor)) { + + ret = RETURN_SUCCESS; + } } } - return; + return ret; } \ No newline at end of file diff --git a/src/timer.h b/src/timer.h index bc6023c..22ed329 100644 --- a/src/timer.h +++ b/src/timer.h @@ -1,27 +1,27 @@ /** * @file timer.h * @author Manny Peterson (mannymsp@gmail.com) - * @brief Kernel sources for timers and timer management in HeliOS - * @version 0.3.0 + * @brief Kernel sources for timers + * @version 0.3.1 * @date 2022-01-31 - * + * * @copyright * HeliOS Embedded Operating System * Copyright (C) 2020-2022 Manny Peterson - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - * + * */ #ifndef TIMER_H_ #define TIMER_H_ @@ -31,7 +31,7 @@ #include "types.h" #include "mem.h" #include "queue.h" -#include "sched.h" +#include "sys.h" #include "task.h" #ifdef __cplusplus @@ -47,6 +47,7 @@ Base_t xTimerHasTimerExpired(Timer_t *timer_); void xTimerReset(Timer_t *timer_); void xTimerStart(Timer_t *timer_); void xTimerStop(Timer_t *timer_); +Base_t TimerListFindTimer(const Timer_t *timer_); #ifdef __cplusplus } // extern "C" { diff --git a/src/types.h b/src/types.h index 025018a..9b34924 100644 --- a/src/types.h +++ b/src/types.h @@ -1,8 +1,8 @@ /** * @file types.h * @author Manny Peterson (mannymsp@gmail.com) - * @brief Kernel header for all kernel type definitions in HeliOS - * @version 0.3.0 + * @brief Kernel header for kernel type definitions + * @version 0.3.1 * @date 2022-01-31 * * @copyright @@ -35,9 +35,15 @@ typedef enum { TaskStateWaiting } TaskState_t; +typedef enum { + SchedulerStateError, + SchedulerStateSuspended, + SchedulerStateRunning +} SchedulerState_t; + typedef enum { TimerStateError, - TimerStateStopped, + TimerStateSuspended, TimerStateRunning } TimerState_t; @@ -78,6 +84,7 @@ typedef struct Task_s { } Task_t; typedef struct TaskRunTimeStats_s { + Base_t id; Time_t lastRunTime; Time_t totalRunTime; } TaskRunTimeStats_t; @@ -110,10 +117,9 @@ typedef struct TimerList_s { typedef struct SysFlags_s { Byte_t running : 1; - Byte_t critical : 1; Byte_t overflow : 1; - Byte_t protect : 1; - Byte_t reserved : 4; + Byte_t privileged : 1; + Byte_t reserved : 5; } SysFlags_t; typedef struct QueueMessage_s { @@ -135,7 +141,7 @@ typedef struct Queue_s { } Queue_t; typedef struct SystemInfo_s { - char productName[PRODUCTNAME_SIZE]; + char productName[OS_PRODUCT_NAME_SIZE]; Base_t majorVersion; Base_t minorVersion; Base_t patchVersion;