aboutsummaryrefslogtreecommitdiff
path: root/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_event.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_event.c')
-rw-r--r--drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_event.c2590
1 files changed, 2590 insertions, 0 deletions
diff --git a/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_event.c b/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_event.c
new file mode 100644
index 00000000000..e9050f1ad7f
--- /dev/null
+++ b/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_event.c
@@ -0,0 +1,2590 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2011 by Vivante Corp.
+* Copyright (C) 2011 Freescale Semiconductor, Inc.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the license, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+*****************************************************************************/
+
+
+
+
+#include "gc_hal_kernel_precomp.h"
+#include "gc_hal_kernel_buffer.h"
+
+#ifdef __QNXNTO__
+#include <atomic.h>
+#include "gc_hal_kernel_qnx.h"
+#endif
+
+#ifdef LINUX
+#include <asm/atomic.h>
+#endif
+
+#define _GC_OBJ_ZONE gcvZONE_EVENT
+
+#define gcdEVENT_ALLOCATION_COUNT (4096 / gcmSIZEOF(gcsHAL_INTERFACE))
+#define gcdEVENT_MIN_THRESHOLD 4
+
+/******************************************************************************\
+********************************* Support Code *********************************
+\******************************************************************************/
+
+static gceSTATUS
+gckEVENT_AllocateQueue(
+ IN gckEVENT Event,
+ OUT gcsEVENT_QUEUE_PTR * Queue
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Event=0x%x", Event);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
+ gcmkVERIFY_ARGUMENT(Queue != gcvNULL);
+
+ /* Do we have free queues? */
+ if (Event->freeList == gcvNULL)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+
+ /* Move one free queue from the free list. */
+ * Queue = Event->freeList;
+ Event->freeList = Event->freeList->next;
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Queue=0x%x", gcmOPT_POINTER(Queue));
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+static gceSTATUS
+gckEVENT_FreeQueue(
+ IN gckEVENT Event,
+ OUT gcsEVENT_QUEUE_PTR Queue
+ )
+{
+ gceSTATUS status = gcvSTATUS_OK;
+
+ gcmkHEADER_ARG("Event=0x%x", Event);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
+ gcmkVERIFY_ARGUMENT(Queue != gcvNULL);
+
+ /* Move one free queue from the free list. */
+ Queue->next = Event->freeList;
+ Event->freeList = Queue;
+
+ /* Success. */
+ gcmkFOOTER();
+ return status;
+}
+
+static gceSTATUS
+gckEVENT_FreeRecord(
+ IN gckEVENT Event,
+ IN gcsEVENT_PTR Record
+ )
+{
+ gceSTATUS status;
+ gctBOOL acquired = gcvFALSE;
+
+ gcmkHEADER_ARG("Event=0x%x Record=0x%x", Event, Record);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
+ gcmkVERIFY_ARGUMENT(Record != gcvNULL);
+
+ /* Acquire the mutex. */
+ gcmkONERROR(gckOS_AcquireMutex(Event->os,
+ Event->freeEventMutex,
+ gcvINFINITE));
+ acquired = gcvTRUE;
+
+ /* Push the record on the free list. */
+ Record->next = Event->freeEventList;
+ Event->freeEventList = Record;
+ Event->freeEventCount += 1;
+
+ /* Release the mutex. */
+ gcmkONERROR(gckOS_ReleaseMutex(Event->os, Event->freeEventMutex));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Roll back. */
+ if (acquired)
+ {
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Event->os, Event->freeEventMutex));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return gcvSTATUS_OK;
+}
+
+#ifndef __QNXNTO__
+
+static gceSTATUS
+gckEVENT_IsEmpty(
+ IN gckEVENT Event,
+ OUT gctBOOL_PTR IsEmpty
+ )
+{
+ gceSTATUS status;
+ gctSIZE_T i;
+
+ gcmkHEADER_ARG("Event=0x%x", Event);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
+ gcmkVERIFY_ARGUMENT(IsEmpty != gcvNULL);
+
+ /* Assume the event queue is empty. */
+ *IsEmpty = gcvTRUE;
+
+ /* Walk the event queue. */
+ for (i = 0; i < gcmCOUNTOF(Event->queues); ++i)
+ {
+ /* Check whether this event is in use. */
+ if (Event->queues[i].head != gcvNULL)
+ {
+ /* The event is in use, hence the queue is not empty. */
+ *IsEmpty = gcvFALSE;
+ break;
+ }
+ }
+
+ /* Try acquiring the mutex. */
+ status = gckOS_AcquireMutex(Event->os, Event->eventQueueMutex, 0);
+ if (status == gcvSTATUS_TIMEOUT)
+ {
+ /* Timeout - queue is no longer empty. */
+ *IsEmpty = gcvFALSE;
+ }
+ else
+ {
+ /* Bail out on error. */
+ gcmkONERROR(status);
+
+ /* Release the mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Event->os, Event->eventQueueMutex));
+ }
+
+ /* Success. */
+ gcmkFOOTER_ARG("*IsEmpty=%d", gcmOPT_VALUE(IsEmpty));
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+#endif
+
+static gceSTATUS
+__RemoveRecordFromProcessDB(
+ IN gckEVENT Event,
+ IN gcsEVENT_PTR Record
+ )
+{
+ gcmkHEADER_ARG("Event=0x%x Record=0x%x", Event, Record);
+ gcmkVERIFY_ARGUMENT(Record != gcvNULL);
+
+ while (Record != gcvNULL)
+ {
+ switch (Record->info.command)
+ {
+ case gcvHAL_FREE_NON_PAGED_MEMORY:
+ gcmkVERIFY_OK(gckKERNEL_RemoveProcessDB(
+ Event->kernel,
+ Record->processID,
+ gcvDB_NON_PAGED,
+ Record->info.u.FreeNonPagedMemory.logical));
+ break;
+
+ case gcvHAL_FREE_CONTIGUOUS_MEMORY:
+ gcmkVERIFY_OK(gckKERNEL_RemoveProcessDB(
+ Event->kernel,
+ Record->processID,
+ gcvDB_CONTIGUOUS,
+ Record->info.u.FreeContiguousMemory.logical));
+ break;
+
+ case gcvHAL_FREE_VIDEO_MEMORY:
+ gcmkVERIFY_OK(gckKERNEL_RemoveProcessDB(
+ Event->kernel,
+ Record->processID,
+ gcvDB_VIDEO_MEMORY,
+ Record->info.u.FreeVideoMemory.node));
+ break;
+
+ case gcvHAL_UNLOCK_VIDEO_MEMORY:
+ gcmkVERIFY_OK(gckKERNEL_RemoveProcessDB(
+ Event->kernel,
+ Record->processID,
+ gcvDB_VIDEO_MEMORY_LOCKED,
+ Record->info.u.UnlockVideoMemory.node));
+ break;
+
+ default:
+ break;
+ }
+
+ Record = Record->next;
+ }
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+static gceSTATUS
+_IsEmpty(
+ IN gckEVENT Event,
+ OUT gctBOOL_PTR IsEmpty
+ )
+{
+ gceSTATUS status;
+ gctSIZE_T i;
+
+ gcmkHEADER_ARG("Event=0x%x", Event);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
+ gcmkVERIFY_ARGUMENT(IsEmpty != gcvNULL);
+
+ /* Assume the event queue is empty. */
+ *IsEmpty = gcvTRUE;
+
+ /* Walk the event queue. */
+ for (i = 0; i < gcmCOUNTOF(Event->queues); ++i)
+ {
+ /* Check whether this event is in use. */
+ if (Event->queues[i].head != gcvNULL)
+ {
+ /* The event is in use, hence the queue is not empty. */
+ *IsEmpty = gcvFALSE;
+ break;
+ }
+ }
+
+ /* Try acquiring the mutex. */
+ status = gckOS_AcquireMutex(Event->os, Event->eventQueueMutex, 0);
+ if (status == gcvSTATUS_TIMEOUT)
+ {
+ /* Timeout - queue is no longer empty. */
+ *IsEmpty = gcvFALSE;
+ }
+ else
+ {
+ /* Bail out on error. */
+ gcmkONERROR(status);
+
+ /* Release the mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Event->os, Event->eventQueueMutex));
+ }
+
+ /* Success. */
+ gcmkFOOTER_ARG("*IsEmpty=%d", gcmOPT_VALUE(IsEmpty));
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/******************************************************************************\
+******************************* gckEVENT API Code *******************************
+\******************************************************************************/
+
+/*******************************************************************************
+**
+** gckEVENT_Construct
+**
+** Construct a new gckEVENT object.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to an gckKERNEL object.
+**
+** OUTPUT:
+**
+** gckEVENT * Event
+** Pointer to a variable that receives the gckEVENT object pointer.
+*/
+gceSTATUS
+gckEVENT_Construct(
+ IN gckKERNEL Kernel,
+ OUT gckEVENT * Event
+ )
+{
+ gckOS os;
+ gceSTATUS status;
+ gckEVENT eventObj = gcvNULL;
+ int i;
+ gcsEVENT_PTR record;
+ gctPOINTER pointer = gcvNULL;
+
+ gcmkHEADER_ARG("Kernel=0x%x", Kernel);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
+ gcmkVERIFY_ARGUMENT(Event != gcvNULL);
+
+ /* Extract the pointer to the gckOS object. */
+ os = Kernel->os;
+ gcmkVERIFY_OBJECT(os, gcvOBJ_OS);
+
+ /* Allocate the gckEVENT object. */
+ gcmkONERROR(gckOS_Allocate(os, gcmSIZEOF(struct _gckEVENT), &pointer));
+
+ eventObj = pointer;
+
+ /* Reset the object. */
+ gcmkVERIFY_OK(gckOS_ZeroMemory(eventObj, gcmSIZEOF(struct _gckEVENT)));
+
+ /* Initialize the gckEVENT object. */
+ eventObj->object.type = gcvOBJ_EVENT;
+ eventObj->kernel = Kernel;
+ eventObj->os = os;
+
+ /* Create the mutexes. */
+ gcmkONERROR(gckOS_CreateMutex(os, &eventObj->eventQueueMutex));
+ gcmkONERROR(gckOS_CreateMutex(os, &eventObj->freeEventMutex));
+ gcmkONERROR(gckOS_CreateMutex(os, &eventObj->eventListMutex));
+
+ /* Create a bunch of event reccords. */
+ for (i = 0; i < gcdEVENT_ALLOCATION_COUNT; i += 1)
+ {
+ /* Allocate an event record. */
+ gcmkONERROR(gckOS_Allocate(os, gcmSIZEOF(gcsEVENT), &pointer));
+
+ record = pointer;
+
+ /* Push it on the free list. */
+ record->next = eventObj->freeEventList;
+ eventObj->freeEventList = record;
+ eventObj->freeEventCount += 1;
+ }
+
+ /* Initialize the free list of event queues. */
+ for (i = 0; i < gcdREPO_LIST_COUNT; i += 1)
+ {
+ eventObj->repoList[i].next = eventObj->freeList;
+ eventObj->freeList = &eventObj->repoList[i];
+ }
+
+ /* Construct the atom. */
+ gcmkONERROR(gckOS_AtomConstruct(os, &eventObj->freeAtom));
+ gcmkONERROR(gckOS_AtomSet(os,
+ eventObj->freeAtom,
+ gcmCOUNTOF(eventObj->queues)));
+
+ /* Return pointer to the gckEVENT object. */
+ *Event = eventObj;
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Event=0x%x", *Event);
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Roll back. */
+ if (eventObj != gcvNULL)
+ {
+ if (eventObj->eventQueueMutex != gcvNULL)
+ {
+ gcmkVERIFY_OK(gckOS_DeleteMutex(os, eventObj->eventQueueMutex));
+ }
+
+ if (eventObj->freeEventMutex != gcvNULL)
+ {
+ gcmkVERIFY_OK(gckOS_DeleteMutex(os, eventObj->freeEventMutex));
+ }
+
+ if (eventObj->eventListMutex != gcvNULL)
+ {
+ gcmkVERIFY_OK(gckOS_DeleteMutex(os, eventObj->eventListMutex));
+ }
+
+ while (eventObj->freeEventList != gcvNULL)
+ {
+ record = eventObj->freeEventList;
+ eventObj->freeEventList = record->next;
+
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os, record));
+ }
+
+ if (eventObj->freeAtom != gcvNULL)
+ {
+ gcmkVERIFY_OK(gckOS_AtomDestroy(os, eventObj->freeAtom));
+ }
+
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os, eventObj));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckEVENT_Destroy
+**
+** Destroy an gckEVENT object.
+**
+** INPUT:
+**
+** gckEVENT Event
+** Pointer to an gckEVENT object.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckEVENT_Destroy(
+ IN gckEVENT Event
+ )
+{
+ gcsEVENT_PTR record;
+ gcsEVENT_QUEUE_PTR queue;
+
+ gcmkHEADER_ARG("Event=0x%x", Event);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
+
+ /* Delete the queue mutex. */
+ gcmkVERIFY_OK(gckOS_DeleteMutex(Event->os, Event->eventQueueMutex));
+
+ /* Free all free events. */
+ while (Event->freeEventList != gcvNULL)
+ {
+ record = Event->freeEventList;
+ Event->freeEventList = record->next;
+
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Event->os, record));
+ }
+
+ /* Delete the free mutex. */
+ gcmkVERIFY_OK(gckOS_DeleteMutex(Event->os, Event->freeEventMutex));
+
+ /* Free all pending queues. */
+ while (Event->queueHead != gcvNULL)
+ {
+ /* Get the current queue. */
+ queue = Event->queueHead;
+
+ /* Free all pending events. */
+ while (queue->head != gcvNULL)
+ {
+ record = queue->head;
+ queue->head = record->next;
+
+ gcmkTRACE_ZONE_N(
+ gcvLEVEL_WARNING, gcvZONE_EVENT,
+ gcmSIZEOF(record) + gcmSIZEOF(queue->source),
+ "Event record 0x%x is still pending for %d.",
+ record, queue->source
+ );
+
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Event->os, record));
+ }
+
+ /* Remove the top queue from the list. */
+ if (Event->queueHead == Event->queueTail)
+ {
+ Event->queueHead =
+ Event->queueTail = gcvNULL;
+ }
+ else
+ {
+ Event->queueHead = Event->queueHead->next;
+ }
+
+ /* Free the queue. */
+ gcmkVERIFY_OK(gckEVENT_FreeQueue(Event, queue));
+ }
+
+ /* Delete the list mutex. */
+ gcmkVERIFY_OK(gckOS_DeleteMutex(Event->os, Event->eventListMutex));
+
+ /* Delete the atom. */
+ gcmkVERIFY_OK(gckOS_AtomDestroy(Event->os, Event->freeAtom));
+
+ /* Mark the gckEVENT object as unknown. */
+ Event->object.type = gcvOBJ_UNKNOWN;
+
+ /* Free the gckEVENT object. */
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Event->os, Event));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckEVENT_GetEvent
+**
+** Reserve the next available hardware event.
+**
+** INPUT:
+**
+** gckEVENT Event
+** Pointer to an gckEVENT object.
+**
+** gctBOOL Wait
+** Set to gcvTRUE to force the function to wait if no events are
+** immediately available.
+**
+** gceKERNEL_WHERE Source
+** Source of the event.
+**
+** OUTPUT:
+**
+** gctUINT8 * EventID
+** Reserved event ID.
+*/
+gceSTATUS
+gckEVENT_GetEvent(
+ IN gckEVENT Event,
+ IN gctBOOL Wait,
+ OUT gctUINT8 * EventID,
+ IN gceKERNEL_WHERE Source
+ )
+{
+ gctINT i, id;
+ gceSTATUS status;
+ gctBOOL acquired = gcvFALSE;
+ gctBOOL suspended = gcvFALSE;
+ gctINT32 free;
+
+#if gcdGPU_TIMEOUT
+ gctUINT32 timer = 0;
+#endif
+
+ gcmkHEADER_ARG("Event=0x%x Source=%d", Event, Source);
+
+ while (gcvTRUE)
+ {
+ /* Grab the queue mutex. */
+ gcmkONERROR(gckOS_AcquireMutex(Event->os,
+ Event->eventQueueMutex,
+ gcvINFINITE));
+ acquired = gcvTRUE;
+
+ /* Walk through all events. */
+ id = Event->lastID;
+ for (i = 0; i < gcmCOUNTOF(Event->queues); ++i)
+ {
+ gctINT nextID = gckMATH_ModuloInt((id + 1),
+ gcmCOUNTOF(Event->queues));
+
+ if (Event->queues[id].head == gcvNULL)
+ {
+ *EventID = (gctUINT8) id;
+
+ Event->lastID = (gctUINT8) nextID;
+
+ /* Save time stamp of event. */
+ Event->queues[id].stamp = ++(Event->stamp);
+ Event->queues[id].source = Source;
+
+ gcmkONERROR(gckOS_AtomDecrement(Event->os,
+ Event->freeAtom,
+ &free));
+#if gcdDYNAMIC_SPEED
+ if (free <= gcdDYNAMIC_EVENT_THRESHOLD)
+ {
+ gcmkONERROR(gckOS_BroadcastHurry(
+ Event->os,
+ Event->kernel->hardware,
+ gcdDYNAMIC_EVENT_THRESHOLD - free));
+ }
+#endif
+
+ /* Release the queue mutex. */
+ gcmkONERROR(gckOS_ReleaseMutex(Event->os,
+ Event->eventQueueMutex));
+
+ /* Success. */
+ gcmkTRACE_ZONE_N(
+ gcvLEVEL_INFO, gcvZONE_EVENT,
+ gcmSIZEOF(id),
+ "Using id=%d",
+ id
+ );
+
+ gcmkFOOTER_ARG("*EventID=%u", *EventID);
+ return gcvSTATUS_OK;
+ }
+
+ id = nextID;
+ }
+
+#if gcdDYNAMIC_SPEED
+ /* No free events, speed up the GPU right now! */
+ gcmkONERROR(gckOS_BroadcastHurry(Event->os,
+ Event->kernel->hardware,
+ gcdDYNAMIC_EVENT_THRESHOLD));
+#endif
+
+ /* Release the queue mutex. */
+ gcmkONERROR(gckOS_ReleaseMutex(Event->os, Event->eventQueueMutex));
+ acquired = gcvFALSE;
+
+ /* Fail if wait is not requested. */
+ if (!Wait)
+ {
+ /* Out of resources. */
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+
+ /* Delay a while. */
+ gcmkONERROR(gckOS_Delay(Event->os, 1));
+
+#if gcdGPU_TIMEOUT
+ /* Increment the wait timer. */
+ timer += 1;
+
+ if (timer == gcdGPU_TIMEOUT)
+ {
+ /* Suspend interrupts. */
+ gcmkONERROR(gckOS_SuspendInterrupt(Event->os));
+ suspended = gcvTRUE;
+
+ /* Try to call any outstanding events. */
+ gcmkONERROR(gckHARDWARE_Interrupt(Event->kernel->hardware,
+ gcvTRUE));
+
+ /* Resume interrupts. */
+ gcmkONERROR(gckOS_ResumeInterrupt(Event->os));
+ suspended = gcvFALSE;
+
+ }
+ else if (timer > gcdGPU_TIMEOUT)
+ {
+ gcmkTRACE_N(
+ gcvLEVEL_ERROR,
+ gcmSIZEOF(gctCONST_STRING) + gcmSIZEOF(gctINT),
+ "%s(%d): no available events\n",
+ __FUNCTION__, __LINE__
+ );
+
+ /* Broadcast GPU stuck. */
+ gcmkONERROR(gckOS_Broadcast(Event->os,
+ Event->kernel->hardware,
+ gcvBROADCAST_GPU_STUCK));
+
+ /* Bail out. */
+ gcmkONERROR(gcvSTATUS_GPU_NOT_RESPONDING);
+ }
+#endif
+ }
+
+OnError:
+ if (acquired)
+ {
+ /* Release the queue mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Event->os, Event->eventQueueMutex));
+ }
+
+ if (suspended)
+ {
+ /* Resume interrupts. */
+ gcmkVERIFY_OK(gckOS_ResumeInterrupt(Event->os));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckEVENT_AllocateRecord
+**
+** Allocate a record for the new event.
+**
+** INPUT:
+**
+** gckEVENT Event
+** Pointer to an gckEVENT object.
+**
+** gctBOOL AllocateAllowed
+** State for allocation if out of free events.
+**
+** OUTPUT:
+**
+** gcsEVENT_PTR * Record
+** Allocated event record.
+*/
+gceSTATUS
+gckEVENT_AllocateRecord(
+ IN gckEVENT Event,
+ IN gctBOOL AllocateAllowed,
+ OUT gcsEVENT_PTR * Record
+ )
+{
+ gceSTATUS status;
+ gctBOOL acquired = gcvFALSE;
+ gctINT i;
+ gcsEVENT_PTR record;
+ gctPOINTER pointer = gcvNULL;
+
+ gcmkHEADER_ARG("Event=0x%x AllocateAllowed=%d", Event, AllocateAllowed);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
+ gcmkVERIFY_ARGUMENT(Record != gcvNULL);
+
+ /* Test if we are below the allocation threshold. */
+ if (AllocateAllowed && (Event->freeEventCount < gcdEVENT_MIN_THRESHOLD))
+ {
+ /* Allocate a bunch of records. */
+ for (i = 0; i < gcdEVENT_ALLOCATION_COUNT; i += 1)
+ {
+ /* Allocate an event record. */
+ gcmkONERROR(gckOS_Allocate(Event->os,
+ gcmSIZEOF(gcsEVENT),
+ &pointer));
+
+ record = pointer;
+
+ /* Acquire the mutex. */
+ gcmkONERROR(gckOS_AcquireMutex(Event->os, Event->freeEventMutex, gcvINFINITE));
+ acquired = gcvTRUE;
+
+ /* Push it on the free list. */
+ record->next = Event->freeEventList;
+ Event->freeEventList = record;
+ Event->freeEventCount += 1;
+
+ /* Release the mutex. */
+ gcmkONERROR(gckOS_ReleaseMutex(Event->os, Event->freeEventMutex));
+ acquired = gcvFALSE;
+ }
+ }
+
+ /* Acquire the mutex. */
+ gcmkONERROR(gckOS_AcquireMutex(Event->os, Event->freeEventMutex, gcvINFINITE));
+ acquired = gcvTRUE;
+
+ *Record = Event->freeEventList;
+ Event->freeEventList = Event->freeEventList->next;
+ Event->freeEventCount -= 1;
+
+ /* Release the mutex. */
+ gcmkONERROR(gckOS_ReleaseMutex(Event->os, Event->freeEventMutex));
+ acquired = gcvFALSE;
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Record=0x%x", gcmOPT_POINTER(Record));
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Roll back. */
+ if (acquired)
+ {
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Event->os, Event->freeEventMutex));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckEVENT_AddList
+**
+** Add a new event to the list of events.
+**
+** INPUT:
+**
+** gckEVENT Event
+** Pointer to an gckEVENT object.
+**
+** gcsHAL_INTERFACE_PTR Interface
+** Pointer to the interface for the event to be added.
+**
+** gceKERNEL_WHERE FromWhere
+** Place in the pipe where the event needs to be generated.
+**
+** gctBOOL AllocateAllowed
+** State for allocation if out of free events.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckEVENT_AddList(
+ IN gckEVENT Event,
+ IN gcsHAL_INTERFACE_PTR Interface,
+ IN gceKERNEL_WHERE FromWhere,
+ IN gctBOOL AllocateAllowed
+ )
+{
+ gceSTATUS status;
+ gctBOOL acquired = gcvFALSE;
+ gcsEVENT_PTR record = gcvNULL;
+ gcsEVENT_QUEUE_PTR queue;
+
+ gcmkHEADER_ARG("Event=0x%x Interface=0x%x FromWhere=%d AllocateAllowed=%d",
+ Event, Interface, FromWhere, AllocateAllowed);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
+ gcmkVERIFY_ARGUMENT(Interface != gcvNULL);
+
+ /* Verify the event command. */
+ gcmkASSERT
+ ( (Interface->command == gcvHAL_FREE_NON_PAGED_MEMORY)
+ || (Interface->command == gcvHAL_FREE_CONTIGUOUS_MEMORY)
+ || (Interface->command == gcvHAL_FREE_VIDEO_MEMORY)
+ || (Interface->command == gcvHAL_WRITE_DATA)
+ || (Interface->command == gcvHAL_UNLOCK_VIDEO_MEMORY)
+ || (Interface->command == gcvHAL_SIGNAL)
+ || (Interface->command == gcvHAL_UNMAP_USER_MEMORY)
+ || (Interface->command == gcvHAL_TIMESTAMP)
+ );
+
+ /* Validate the source. */
+ if ((FromWhere != gcvKERNEL_COMMAND) && (FromWhere != gcvKERNEL_PIXEL))
+ {
+ /* Invalid argument. */
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ /* Allocate a free record. */
+ gcmkONERROR(gckEVENT_AllocateRecord(Event, AllocateAllowed, &record));
+
+ /* Termninate the record. */
+ record->next = gcvNULL;
+
+ /* Copy the event interface into the record. */
+ gcmkONERROR(gckOS_MemCopy(&record->info, Interface, gcmSIZEOF(record->info)));
+
+ /* Get process ID. */
+ gcmkONERROR(gckOS_GetProcessID(&record->processID));
+
+#ifdef __QNXNTO__
+ record->kernel = Event->kernel;
+#endif
+
+ /* Acquire the mutex. */
+ gcmkONERROR(gckOS_AcquireMutex(Event->os, Event->eventListMutex, gcvINFINITE));
+ acquired = gcvTRUE;
+
+ /* Do we need to allocate a new queue? */
+ if ((Event->queueTail == gcvNULL) || (Event->queueTail->source != FromWhere))
+ {
+ /* Allocate a new queue. */
+ gcmkONERROR(gckEVENT_AllocateQueue(Event, &queue));
+
+ /* Initialize the queue. */
+ queue->source = FromWhere;
+ queue->head = gcvNULL;
+ queue->next = gcvNULL;
+
+ /* Attach it to the list of alloicated queues. */
+ if (Event->queueTail == gcvNULL)
+ {
+ Event->queueHead =
+ Event->queueTail = queue;
+ }
+ else
+ {
+ Event->queueTail->next = queue;
+ Event->queueTail = queue;
+ }
+ }
+ else
+ {
+ queue = Event->queueTail;
+ }
+
+ /* Attach the record to the queue. */
+ if (queue->head == gcvNULL)
+ {
+ queue->head = record;
+ queue->tail = record;
+ }
+ else
+ {
+ queue->tail->next = record;
+ queue->tail = record;
+ }
+
+ /* Release the mutex. */
+ gcmkONERROR(gckOS_ReleaseMutex(Event->os, Event->eventListMutex));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Roll back. */
+ if (acquired)
+ {
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Event->os, Event->eventListMutex));
+ }
+
+ if (record != gcvNULL)
+ {
+ gcmkVERIFY_OK(gckEVENT_FreeRecord(Event, record));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckEVENT_Unlock
+**
+** Schedule an event to unlock virtual memory.
+**
+** INPUT:
+**
+** gckEVENT Event
+** Pointer to an gckEVENT object.
+**
+** gceKERNEL_WHERE FromWhere
+** Place in the pipe where the event needs to be generated.
+**
+** gcuVIDMEM_NODE_PTR Node
+** Pointer to a gcuVIDMEM_NODE union that specifies the virtual memory
+** to unlock.
+**
+** gceSURF_TYPE Type
+** Type of surface to unlock.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckEVENT_Unlock(
+ IN gckEVENT Event,
+ IN gceKERNEL_WHERE FromWhere,
+ IN gcuVIDMEM_NODE_PTR Node,
+ IN gceSURF_TYPE Type
+ )
+{
+ gceSTATUS status;
+ gcsHAL_INTERFACE iface;
+
+ gcmkHEADER_ARG("Event=0x%x FromWhere=%d Node=0x%x Type=%d",
+ Event, FromWhere, Node, Type);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
+ gcmkVERIFY_ARGUMENT(Node != gcvNULL);
+
+ /* Mark the event as an unlock. */
+ iface.command = gcvHAL_UNLOCK_VIDEO_MEMORY;
+ iface.u.UnlockVideoMemory.node = Node;
+ iface.u.UnlockVideoMemory.type = Type;
+ iface.u.UnlockVideoMemory.asynchroneous = 0;
+
+ /* Append it to the queue. */
+ gcmkONERROR(gckEVENT_AddList(Event, &iface, FromWhere, gcvFALSE));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckEVENT_FreeVideoMemory
+**
+** Schedule an event to free video memory.
+**
+** INPUT:
+**
+** gckEVENT Event
+** Pointer to an gckEVENT object.
+**
+** gcuVIDMEM_NODE_PTR VideoMemory
+** Pointer to a gcuVIDMEM_NODE object to free.
+**
+** gceKERNEL_WHERE FromWhere
+** Place in the pipe where the event needs to be generated.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckEVENT_FreeVideoMemory(
+ IN gckEVENT Event,
+ IN gcuVIDMEM_NODE_PTR VideoMemory,
+ IN gceKERNEL_WHERE FromWhere
+ )
+{
+ gceSTATUS status;
+ gcsHAL_INTERFACE iface;
+
+ gcmkHEADER_ARG("Event=0x%x VideoMemory=0x%x FromWhere=%d",
+ Event, VideoMemory, FromWhere);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
+ gcmkVERIFY_ARGUMENT(VideoMemory != gcvNULL);
+
+ /* Create an event. */
+ iface.command = gcvHAL_FREE_VIDEO_MEMORY;
+ iface.u.FreeVideoMemory.node = VideoMemory;
+
+ /* Append it to the queue. */
+ gcmkONERROR(gckEVENT_AddList(Event, &iface, FromWhere, gcvFALSE));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckEVENT_FreeNonPagedMemory
+**
+** Schedule an event to free non-paged memory.
+**
+** INPUT:
+**
+** gckEVENT Event
+** Pointer to an gckEVENT object.
+**
+** gctSIZE_T Bytes
+** Number of bytes of non-paged memory to free.
+**
+** gctPHYS_ADDR Physical
+** Physical address of non-paged memory to free.
+**
+** gctPOINTER Logical
+** Logical address of non-paged memory to free.
+**
+** gceKERNEL_WHERE FromWhere
+** Place in the pipe where the event needs to be generated.
+*/
+gceSTATUS
+gckEVENT_FreeNonPagedMemory(
+ IN gckEVENT Event,
+ IN gctSIZE_T Bytes,
+ IN gctPHYS_ADDR Physical,
+ IN gctPOINTER Logical,
+ IN gceKERNEL_WHERE FromWhere
+ )
+{
+ gceSTATUS status;
+ gcsHAL_INTERFACE iface;
+
+ gcmkHEADER_ARG("Event=0x%x Bytes=%lu Physical=0x%x Logical=0x%x "
+ "FromWhere=%d",
+ Event, Bytes, Physical, Logical, FromWhere);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
+ gcmkVERIFY_ARGUMENT(Physical != gcvNULL);
+ gcmkVERIFY_ARGUMENT(Logical != gcvNULL);
+ gcmkVERIFY_ARGUMENT(Bytes > 0);
+
+ /* Create an event. */
+ iface.command = gcvHAL_FREE_NON_PAGED_MEMORY;
+ iface.u.FreeNonPagedMemory.bytes = Bytes;
+ iface.u.FreeNonPagedMemory.physical = Physical;
+ iface.u.FreeNonPagedMemory.logical = Logical;
+
+ /* Append it to the queue. */
+ gcmkONERROR(gckEVENT_AddList(Event, &iface, FromWhere, gcvFALSE));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckEVENT_FreeContigiuousMemory
+**
+** Schedule an event to free contiguous memory.
+**
+** INPUT:
+**
+** gckEVENT Event
+** Pointer to an gckEVENT object.
+**
+** gctSIZE_T Bytes
+** Number of bytes of contiguous memory to free.
+**
+** gctPHYS_ADDR Physical
+** Physical address of contiguous memory to free.
+**
+** gctPOINTER Logical
+** Logical address of contiguous memory to free.
+**
+** gceKERNEL_WHERE FromWhere
+** Place in the pipe where the event needs to be generated.
+*/
+gceSTATUS
+gckEVENT_FreeContiguousMemory(
+ IN gckEVENT Event,
+ IN gctSIZE_T Bytes,
+ IN gctPHYS_ADDR Physical,
+ IN gctPOINTER Logical,
+ IN gceKERNEL_WHERE FromWhere
+ )
+{
+ gceSTATUS status;
+ gcsHAL_INTERFACE iface;
+
+ gcmkHEADER_ARG("Event=0x%x Bytes=%lu Physical=0x%x Logical=0x%x "
+ "FromWhere=%d",
+ Event, Bytes, Physical, Logical, FromWhere);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
+ gcmkVERIFY_ARGUMENT(Physical != gcvNULL);
+ gcmkVERIFY_ARGUMENT(Logical != gcvNULL);
+ gcmkVERIFY_ARGUMENT(Bytes > 0);
+
+ /* Create an event. */
+ iface.command = gcvHAL_FREE_CONTIGUOUS_MEMORY;
+ iface.u.FreeContiguousMemory.bytes = Bytes;
+ iface.u.FreeContiguousMemory.physical = Physical;
+ iface.u.FreeContiguousMemory.logical = Logical;
+
+ /* Append it to the queue. */
+ gcmkONERROR(gckEVENT_AddList(Event, &iface, FromWhere, gcvFALSE));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckEVENT_Signal
+**
+** Schedule an event to trigger a signal.
+**
+** INPUT:
+**
+** gckEVENT Event
+** Pointer to an gckEVENT object.
+**
+** gctSIGNAL Signal
+** Pointer to the signal to trigger.
+**
+** gceKERNEL_WHERE FromWhere
+** Place in the pipe where the event needs to be generated.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckEVENT_Signal(
+ IN gckEVENT Event,
+ IN gctSIGNAL Signal,
+ IN gceKERNEL_WHERE FromWhere
+ )
+{
+ gceSTATUS status;
+ gcsHAL_INTERFACE iface;
+
+ gcmkHEADER_ARG("Event=0x%x Signal=0x%x FromWhere=%d",
+ Event, Signal, FromWhere);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
+ gcmkVERIFY_ARGUMENT(Signal != gcvNULL);
+
+ /* Mark the event as a signal. */
+ iface.command = gcvHAL_SIGNAL;
+ iface.u.Signal.signal = Signal;
+#ifdef __QNXNTO__
+ iface.u.Signal.coid = 0;
+ iface.u.Signal.rcvid = 0;
+#endif
+ iface.u.Signal.auxSignal = gcvNULL;
+ iface.u.Signal.process = gcvNULL;
+
+ /* Append it to the queue. */
+ gcmkONERROR(gckEVENT_AddList(Event, &iface, FromWhere, gcvFALSE));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckEVENT_Submit
+**
+** Submit the current event queue to the GPU.
+**
+** INPUT:
+**
+** gckEVENT Event
+** Pointer to an gckEVENT object.
+**
+** gctBOOL Wait
+** Submit requires one vacant event; if Wait is set to not zero,
+** and there are no vacant events at this time, the function will
+** wait until an event becomes vacant so that submission of the
+** queue is successful.
+**
+** gctBOOL FromPower
+** Determines whether the call originates from inside the power
+** management or not.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckEVENT_Submit(
+ IN gckEVENT Event,
+ IN gctBOOL Wait,
+ IN gctBOOL FromPower
+ )
+{
+ gceSTATUS status;
+ gctUINT8 id = 0xFF;
+ gcsEVENT_QUEUE_PTR queue;
+ gctBOOL acquired = gcvFALSE;
+
+#if !gcdNULL_DRIVER
+ gctSIZE_T bytes;
+ gctPOINTER buffer;
+ gckCOMMAND command = gcvNULL;
+ gctBOOL commitEntered = gcvFALSE;
+#endif
+
+ gcmkHEADER_ARG("Event=0x%x Wait=%d", Event, Wait);
+
+#if !gcdNULL_DRIVER
+ /* Get gckCOMMAND object. */
+ command = Event->kernel->command;
+#endif
+
+ /* Are there event queues? */
+ if (Event->queueHead != gcvNULL)
+ {
+#if !gcdNULL_DRIVER
+ /* Acquire the command queue. */
+ gcmkONERROR(gckCOMMAND_EnterCommit(command, FromPower));
+ commitEntered = gcvTRUE;
+#endif
+
+ /* Process all queues. */
+ while (Event->queueHead != gcvNULL)
+ {
+ /* Acquire the list mutex. */
+ gcmkONERROR(gckOS_AcquireMutex(Event->os,
+ Event->eventListMutex,
+ gcvINFINITE));
+ acquired = gcvTRUE;
+
+ /* Get the current queue. */
+ queue = Event->queueHead;
+
+ /* Allocate an event ID. */
+ gcmkONERROR(gckEVENT_GetEvent(Event, Wait, &id, queue->source));
+
+ /* Copy event list to event ID queue. */
+ Event->queues[id].source = queue->source;
+ Event->queues[id].head = queue->head;
+
+ /* Remove the top queue from the list. */
+ if (Event->queueHead == Event->queueTail)
+ {
+ Event->queueHead = gcvNULL;
+ Event->queueTail = gcvNULL;
+ }
+ else
+ {
+ Event->queueHead = Event->queueHead->next;
+ }
+
+ /* Free the queue. */
+ gcmkONERROR(gckEVENT_FreeQueue(Event, queue));
+
+ /* Release the list mutex. */
+ gcmkONERROR(gckOS_ReleaseMutex(Event->os, Event->eventListMutex));
+ acquired = gcvFALSE;
+
+ gcmkONERROR(__RemoveRecordFromProcessDB(Event,
+ Event->queues[id].head));
+
+#if gcdNULL_DRIVER
+ /* Notify immediately on infinite hardware. */
+ gcmkONERROR(gckEVENT_Interrupt(Event, 1 << id));
+
+ gcmkONERROR(gckEVENT_Notify(Event, 0));
+#else
+ /* Get the size of the hardware event. */
+ gcmkONERROR(gckHARDWARE_Event(Event->kernel->hardware,
+ gcvNULL,
+ id,
+ gcvKERNEL_PIXEL,
+ &bytes));
+
+ /* Reserve space in the command queue. */
+ gcmkONERROR(gckCOMMAND_Reserve(command,
+ bytes,
+ &buffer,
+ &bytes));
+
+ /* Set the hardware event in the command queue. */
+ gcmkONERROR(gckHARDWARE_Event(Event->kernel->hardware,
+ buffer,
+ id,
+ Event->queues[id].source,
+ &bytes));
+
+ /* Execute the hardware event. */
+ gcmkONERROR(gckCOMMAND_Execute(command, bytes));
+#endif
+ }
+
+#if !gcdNULL_DRIVER
+ /* Release the command queue. */
+ gcmkONERROR(gckCOMMAND_ExitCommit(command, FromPower));
+ commitEntered = gcvFALSE;
+#endif
+ }
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+#if !gcdNULL_DRIVER
+ if (commitEntered)
+ {
+ /* Release the command queue mutex. */
+ gcmkVERIFY_OK(gckCOMMAND_ExitCommit(command, FromPower));
+ }
+#endif
+
+ if (acquired)
+ {
+ /* Need to unroll the mutex acquire. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Event->os, Event->eventListMutex));
+ }
+
+ if (id != 0xFF)
+ {
+ /* Need to unroll the event allocation. */
+ Event->queues[id].head = gcvNULL;
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckEVENT_Commit
+**
+** Commit an event queue from the user.
+**
+** INPUT:
+**
+** gckEVENT Event
+** Pointer to an gckEVENT object.
+**
+** gcsQUEUE_PTR Queue
+** User event queue.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckEVENT_Commit(
+ IN gckEVENT Event,
+ IN gcsQUEUE_PTR Queue
+ )
+{
+ gceSTATUS status;
+ gcsQUEUE_PTR record = gcvNULL, next;
+ gctUINT32 processID;
+ gctBOOL needCopy = gcvFALSE;
+
+ gcmkHEADER_ARG("Event=0x%x Queue=0x%x", Event, Queue);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
+
+ /* Get the current process ID. */
+ gcmkONERROR(gckOS_GetProcessID(&processID));
+
+ /* Query if we need to copy the client data. */
+ gcmkONERROR(gckOS_QueryNeedCopy(Event->os, processID, &needCopy));
+
+ /* Loop while there are records in the queue. */
+ while (Queue != gcvNULL)
+ {
+ gcsQUEUE queue;
+
+ if (needCopy)
+ {
+ /* Point to stack record. */
+ record = &queue;
+
+ /* Copy the data from the client. */
+ gcmkONERROR(gckOS_CopyFromUserData(Event->os,
+ record,
+ Queue,
+ gcmSIZEOF(gcsQUEUE)));
+ }
+ else
+ {
+ gctPOINTER pointer = gcvNULL;
+
+ /* Map record into kernel memory. */
+ gcmkONERROR(gckOS_MapUserPointer(Event->os,
+ Queue,
+ gcmSIZEOF(gcsQUEUE),
+ &pointer));
+
+ record = pointer;
+ }
+
+ /* Append event record to event queue. */
+ gcmkONERROR(
+ gckEVENT_AddList(Event, &record->iface, gcvKERNEL_PIXEL, gcvTRUE));
+
+ /* Next record in the queue. */
+ next = record->next;
+
+ if (!needCopy)
+ {
+ /* Unmap record from kernel memory. */
+ gcmkONERROR(
+ gckOS_UnmapUserPointer(Event->os,
+ Queue,
+ gcmSIZEOF(gcsQUEUE),
+ (gctPOINTER *) record));
+ record = gcvNULL;
+ }
+
+ Queue = next;
+ }
+
+ /* Submit the event list. */
+ gcmkONERROR(gckEVENT_Submit(Event, gcvTRUE, gcvFALSE));
+
+ /* Success */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ if ((record != gcvNULL) && !needCopy)
+ {
+ /* Roll back. */
+ gcmkVERIFY_OK(gckOS_UnmapUserPointer(Event->os,
+ Queue,
+ gcmSIZEOF(gcsQUEUE),
+ (gctPOINTER *) record));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckEVENT_Compose
+**
+** Schedule a composition event and start a composition.
+**
+** INPUT:
+**
+** gckEVENT Event
+** Pointer to an gckEVENT object.
+**
+** gcsHAL_COMPOSE_PTR Info
+** Pointer to the composition structure.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckEVENT_Compose(
+ IN gckEVENT Event,
+ IN gcsHAL_COMPOSE_PTR Info
+ )
+{
+ gceSTATUS status;
+ gcsEVENT_PTR headRecord;
+ gcsEVENT_PTR tailRecord;
+ gcsEVENT_PTR tempRecord;
+ gctUINT8 id = 0xFF;
+ gctUINT32 processID;
+
+ gcmkHEADER_ARG("Event=0x%x Info=0x%x", Event, Info);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
+ gcmkVERIFY_ARGUMENT(Info != gcvNULL);
+
+ /* Allocate an event ID. */
+ gcmkONERROR(gckEVENT_GetEvent(Event, gcvTRUE, &id, gcvKERNEL_PIXEL));
+
+ /* Get process ID. */
+ gcmkONERROR(gckOS_GetProcessID(&processID));
+
+ /* Allocate a record. */
+ gcmkONERROR(gckEVENT_AllocateRecord(Event, gcvTRUE, &tempRecord));
+ headRecord = tailRecord = tempRecord;
+
+ /* Initialize the record. */
+ tempRecord->info.command = gcvHAL_SIGNAL;
+ tempRecord->info.u.Signal.process = Info->process;
+#ifdef __QNXNTO__
+ tempRecord->info.u.Signal.coid = Info->coid;
+ tempRecord->info.u.Signal.rcvid = Info->rcvid;
+#endif
+ tempRecord->info.u.Signal.signal = Info->signal;
+ tempRecord->info.u.Signal.auxSignal = gcvNULL;
+ tempRecord->next = gcvNULL;
+ tempRecord->processID = processID;
+
+ /* Allocate another record for user signal #1. */
+ if (Info->userSignal1 != gcvNULL)
+ {
+ /* Allocate a record. */
+ gcmkONERROR(gckEVENT_AllocateRecord(Event, gcvTRUE, &tempRecord));
+ tailRecord->next = tempRecord;
+ tailRecord = tempRecord;
+
+ /* Initialize the record. */
+ tempRecord->info.command = gcvHAL_SIGNAL;
+ tempRecord->info.u.Signal.process = Info->userProcess;
+#ifdef __QNXNTO__
+ tempRecord->info.u.Signal.coid = Info->coid;
+ tempRecord->info.u.Signal.rcvid = Info->rcvid;
+#endif
+ tempRecord->info.u.Signal.signal = Info->userSignal1;
+ tempRecord->info.u.Signal.auxSignal = gcvNULL;
+ tempRecord->next = gcvNULL;
+ tempRecord->processID = processID;
+ }
+
+ /* Allocate another record for user signal #2. */
+ if (Info->userSignal2 != gcvNULL)
+ {
+ /* Allocate a record. */
+ gcmkONERROR(gckEVENT_AllocateRecord(Event, gcvTRUE, &tempRecord));
+ tailRecord->next = tempRecord;
+ tailRecord = tempRecord;
+
+ /* Initialize the record. */
+ tempRecord->info.command = gcvHAL_SIGNAL;
+ tempRecord->info.u.Signal.process = Info->userProcess;
+#ifdef __QNXNTO__
+ tempRecord->info.u.Signal.coid = Info->coid;
+ tempRecord->info.u.Signal.rcvid = Info->rcvid;
+#endif
+ tempRecord->info.u.Signal.signal = Info->userSignal2;
+ tempRecord->info.u.Signal.auxSignal = gcvNULL;
+ tempRecord->next = gcvNULL;
+ tempRecord->processID = processID;
+ }
+
+ /* Set the event list. */
+ Event->queues[id].head = headRecord;
+
+ /* Start composition. */
+ gcmkONERROR(gckHARDWARE_Compose(
+ Event->kernel->hardware, processID,
+ Info->size, Info->physical, Info->logical, id
+ ));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckEVENT_Interrupt
+**
+** Called by the interrupt service routine to store the triggered interrupt
+** mask to be later processed by gckEVENT_Notify.
+**
+** INPUT:
+**
+** gckEVENT Event
+** Pointer to an gckEVENT object.
+**
+** gctUINT32 Data
+** Mask for the 32 interrupts.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckEVENT_Interrupt(
+ IN gckEVENT Event,
+ IN gctUINT32 Data
+ )
+{
+ gcmkHEADER_ARG("Event=0x%x Data=0x%x", Event, Data);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
+
+ /* Combine current interrupt status with pending flags. */
+#ifdef __QNXNTO__
+ atomic_set(&Event->pending, Data);
+#else
+#ifdef LINUX
+ {
+ gctUINT32 oldVal,newVal;
+ do{
+ oldVal = Event->pending;
+ newVal = oldVal| Data;
+ }while(atomic_cmpxchg((atomic_t *)&Event->pending,oldVal,newVal)!=oldVal);
+ }
+#else
+ Event->pending |= Data;
+#endif
+#endif
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckEVENT_Notify
+**
+** Process all triggered interrupts.
+**
+** INPUT:
+**
+** gckEVENT Event
+** Pointer to an gckEVENT object.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckEVENT_Notify(
+ IN gckEVENT Event,
+ IN gctUINT32 IDs
+ )
+{
+ gceSTATUS status = gcvSTATUS_OK;
+ gctINT i;
+ gcsEVENT_QUEUE * queue;
+ gctUINT mask = 0;
+ gctBOOL acquired = gcvFALSE;
+#ifdef __QNXNTO__
+ gcuVIDMEM_NODE_PTR node;
+#endif
+ gctUINT pending;
+ gctBOOL suspended = gcvFALSE;
+#ifndef __QNXNTO__
+ gctBOOL empty = gcvFALSE, idle = gcvFALSE;
+#endif
+ gcmDEBUG_ONLY(gctINT eventNumber = 0;)
+ gctINT32 free;
+#if gcdSECURE_USER
+ gcskSECURE_CACHE_PTR cache;
+#endif
+
+ gcmkHEADER_ARG("Event=0x%x IDs=0x%x", Event, IDs);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
+
+ gcmDEBUG_ONLY(
+ if (IDs != 0)
+ {
+ for (i = 0; i < gcmCOUNTOF(Event->queues); ++i)
+ {
+ if (Event->queues[i].head != gcvNULL)
+ {
+ gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_EVENT,
+ "Queue(%d): stamp=%llu source=%d",
+ i,
+ Event->queues[i].stamp,
+ Event->queues[i].source);
+ }
+ }
+ }
+ );
+
+ for (;;)
+ {
+ /* Suspend interrupts. */
+ gcmkONERROR(gckOS_SuspendInterruptEx(Event->os, Event->kernel->core));
+ suspended = gcvTRUE;
+
+ /* Get current interrupts. */
+ pending = Event->pending;
+
+ /* Resume interrupts. */
+ gcmkONERROR(gckOS_ResumeInterruptEx(Event->os, Event->kernel->core));
+ suspended = gcvFALSE;
+
+ if (pending == 0)
+ {
+ /* No more pending interrupts - done. */
+ break;
+ }
+
+ gcmkTRACE_ZONE_N(
+ gcvLEVEL_INFO, gcvZONE_EVENT,
+ gcmSIZEOF(pending),
+ "Pending interrupts 0x%x",
+ pending
+ );
+
+ queue = gcvNULL;
+
+ gcmDEBUG_ONLY(
+ if (IDs == 0)
+ {
+ for (i = 0; i < gcmCOUNTOF(Event->queues); ++i)
+ {
+ if (Event->queues[i].head != gcvNULL)
+ {
+ gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_EVENT,
+ "Queue(%d): stamp=%llu source=%d",
+ i,
+ Event->queues[i].stamp,
+ Event->queues[i].source);
+ }
+ }
+ }
+ );
+
+ /* Find the oldest pending interrupt. */
+ for (i = 0; i < gcmCOUNTOF(Event->queues); ++i)
+ {
+ if ((Event->queues[i].head != gcvNULL)
+ && (pending & (1 << i))
+ )
+ {
+ if ((queue == gcvNULL)
+ || (Event->queues[i].stamp < queue->stamp)
+ )
+ {
+ queue = &Event->queues[i];
+ mask = 1 << i;
+ gcmDEBUG_ONLY(eventNumber = i);
+ }
+ }
+ }
+
+ if (queue == gcvNULL)
+ {
+ gcmkTRACE_ZONE_N(
+ gcvLEVEL_ERROR, gcvZONE_EVENT,
+ gcmSIZEOF(pending),
+ "Interrupts 0x%x are not pending.",
+ pending
+ );
+
+ /* Suspend interrupts. */
+ gcmkONERROR(gckOS_SuspendInterruptEx(Event->os, Event->kernel->core));
+ suspended = gcvTRUE;
+
+ /* Mark pending interrupts as handled. */
+#ifdef __QNXNTO__
+ atomic_clr(&Event->pending, pending);
+#else
+#ifdef LINUX
+ {
+ gctUINT32 oldVal,newVal;
+ do{
+ oldVal = Event->pending;
+ newVal = oldVal & (~pending);
+ }while(atomic_cmpxchg((atomic_t *)&Event->pending,oldVal,newVal)!=oldVal);
+ }
+#else
+ Event->pending &= ~pending;
+#endif
+#endif
+
+ /* Resume interrupts. */
+ gcmkONERROR(gckOS_ResumeInterruptEx(Event->os, Event->kernel->core));
+ suspended = gcvFALSE;
+
+ break;
+ }
+
+ /* Check whether there is a missed interrupt. */
+ for (i = 0; i < gcmCOUNTOF(Event->queues); ++i)
+ {
+ if ((Event->queues[i].head != gcvNULL)
+ && (Event->queues[i].stamp < queue->stamp)
+ && (Event->queues[i].source == queue->source)
+ )
+ {
+ gcmkTRACE_N(
+ gcvLEVEL_ERROR,
+ gcmSIZEOF(i) + gcmSIZEOF(Event->queues[i].stamp),
+ "Event %d lost (stamp %llu)",
+ i, Event->queues[i].stamp
+ );
+
+ /* Use this event instead. */
+ queue = &Event->queues[i];
+ mask = 0;
+ }
+ }
+
+ if (mask != 0)
+ {
+#if gcmIS_DEBUG(gcdDEBUG_TRACE)
+ gcmkTRACE_ZONE_N(
+ gcvLEVEL_INFO, gcvZONE_EVENT,
+ gcmSIZEOF(eventNumber),
+ "Processing interrupt %d",
+ eventNumber
+ );
+#endif
+ }
+
+ /* Walk all events for this interrupt. */
+ while (1)
+ {
+ gcsEVENT_PTR record,record_next;
+#ifndef __QNXNTO__
+ gctPOINTER logical;
+#endif
+#if gcdSECURE_USER
+ gctSIZE_T bytes;
+#endif
+
+ /* Grab the mutex queue. */
+ gcmkONERROR(gckOS_AcquireMutex(Event->os,
+ Event->eventQueueMutex,
+ gcvINFINITE));
+ acquired = gcvTRUE;
+
+ /* Grab the event head. */
+ record = queue->head;
+ record_next = gcvNULL;
+ if (record != gcvNULL)
+ {
+ record_next = record->next;
+ queue->head = record_next;
+ }
+
+ /* Release the mutex queue. */
+ gcmkONERROR(gckOS_ReleaseMutex(Event->os, Event->eventQueueMutex));
+ acquired = gcvFALSE;
+
+ /* Dispatch on event type. */
+ if (record != gcvNULL)
+ {
+#ifdef __QNXNTO__
+ /* Assign record->processID as the pid for this galcore thread.
+ * Used in OS calls like gckOS_UnlockMemory() which do not take a pid.
+ */
+ drv_thread_specific_key_assign(record->processID, 0);
+#endif
+
+#if gcdSECURE_USER
+ /* Get the cache that belongs to this process. */
+ gcmkONERROR(gckKERNEL_GetProcessDBCache(Event->kernel,
+ record->processID,
+ &cache));
+#endif
+
+ gcmkTRACE_ZONE_N(
+ gcvLEVEL_INFO, gcvZONE_EVENT,
+ gcmSIZEOF(record->info.command),
+ "Processing event type: %d",
+ record->info.command
+ );
+
+ switch (record->info.command)
+ {
+ case gcvHAL_FREE_NON_PAGED_MEMORY:
+ gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_EVENT,
+ "gcvHAL_FREE_NON_PAGED_MEMORY: 0x%x",
+ record->info.u.FreeNonPagedMemory.physical);
+
+ /* Free non-paged memory. */
+ status = gckOS_FreeNonPagedMemory(
+ Event->os,
+ record->info.u.FreeNonPagedMemory.bytes,
+ record->info.u.FreeNonPagedMemory.physical,
+ record->info.u.FreeNonPagedMemory.logical);
+
+ if (gcmIS_SUCCESS(status))
+ {
+#if gcdSECURE_USER
+ gcmkVERIFY_OK(gckKERNEL_FlushTranslationCache(
+ Event->kernel,
+ cache,
+ record->event.u.FreeNonPagedMemory.logical,
+ record->event.u.FreeNonPagedMemory.bytes));
+#endif
+ }
+ break;
+
+ case gcvHAL_FREE_CONTIGUOUS_MEMORY:
+ gcmkTRACE_ZONE(
+ gcvLEVEL_VERBOSE, gcvZONE_EVENT,
+ "gcvHAL_FREE_CONTIGUOUS_MEMORY: 0x%x",
+ record->info.u.FreeContiguousMemory.physical);
+
+ /* Unmap the user memory. */
+ status = gckOS_FreeContiguous(
+ Event->os,
+ record->info.u.FreeContiguousMemory.physical,
+ record->info.u.FreeContiguousMemory.logical,
+ record->info.u.FreeContiguousMemory.bytes);
+
+ if (gcmIS_SUCCESS(status))
+ {
+#if gcdSECURE_USER
+ gcmkVERIFY_OK(gckKERNEL_FlushTranslationCache(
+ Event->kernel,
+ cache,
+ event->event.u.FreeContiguousMemory.logical,
+ event->event.u.FreeContiguousMemory.bytes));
+#endif
+ }
+ break;
+
+ case gcvHAL_FREE_VIDEO_MEMORY:
+ gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_EVENT,
+ "gcvHAL_FREE_VIDEO_MEMORY: 0x%x",
+ record->info.u.FreeVideoMemory.node);
+
+#ifdef __QNXNTO__
+ node = record->info.u.FreeVideoMemory.node;
+#if gcdUSE_VIDMEM_PER_PID
+ /* Check if the VidMem object still exists. */
+ if (gckKERNEL_GetVideoMemoryPoolPid(record->kernel,
+ gcvPOOL_SYSTEM,
+ record->processID,
+ gcvNULL) == gcvSTATUS_NOT_FOUND)
+ {
+ /*printf("Vidmem not found for process:%d\n", queue->processID);*/
+ status = gcvSTATUS_OK;
+ break;
+ }
+#else
+ if ((node->VidMem.memory->object.type == gcvOBJ_VIDMEM)
+ && (node->VidMem.logical != gcvNULL)
+ )
+ {
+ gcmkERR_BREAK(
+ gckKERNEL_UnmapVideoMemory(record->kernel,
+ node->VidMem.logical,
+ record->processID,
+ node->VidMem.bytes));
+ node->VidMem.logical = gcvNULL;
+ }
+#endif
+#endif
+
+ /* Free video memory. */
+ status =
+ gckVIDMEM_Free(record->info.u.FreeVideoMemory.node);
+
+ break;
+
+ case gcvHAL_WRITE_DATA:
+#ifndef __QNXNTO__
+ /* Convert physical into logical address. */
+ gcmkERR_BREAK(
+ gckOS_MapPhysical(Event->os,
+ record->info.u.WriteData.address,
+ gcmSIZEOF(gctUINT32),
+ &logical));
+
+ /* Write data. */
+ gcmkERR_BREAK(
+ gckOS_WriteMemory(Event->os,
+ logical,
+ record->info.u.WriteData.data));
+
+ /* Unmap the physical memory. */
+ gcmkERR_BREAK(
+ gckOS_UnmapPhysical(Event->os,
+ logical,
+ gcmSIZEOF(gctUINT32)));
+#else
+ /* Write data. */
+ gcmkERR_BREAK(
+ gckOS_WriteMemory(Event->os,
+ (gctPOINTER)
+ record->info.u.WriteData.address,
+ record->info.u.WriteData.data));
+#endif
+ break;
+
+ case gcvHAL_UNLOCK_VIDEO_MEMORY:
+ gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_EVENT,
+ "gcvHAL_UNLOCK_VIDEO_MEMORY: 0x%x",
+ record->info.u.UnlockVideoMemory.node);
+
+ /* Save node information before it disappears. */
+#if gcdSECURE_USER
+ node = event->event.u.UnlockVideoMemory.node;
+ if (node->VidMem.memory->object.type == gcvOBJ_VIDMEM)
+ {
+ logical = gcvNULL;
+ bytes = 0;
+ }
+ else
+ {
+ logical = node->Virtual.logical;
+ bytes = node->Virtual.bytes;
+ }
+#endif
+
+ /* Unlock. */
+ status = gckVIDMEM_Unlock(
+ Event->kernel,
+ record->info.u.UnlockVideoMemory.node,
+ record->info.u.UnlockVideoMemory.type,
+ gcvNULL);
+
+#if gcdSECURE_USER
+ if (gcmIS_SUCCESS(status) && (logical != gcvNULL))
+ {
+ gcmkVERIFY_OK(gckKERNEL_FlushTranslationCache(
+ Event->kernel,
+ cache,
+ logical,
+ bytes));
+ }
+#endif
+ break;
+
+ case gcvHAL_SIGNAL:
+ gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_EVENT,
+ "gcvHAL_SIGNAL: 0x%x",
+ record->info.u.Signal.signal);
+
+#ifdef __QNXNTO__
+ if ((record->info.u.Signal.coid == 0)
+ && (record->info.u.Signal.rcvid == 0)
+ )
+ {
+ /* Kernel signal. */
+ gcmkERR_BREAK(
+ gckOS_Signal(Event->os,
+ record->info.u.Signal.signal,
+ gcvTRUE));
+ }
+ else
+ {
+ /* User signal. */
+ gcmkERR_BREAK(
+ gckOS_UserSignal(Event->os,
+ record->info.u.Signal.signal,
+ record->info.u.Signal.rcvid,
+ record->info.u.Signal.coid));
+ }
+#else
+ /* Set signal. */
+ if (record->info.u.Signal.process == gcvNULL)
+ {
+ /* Kernel signal. */
+ gcmkERR_BREAK(
+ gckOS_Signal(Event->os,
+ record->info.u.Signal.signal,
+ gcvTRUE));
+ }
+ else
+ {
+ /* User signal. */
+ gcmkERR_BREAK(
+ gckOS_UserSignal(Event->os,
+ record->info.u.Signal.signal,
+ record->info.u.Signal.process));
+ }
+
+ gcmkASSERT(record->info.u.Signal.auxSignal == gcvNULL);
+#endif
+ break;
+
+ case gcvHAL_UNMAP_USER_MEMORY:
+ gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_EVENT,
+ "gcvHAL_UNMAP_USER_MEMORY: 0x%x",
+ record->info.u.UnmapUserMemory.info);
+
+ /* Unmap the user memory. */
+ status = gckOS_UnmapUserMemoryEx(
+ Event->os,
+ Event->kernel->core,
+ record->info.u.UnmapUserMemory.memory,
+ record->info.u.UnmapUserMemory.size,
+ record->info.u.UnmapUserMemory.info,
+ record->info.u.UnmapUserMemory.address);
+
+#if gcdSECURE_USER
+ if (gcmIS_SUCCESS(status))
+ {
+ gcmkVERIFY_OK(gckKERNEL_FlushTranslationCache(
+ Event->kernel,
+ cache,
+ event->event.u.UnmapUserMemory.memory,
+ event->event.u.UnmapUserMemory.size));
+ }
+#endif
+ gcmkVERIFY_OK(gckKERNEL_RemoveProcessDB(
+ Event->kernel,
+ record->processID, gcvDB_MAP_USER_MEMORY,
+ record->info.u.UnmapUserMemory.memory));
+ break;
+
+ case gcvHAL_TIMESTAMP:
+ gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_EVENT,
+ "gcvHAL_TIMESTAMP: %d %d",
+ record->info.u.TimeStamp.timer,
+ record->info.u.TimeStamp.request);
+
+ /* Process the timestamp. */
+ switch (record->info.u.TimeStamp.request)
+ {
+ case 0:
+ status = gckOS_GetTime(&Event->kernel->timers[
+ record->info.u.TimeStamp.timer].
+ stopTime);
+ break;
+
+ case 1:
+ status = gckOS_GetTime(&Event->kernel->timers[
+ record->info.u.TimeStamp.timer].
+ startTime);
+ break;
+
+ default:
+ gcmkTRACE_ZONE_N(
+ gcvLEVEL_ERROR, gcvZONE_EVENT,
+ gcmSIZEOF(record->info.u.TimeStamp.request),
+ "Invalid timestamp request: %d",
+ record->info.u.TimeStamp.request
+ );
+
+ status = gcvSTATUS_INVALID_ARGUMENT;
+ break;
+ }
+ break;
+
+ default:
+ /* Invalid argument. */
+ gcmkTRACE_ZONE_N(
+ gcvLEVEL_ERROR, gcvZONE_EVENT,
+ gcmSIZEOF(record->info.command),
+ "Unknown event type: %d",
+ record->info.command
+ );
+
+ status = gcvSTATUS_INVALID_ARGUMENT;
+ break;
+ }
+
+ /* Make sure there are no errors generated. */
+ if (gcmIS_ERROR(status))
+ {
+ gcmkTRACE_ZONE_N(
+ gcvLEVEL_WARNING, gcvZONE_EVENT,
+ gcmSIZEOF(status),
+ "Event produced status: %d(%s)",
+ status, gckOS_DebugStatus2Name(status));
+ }
+
+ /* Free the event. */
+ gcmkVERIFY_OK(gckEVENT_FreeRecord(Event, record));
+ }
+
+ //Can't use queue->head to check, as the value may be updated while it equals to NULL.
+ //So use the shadow value to check.
+ if(record_next == gcvNULL)
+ break;
+
+ }
+
+ /* Increase the number of free events. */
+ gcmkONERROR(gckOS_AtomIncrement(Event->os, Event->freeAtom, &free));
+
+ gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_EVENT,
+ "Handled interrupt 0x%x", mask);
+
+ /* Suspend interrupts. */
+ gcmkONERROR(gckOS_SuspendInterruptEx(Event->os, Event->kernel->core));
+ suspended = gcvTRUE;
+
+ /* Mark pending interrupt as handled. */
+#ifdef LINUX
+ {
+ gctUINT32 oldVal,newVal;
+ do{
+ oldVal = Event->pending;
+ newVal = oldVal & (~mask);
+ }while(atomic_cmpxchg((atomic_t *)&Event->pending,oldVal,newVal)!=oldVal);
+ }
+#elif defined __QNXNTO__
+ atomic_clr(&Event->pending, mask);
+#else
+ Event->pending &= ~mask;
+#endif
+
+ /* Resume interrupts. */
+ gcmkONERROR(gckOS_ResumeInterruptEx(Event->os, Event->kernel->core));
+ suspended = gcvFALSE;
+ }
+
+#ifndef __QNXNTO__
+
+ /* Check whether the event queue is empty. */
+ gcmkONERROR(gckEVENT_IsEmpty(Event, &empty));
+
+ if (empty && (IDs == 0))
+ {
+ /* Query whether the hardware is idle. */
+ gcmkONERROR(gckHARDWARE_QueryIdle(Event->kernel->hardware, &idle));
+
+ if (idle)
+ {
+ /* Inform the system of idle GPU. */
+ gcmkONERROR(gckOS_Broadcast(Event->os,
+ Event->kernel->hardware,
+ gcvBROADCAST_GPU_IDLE));
+ }
+ }
+#endif
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ if (acquired)
+ {
+ /* Release mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Event->os, Event->eventQueueMutex));
+ }
+
+ if (suspended)
+ {
+ /* Resume interrupts. */
+ gcmkVERIFY_OK(gckOS_ResumeInterruptEx(Event->os, Event->kernel->core));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+** gckEVENT_FreeProcess
+**
+** Free all events owned by a particular process ID.
+**
+** INPUT:
+**
+** gckEVENT Event
+** Pointer to an gckEVENT object.
+**
+** gctUINT32 ProcessID
+** Process ID of the process to be freed up.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckEVENT_FreeProcess(
+ IN gckEVENT Event,
+ IN gctUINT32 ProcessID
+ )
+{
+ gctSIZE_T i;
+ gctBOOL acquired = gcvFALSE;
+ gcsEVENT_PTR record, next;
+ gceSTATUS status;
+ gcsEVENT_PTR deleteHead, deleteTail;
+ gctBOOL empty, idle;
+
+ gcmkHEADER_ARG("Event=0x%x ProcessID=%d", Event, ProcessID);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
+
+ /* Walk through all queues. */
+ for (i = 0; i < gcmCOUNTOF(Event->queues); ++i)
+ {
+ if (Event->queues[i].head != gcvNULL)
+ {
+ /* Grab the event queue mutex. */
+ gcmkONERROR(gckOS_AcquireMutex(Event->os,
+ Event->eventQueueMutex,
+ gcvINFINITE));
+ acquired = gcvTRUE;
+
+ /* Grab the mutex head. */
+ record = Event->queues[i].head;
+ Event->queues[i].head = gcvNULL;
+ Event->queues[i].tail = gcvNULL;
+ deleteHead = gcvNULL;
+ deleteTail = gcvNULL;
+
+ while (record != gcvNULL)
+ {
+ next = record->next;
+ if (record->processID == ProcessID)
+ {
+ if (deleteHead == gcvNULL)
+ {
+ deleteHead = record;
+ }
+ else
+ {
+ deleteTail->next = record;
+ }
+
+ deleteTail = record;
+ }
+ else
+ {
+ if (Event->queues[i].head == gcvNULL)
+ {
+ Event->queues[i].head = record;
+ }
+ else
+ {
+ Event->queues[i].tail->next = record;
+ }
+
+ Event->queues[i].tail = record;
+ }
+
+ record->next = gcvNULL;
+ record = next;
+ }
+
+ /* Release the mutex queue. */
+ gcmkONERROR(gckOS_ReleaseMutex(Event->os, Event->eventQueueMutex));
+ acquired = gcvFALSE;
+
+ /* Loop through the entire list of events. */
+ for (record = deleteHead; record != gcvNULL; record = next)
+ {
+ /* Get the next event record. */
+ next = record->next;
+
+ /* Free the event record. */
+ gcmkONERROR(gckEVENT_FreeRecord(Event, record));
+ }
+ }
+ }
+
+ /*Check whether the event queue is empty.*/
+ gcmkONERROR(_IsEmpty(Event, &empty));
+
+ if (empty)
+ {
+ /* Query whether the hardware is idle. */
+ gcmkONERROR(gckHARDWARE_QueryIdle(Event->kernel->hardware, &idle));
+
+ if (idle)
+ {
+ /* Inform the system of idle GPU. */
+ gcmkONERROR(gckOS_Broadcast(Event->os,
+ Event->kernel->hardware,
+ gcvBROADCAST_GPU_IDLE));
+ }
+ }
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Release the event queue mutex. */
+ if (acquired)
+ {
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Event->os, Event->eventQueueMutex));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+** gckEVENT_Stop
+**
+** Stop the hardware using the End event mechanism.
+**
+** INPUT:
+**
+** gckEVENT Event
+** Pointer to an gckEVENT object.
+**
+** gctUINT32 ProcessID
+** Process ID Logical belongs.
+**
+** gctPHYS_ADDR Handle
+** Physical address handle. If gcvNULL it is video memory.
+**
+** gctPOINTER Logical
+** Logical address to flush.
+**
+** gctSIGNAL Signal
+** Pointer to the signal to trigger.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckEVENT_Stop(
+ IN gckEVENT Event,
+ IN gctUINT32 ProcessID,
+ IN gctPHYS_ADDR Handle,
+ IN gctPOINTER Logical,
+ IN gctSIGNAL Signal,
+ IN OUT gctSIZE_T * waitSize
+ )
+{
+ gceSTATUS status;
+ /* gctSIZE_T waitSize;*/
+ gcsEVENT_PTR record;
+ gctUINT8 id = 0xFF;
+
+ gcmkHEADER_ARG("Event=0x%x ProcessID=%u Handle=0x%x Logical=0x%x "
+ "Signal=0x%x",
+ Event, ProcessID, Handle, Logical, Signal);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
+
+ /* Submit the current event queue. */
+ gcmkONERROR(gckEVENT_Submit(Event, gcvTRUE, gcvFALSE));
+
+ gcmkONERROR(gckEVENT_GetEvent(Event, gcvTRUE, &id, gcvKERNEL_PIXEL));
+
+ /* Allocate a record. */
+ gcmkONERROR(gckEVENT_AllocateRecord(Event, gcvTRUE, &record));
+
+ /* Initialize the record. */
+ record->next = gcvNULL;
+ record->processID = ProcessID;
+ record->info.command = gcvHAL_SIGNAL;
+ record->info.u.Signal.signal = Signal;
+#ifdef __QNXNTO__
+ record->info.u.Signal.coid = 0;
+ record->info.u.Signal.rcvid = 0;
+#endif
+ record->info.u.Signal.auxSignal = gcvNULL;
+ record->info.u.Signal.process = gcvNULL;
+
+ /* Append the record. */
+ Event->queues[id].head = record;
+
+ /* Replace last WAIT with END. */
+ gcmkONERROR(gckHARDWARE_End(
+ Event->kernel->hardware, Logical, waitSize
+ ));
+
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ /* Flush the cache for the END. */
+ gcmkONERROR(gckOS_CacheClean(
+ Event->os,
+ ProcessID,
+ gcvNULL,
+ Handle,
+ Logical,
+ *waitSize
+ ));
+#endif
+
+ /* Wait for the signal. */
+ gcmkONERROR(gckOS_WaitSignal(Event->os, Signal, gcvINFINITE));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}