217 lines
5.5 KiB
C
217 lines
5.5 KiB
C
/*
|
|
* Copyright (C) 2016 The Android Open Source Project
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include <platform.h>
|
|
#include <eventQ.h>
|
|
#include <stddef.h>
|
|
#include <timer.h>
|
|
#include <stdio.h>
|
|
#include <heap.h>
|
|
#include <slab.h>
|
|
#include <cpu.h>
|
|
#include <util.h>
|
|
#include <plat/plat.h>
|
|
#include <plat/taggedPtr.h>
|
|
|
|
#define for_each_item_safe(head, pos, tmp) \
|
|
for (pos = (head)->next; tmp = (pos)->next, (pos) != (head); pos = (tmp))
|
|
|
|
struct EvtList
|
|
{
|
|
struct EvtList *next;
|
|
struct EvtList *prev;
|
|
};
|
|
|
|
struct EvtRecord {
|
|
struct EvtList item;
|
|
uint32_t evtType;
|
|
void* evtData;
|
|
TaggedPtr evtFreeData;
|
|
};
|
|
|
|
struct EvtQueue {
|
|
struct EvtList head;
|
|
struct SlabAllocator *evtsSlab;
|
|
EvtQueueForciblyDiscardEvtCbkF forceDiscardCbk;
|
|
};
|
|
|
|
static inline void __evtListDel(struct EvtList *prev, struct EvtList *next)
|
|
{
|
|
next->prev = prev;
|
|
prev->next = next;
|
|
}
|
|
|
|
static inline void evtListDel(struct EvtList *entry)
|
|
{
|
|
__evtListDel(entry->prev, entry->next);
|
|
entry->next = entry->prev = NULL;
|
|
}
|
|
|
|
struct EvtQueue* evtQueueAlloc(uint32_t size, EvtQueueForciblyDiscardEvtCbkF forceDiscardCbk)
|
|
{
|
|
struct EvtQueue *q = heapAlloc(sizeof(struct EvtQueue));
|
|
struct SlabAllocator *slab = slabAllocatorNew(sizeof(struct EvtRecord),
|
|
alignof(struct EvtRecord), size);
|
|
|
|
if (q && slab) {
|
|
q->forceDiscardCbk = forceDiscardCbk;
|
|
q->evtsSlab = slab;
|
|
q->head.next = &q->head;
|
|
q->head.prev = &q->head;
|
|
return q;
|
|
}
|
|
|
|
if (q)
|
|
heapFree(q);
|
|
if (slab)
|
|
slabAllocatorDestroy(slab);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void evtQueueFree(struct EvtQueue* q)
|
|
{
|
|
struct EvtList *pos, *tmp;
|
|
|
|
for_each_item_safe (&q->head, pos, tmp) {
|
|
struct EvtRecord * rec = container_of(pos, struct EvtRecord, item);
|
|
|
|
q->forceDiscardCbk(rec->evtType, rec->evtData, rec->evtFreeData);
|
|
slabAllocatorFree(q->evtsSlab, rec);
|
|
}
|
|
|
|
slabAllocatorDestroy(q->evtsSlab);
|
|
heapFree(q);
|
|
}
|
|
|
|
bool evtQueueEnqueue(struct EvtQueue* q, uint32_t evtType, void *evtData,
|
|
TaggedPtr evtFreeData, bool atFront)
|
|
{
|
|
struct EvtRecord *rec;
|
|
uint64_t intSta;
|
|
struct EvtList *item = NULL, *a, *b;
|
|
|
|
if (!q)
|
|
return false;
|
|
|
|
rec = slabAllocatorAlloc(q->evtsSlab);
|
|
if (!rec) {
|
|
struct EvtList *pos;
|
|
|
|
intSta = cpuIntsOff();
|
|
//find a victim for discarding
|
|
for (pos = q->head.next; pos != &q->head; pos = pos->next) {
|
|
rec = container_of(pos, struct EvtRecord, item);
|
|
if (!(rec->evtType & EVENT_TYPE_BIT_DISCARDABLE))
|
|
continue;
|
|
q->forceDiscardCbk(rec->evtType, rec->evtData, rec->evtFreeData);
|
|
evtListDel(pos);
|
|
item = pos;
|
|
}
|
|
cpuIntsRestore (intSta);
|
|
} else {
|
|
item = &rec->item;
|
|
}
|
|
|
|
if (!item)
|
|
return false;
|
|
|
|
item->prev = item->next = NULL;
|
|
|
|
rec->evtType = evtType;
|
|
rec->evtData = evtData;
|
|
rec->evtFreeData = evtFreeData;
|
|
|
|
intSta = cpuIntsOff();
|
|
|
|
if (unlikely(atFront)) {
|
|
b = q->head.next;
|
|
a = b->prev;
|
|
} else {
|
|
a = q->head.prev;
|
|
b = a->next;
|
|
}
|
|
|
|
a->next = item;
|
|
item->prev = a;
|
|
b->prev = item;
|
|
item->next = b;
|
|
|
|
cpuIntsRestore(intSta);
|
|
platWake();
|
|
return true;
|
|
}
|
|
|
|
void evtQueueRemoveAllMatching(struct EvtQueue* q,
|
|
bool (*match)(uint32_t evtType, const void *data, void *context),
|
|
void *context)
|
|
{
|
|
uint64_t intSta = cpuIntsOff();
|
|
struct EvtList *pos, *tmp;
|
|
|
|
for_each_item_safe (&q->head, pos, tmp) {
|
|
struct EvtRecord * rec = container_of(pos, struct EvtRecord, item);
|
|
|
|
if (match(rec->evtType, rec->evtData, context)) {
|
|
q->forceDiscardCbk(rec->evtType, rec->evtData, rec->evtFreeData);
|
|
evtListDel(pos);
|
|
slabAllocatorFree(q->evtsSlab, rec);
|
|
}
|
|
}
|
|
cpuIntsRestore(intSta);
|
|
}
|
|
|
|
bool evtQueueDequeue(struct EvtQueue* q, uint32_t *evtTypeP, void **evtDataP,
|
|
TaggedPtr *evtFreeDataP, bool sleepIfNone)
|
|
{
|
|
struct EvtRecord *rec = NULL;
|
|
uint64_t intSta;
|
|
|
|
while(1) {
|
|
struct EvtList *pos;
|
|
intSta = cpuIntsOff();
|
|
|
|
pos = q->head.next;
|
|
if (pos != &q->head) {
|
|
rec = container_of(pos, struct EvtRecord, item);
|
|
evtListDel(pos);
|
|
break;
|
|
}
|
|
else if (!sleepIfNone)
|
|
break;
|
|
else if (!timIntHandler()) {
|
|
// check for timers
|
|
// if any fire, do not sleep (since by the time callbacks run, more might be due)
|
|
platSleep();
|
|
//first thing when awake: check timers again
|
|
timIntHandler();
|
|
}
|
|
cpuIntsRestore(intSta);
|
|
}
|
|
|
|
cpuIntsRestore(intSta);
|
|
|
|
if (!rec)
|
|
return false;
|
|
|
|
*evtTypeP = rec->evtType;
|
|
*evtDataP = rec->evtData;
|
|
*evtFreeDataP = rec->evtFreeData;
|
|
slabAllocatorFree(q->evtsSlab, rec);
|
|
|
|
return true;
|
|
}
|