mbed-os/connectivity/nfc/libraries/stack/platform/nfc_scheduler.c

187 lines
5.7 KiB
C

/*
* Copyright (c) 2015-2018, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* \file nfc_scheduler.c
* \copyright Copyright (c) ARM Ltd 2015
* \author Donatien Garnier
*/
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#define __DEBUG__ 0
#ifndef __MODULE__
#define __MODULE__ "nfc_scheduler.c"
#endif
#include "platform/nfc_scheduler.h"
void nfc_scheduler_init(nfc_scheduler_t *pScheduler, nfc_scheduler_timer_t *pTimer)
{
pScheduler->pNext = NULL;
pScheduler->pTimer = pTimer;
//Start timer
nfc_scheduler_timer_start(pTimer);
}
#define MAX_TIMEOUT UINT32_MAX
uint32_t nfc_scheduler_iteration(nfc_scheduler_t *pScheduler, uint32_t events)
{
while (true) {
nfc_task_t *pPrioTask = NULL;
nfc_task_t *pPrioTaskPrevious = NULL;
uint32_t prioTaskEvent = 0;
int64_t timeout;
nfc_task_t *pPreviousTask = NULL;
nfc_task_t *pTask = pScheduler->pNext;
if (pTask == NULL) {
NFC_DBG("Empty queue, %lu ms elapsed", nfc_scheduler_timer_get(pScheduler->pTimer));
//Empty queue, return
return MAX_TIMEOUT;
}
//Get timer value
uint32_t timeElapsed = nfc_scheduler_timer_get(pScheduler->pTimer);
NFC_DBG("%lu ms elapsed", timeElapsed);
nfc_scheduler_timer_reset(pScheduler->pTimer);
do {
//Apply timeouts
if (pTask->events & EVENT_TIMEOUT) {
pTask->timeout -= timeElapsed;
}
pPreviousTask = pTask;
pTask = pTask->pNext;
} while (pTask != NULL);
pTask = pScheduler->pNext;
pPreviousTask = NULL;
timeout = MAX_TIMEOUT;
do {
//Check which task should be woken up first
if ((events & EVENT_HW_INTERRUPT) && (pTask->events & EVENT_HW_INTERRUPT)) {
//Hardware interrupts have prio
pPrioTask = pTask;
pPrioTaskPrevious = pPreviousTask;
timeout = 0;
events &= ~EVENT_HW_INTERRUPT; //Only one task gets triggered per event
prioTaskEvent = EVENT_HW_INTERRUPT;
break;
} else if ((pTask->events & EVENT_TIMEOUT) && (pTask->timeout < timeout)) {
pPrioTask = pTask;
pPrioTaskPrevious = pPreviousTask;
timeout = pTask->timeout;
prioTaskEvent = EVENT_TIMEOUT;
}
pPreviousTask = pTask;
pTask = pTask->pNext;
} while (pTask != NULL);
if (pPrioTask == NULL) {
//No task to wake up, exit
NFC_DBG("No task to wake up");
return MAX_TIMEOUT;
}
if (timeout > 0) {
//No task to wake up yet
if (timeout > MAX_TIMEOUT) {
NFC_DBG("No task to wake up");
return MAX_TIMEOUT;
} else {
NFC_DBG("No task to wake up, wait %lu ms", timeout);
return timeout;
}
}
//Dequeue task
if (pPrioTaskPrevious == NULL) {
pScheduler->pNext = pPrioTask->pNext;
} else {
pPrioTaskPrevious->pNext = pPrioTask->pNext;
}
pPrioTask->pNext = NULL;
//Execute task
NFC_DBG("Calling task %p - events %02X", pPrioTask, prioTaskEvent);
pPrioTask->fn(prioTaskEvent, pPrioTask->pUserData);
events &= ~EVENT_HW_INTERRUPT; //Only one task gets triggered per event
}
return MAX_TIMEOUT;
}
void nfc_scheduler_queue_task(nfc_scheduler_t *pScheduler, nfc_task_t *pTask)
{
pTask->timeout = pTask->timeoutInitial + nfc_scheduler_timer_get(pScheduler->pTimer);
NFC_DBG("Queuing task %p: events %1X, timeout %lu ms", pTask, pTask->events, pTask->timeout);
//Find last task
nfc_task_t *pPrevTask = pScheduler->pNext;
pTask->pNext = NULL;
if (pPrevTask == NULL) {
pScheduler->pNext = pTask;
return;
}
while (pPrevTask->pNext != NULL) {
pPrevTask = pPrevTask->pNext;
}
pPrevTask->pNext = pTask;
}
void nfc_scheduler_dequeue_task(nfc_scheduler_t *pScheduler, bool abort, nfc_task_t *pTask)
{
NFC_DBG("Dequeuing task %p", pTask);
//Find task
nfc_task_t *pPrevTask = pScheduler->pNext;
if (pPrevTask == NULL) {
pTask->pNext = NULL;
return;
}
if (pPrevTask == pTask) {
if (abort) {
pTask->fn(EVENT_ABORTED, pTask->pUserData);
}
pScheduler->pNext = pTask->pNext;
pTask->pNext = NULL;
return;
}
while (pPrevTask->pNext != NULL) {
if (pPrevTask->pNext == pTask) {
if (abort) {
pTask->fn(EVENT_ABORTED, pTask->pUserData);
}
pPrevTask->pNext = pTask->pNext;
pTask->pNext = NULL;
return;
}
pPrevTask = pPrevTask->pNext;
}
pTask->pNext = NULL;
}
void task_init(nfc_task_t *pTask, uint32_t events, uint32_t timeout, nfc_task_fn fn, void *pUserData)
{
pTask->events = events;
pTask->timeoutInitial = timeout;
pTask->fn = fn;
pTask->pUserData = pUserData;
pTask->pNext = NULL;
}