| /* |
| * 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; |
| } |