mirror of https://github.com/ARMmbed/mbed-os.git
187 lines
5.7 KiB
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;
|
|
}
|