projects/freertos/event_task_freertos.c (179 lines of code) (raw):
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include "event_task_freertos.h"
int event_task_freertos_lock (const struct event_task *task)
{
const struct event_task_freertos *freertos = (const struct event_task_freertos*) task;
if (freertos == NULL) {
return EVENT_TASK_INVALID_ARGUMENT;
}
return platform_mutex_lock (&freertos->state->lock);
}
int event_task_freertos_unlock (const struct event_task *task)
{
const struct event_task_freertos *freertos = (const struct event_task_freertos*) task;
if (freertos == NULL) {
return EVENT_TASK_INVALID_ARGUMENT;
}
return platform_mutex_unlock (&freertos->state->lock);
}
int event_task_freertos_get_event_context (const struct event_task *task,
struct event_task_context **context)
{
const struct event_task_freertos *freertos = (const struct event_task_freertos*) task;
int status;
if ((freertos == NULL) || (context == NULL)) {
return EVENT_TASK_INVALID_ARGUMENT;
}
if (freertos->state->task) {
platform_mutex_lock (&freertos->state->lock);
if (!freertos->state->notifying && (freertos->state->running < 0)) {
freertos->state->notifying = true;
*context = &freertos->state->context;
status = 0;
}
else {
platform_mutex_unlock (&freertos->state->lock);
status = EVENT_TASK_BUSY;
}
}
else {
status = EVENT_TASK_NO_TASK;
}
return status;
}
int event_task_freertos_notify (const struct event_task *task,
const struct event_task_handler *handler)
{
const struct event_task_freertos *freertos = (const struct event_task_freertos*) task;
int status;
if (task == NULL) {
return EVENT_TASK_INVALID_ARGUMENT;
}
if (freertos->state->task) {
if (freertos->state->running < 0) {
if (freertos->state->notifying) {
/* Make sure the requested handler is registered with the task. */
status = event_task_find_handler (handler, freertos->handlers,
freertos->num_handlers);
if (!ROT_IS_ERROR (status)) {
freertos->state->running = status;
status = 0;
}
freertos->state->notifying = false;
platform_mutex_unlock (&freertos->state->lock);
if (freertos->state->running >= 0) {
/* If the handler is valid, notify the task to process the event. */
xTaskNotifyGive (freertos->state->task);
}
}
else {
status = EVENT_TASK_NOT_READY;
}
}
else {
status = EVENT_TASK_BUSY;
}
}
else {
status = EVENT_TASK_NO_TASK;
}
return status;
}
/**
* Initialize an event handler task. The actual FreeRTOS task will not be allocated until a call to
* one of the task allocation functions is made.
*
* @param task The event handler task to initialize.
* @param state Variable context for the task. This must be uninitialized.
* @param system The manager for system operations.
* @param handlers The list of event handlers that can be used with this task instance.
* @param num_handlers The number of event handlers in the list.
*
* @return 0 if the task was initialized or an error code
*/
int event_task_freertos_init (struct event_task_freertos *task,
struct event_task_freertos_state *state, struct system *system,
const struct event_task_handler **handlers, size_t num_handlers)
{
if (task == NULL) {
return EVENT_TASK_INVALID_ARGUMENT;
}
memset (task, 0, sizeof (struct event_task_freertos));
task->base.lock = event_task_freertos_lock;
task->base.unlock = event_task_freertos_unlock;
task->base.get_event_context = event_task_freertos_get_event_context;
task->base.notify = event_task_freertos_notify;
task->state = state;
task->system = system;
task->handlers = handlers;
task->num_handlers = num_handlers;
return event_task_freertos_init_state (task);
}
/**
* Initialize only the variable state for an event handler task. The rest of the task instance is
* assumed to have already been initialized. The actual FreeRTOS task will not be allocated until a
* call to one of the task allocation functions is made.
*
* This would generally be used with a statically initialized instance.
*
* @param task The task instance that contains the state to initialize.
*
* @return 0 if the state was successfully initialized or an error code.
*/
int event_task_freertos_init_state (const struct event_task_freertos *task)
{
if ((task == NULL) || (task->state == NULL) || (task->system == NULL) ||
(task->handlers == NULL) || (task->num_handlers == 0)) {
return EVENT_TASK_INVALID_ARGUMENT;
}
memset (task->state, 0, sizeof (struct event_task_freertos_state));
/* Leave the running handler set to 0 initially. This will get cleared after the handlers have
* been initialized for execution within the task context. */
return platform_mutex_init (&task->state->lock);
}
/**
* Stop the event task and release all resources used by the task. No handlers will be released.
*
* There is no synchronization done to ensure a task is only stopped when nothing is running. A
* released task will be stopped immediately.
*
* @param task The task to release.
*/
void event_task_freertos_release (const struct event_task_freertos *task)
{
if (task) {
vTaskDelete (task->state->task);
platform_mutex_free (&task->state->lock);
}
}
/**
* Task routine to handle notifications for registered handlers.
*
* @param task The task to process event notifications.
*/
static void event_task_freertos_process_notification (const struct event_task_freertos *task)
{
bool reset = false;
/* Wait for the task to be started before executing anything in the task context. */
ulTaskNotifyTake (pdTRUE, portMAX_DELAY);
event_task_prepare_handlers (task->handlers, task->num_handlers);
/* Indicate that the handlers have been initialized and the task is ready to process
* notifications. */
platform_mutex_lock (&task->state->lock);
task->state->running = -1;
platform_mutex_unlock (&task->state->lock);
while (1) {
/* Wait for notification that an event should be processed. */
ulTaskNotifyTake (pdTRUE, portMAX_DELAY);
/* Sanity check the handler index before using it. */
if ((task->state->running >= 0) && ((size_t) task->state->running < task->num_handlers)) {
/* Execute the selected handler for the event. */
task->handlers[task->state->running]->execute (task->handlers[task->state->running],
&task->state->context, &reset);
}
if (reset) {
/* If the event requires it, reset the system. We need to wait a bit before triggering
* the reset to allow time for any execution status to be reported. */
platform_msleep (5000);
system_reset (task->system);
reset = false; /* We should never get here, but clear the flag if the reset fails. */
}
/* Clear the running handler to be ready for the next event notification. */
platform_mutex_lock (&task->state->lock);
task->state->running = -1;
platform_mutex_unlock (&task->state->lock);
}
}
#if configSUPPORT_DYNAMIC_ALLOCATION == 1
/**
* Allocate the event handler task using dynamic allocation of task resources. The task will not be
* ready for processing events until {@link event_task_freertos_start} is called.
*
* @param task The event task to allocate.
* @param stack_words The size of the task stack. The stack size is measured in words.
* @param task_name An identifying name to assign to the task. The maximum length is determined by
* the FreeRTOS configuration for the platform.
* @param priority The priority to assign to this task.
*
* @return 0 if the task was allocated or an error code.
*/
int event_task_freertos_allocate (const struct event_task_freertos *task, uint16_t stack_words,
const char *task_name, int priority)
{
int status;
if (task == NULL) {
return EVENT_TASK_INVALID_ARGUMENT;
}
status = xTaskCreate ((TaskFunction_t) event_task_freertos_process_notification, task_name,
stack_words, (void*) task, priority, &task->state->task);
if (status != pdPASS) {
task->state->task = NULL;
return EVENT_TASK_NO_MEMORY;
}
return 0;
}
#endif
#if configSUPPORT_STATIC_ALLOCATION == 1
/**
* Allocate the event handler task using static allocation of task resources. The task will not be
* ready for processing events until {@link event_task_freertos_start} is called.
*
* @param task The event task to allocate.
* @param context The statically allocated FreeRTOS context for the task.
* @param stack A buffer to use for the task's stack.
* @param stack_words The number of words in the stack buffer.
* @param task_name An identifying name to assign to the task. The maximum length is determined by
* the FreeRTOS configuration for the platform.
* @param priority The priority to assign to this task.
*
* @return 0 if the task was allocated or an error code.
*/
int event_task_freertos_allocate_static (const struct event_task_freertos *task,
StaticTask_t *context, StackType_t *stack, uint32_t stack_words, const char *task_name,
int priority)
{
if (task == NULL) {
return EVENT_TASK_INVALID_ARGUMENT;
}
task->state->task = xTaskCreateStatic (
(TaskFunction_t) event_task_freertos_process_notification, task_name, stack_words,
(void*) task, priority, stack, context);
if (task->state->task == NULL) {
return EVENT_TASK_INVALID_ARGUMENT;
}
return 0;
}
#endif
/**
* Start running an event handler task that was previously allocated. No events can be handled
* until the task has been started.
*
* @param task The event task to start. If this is null, nothing will be done.
*/
void event_task_freertos_start (const struct event_task_freertos *task)
{
if (task != NULL) {
xTaskNotifyGive (task->state->task);
}
}