/******************************************************************************* ** ** gckKERNEL_GetVideoMemoryPool ** ** Get the gckVIDMEM object belonging to the specified pool. ** ** INPUT: ** ** gckKERNEL Kernel ** Pointer to an gckKERNEL object. ** ** gcePOOL Pool ** Pool to query gckVIDMEM object for. ** ** OUTPUT: ** ** gckVIDMEM * VideoMemory ** Pointer to a variable that will hold the pointer to the gckVIDMEM ** object belonging to the requested pool. */ gceSTATUS gckKERNEL_GetVideoMemoryPool( IN gckKERNEL Kernel, IN gcePOOL Pool, OUT gckVIDMEM * VideoMemory ) { GCHAL * gchal; gckVIDMEM videoMemory; gceSTATUS status; gcmkHEADER_ARG("Kernel=%p Pool=%d", Kernel, Pool); gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_KERNEL, "[ENTER] gckHARDWARE_GetVideoMemoryPool"); /* Verify the arguments. */ gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); gcmkVERIFY_ARGUMENT(VideoMemory != gcvNULL); /* Extract the pointer to the GCHAL class. */ gchal = (GCHAL *) Kernel->context; /* Dispatch on pool. */ switch (Pool) { case gcvPOOL_LOCAL_INTERNAL: /* Internal memory. */ videoMemory = gchal->GetInternalHeap(); break; case gcvPOOL_LOCAL_EXTERNAL: /* External memory. */ videoMemory = gchal->GetExternalHeap(); break; case gcvPOOL_SYSTEM: /* System memory. */ videoMemory = gchal->GetContiguousHeap(); break; default: /* Unknown pool. */ videoMemory = gcvNULL; gcmkFATAL("Unknown memory pool: %u", Pool); } /* Return pointer to the gckVIDMEM object. */ *VideoMemory = videoMemory; /* Determine the status. */ status = (videoMemory == gcvNULL) ? gcvSTATUS_OUT_OF_MEMORY : gcvSTATUS_OK; if (gcmIS_SUCCESS(status)) { gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_KERNEL, "gckHARDWARE_GetVideoMemoryPool: Pool %u starts at %p", Pool, videoMemory); } gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_KERNEL, "[LEAVE] gckHARDWARE_GetVideoMemoryPool(%u)", status); /* Return the status. */ gcmkFOOTER_ARG("status=%d, *VideoMemory=%p", status, gcmOPT_VALUE(VideoMemory)); return status; }
/******************************************************************************* ** ** gckVIDMEM_Unlock ** ** Unlock a video memory node. ** ** INPUT: ** ** gcuVIDMEM_NODE_PTR Node ** Pointer to a locked gcuVIDMEM_NODE union. ** ** gceSURF_TYPE Type ** Type of surface to unlock. ** ** gctSIZE_T * CommandSize ** Pointer to a variable specifying the number of bytes in the command ** buffer specified by 'Commands'. If gcvNULL, there is no command ** buffer and the video memory shoud be unlocked synchronously. ** ** gctBOOL * Asynchroneous ** Pointer to a variable specifying whether the surface should be ** unlocked asynchroneously or not. ** ** OUTPUT: ** ** gctBOOL * Asynchroneous ** Pointer to a variable receiving the number of bytes used in the ** command buffer specified by 'Commands'. If gcvNULL, there is no ** command buffer. */ gceSTATUS gckVIDMEM_Unlock( IN gcuVIDMEM_NODE_PTR Node, IN gceSURF_TYPE Type, IN OUT gctBOOL * Asynchroneous ) { gceSTATUS status; gckKERNEL kernel; gckHARDWARE hardware; gctPOINTER buffer; gctSIZE_T requested, bufferSize; gckCOMMAND command = gcvNULL; gceKERNEL_FLUSH flush; gckOS os = gcvNULL; gctBOOL acquired = gcvFALSE; gctBOOL needRelease = gcvFALSE; gctBOOL pendingUnlock = gcvFALSE; gcmkHEADER_ARG("Node=0x%x Type=%d *Asynchroneous=%d", Node, Type, gcmOPT_VALUE(Asynchroneous)); /* Verify the arguments. */ if ((Node == gcvNULL) || (Node->VidMem.memory == gcvNULL) ) { /* Invalid object. */ gcmkONERROR(gcvSTATUS_INVALID_OBJECT); } /**************************** Video Memory ********************************/ if (Node->VidMem.memory->object.type == gcvOBJ_VIDMEM) { if (Node->VidMem.locked <= 0) { /* The surface was not locked. */ gcmkONERROR(gcvSTATUS_MEMORY_UNLOCKED); } /* Decrement the lock count. */ Node->VidMem.locked --; if (Asynchroneous != gcvNULL) { /* No need for any events. */ *Asynchroneous = gcvFALSE; } } /*************************** Virtual Memory *******************************/ else { /* Verify the gckKERNEL object pointer. */ kernel = Node->Virtual.kernel; gcmkVERIFY_OBJECT(kernel, gcvOBJ_KERNEL); /* Verify the gckHARDWARE object pointer. */ hardware = Node->Virtual.kernel->hardware; gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE); /* Verify the gckCOMMAND object pointer. */ command = Node->Virtual.kernel->command; gcmkVERIFY_OBJECT(command, gcvOBJ_COMMAND); if (Asynchroneous == gcvNULL) { gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, "gckVIDMEM_Unlock: Unlocking virtual node 0x%x (%d)", Node, Node->Virtual.locked); /* Get the gckOS object pointer. */ os = kernel->os; gcmkVERIFY_OBJECT(os, gcvOBJ_OS); /* Grab the mutex. */ gcmkONERROR( gckOS_AcquireMutex(os, Node->Virtual.mutex, gcvINFINITE)); /* If we need to unlock a node from virtual memory we have to be ** very carefull. If the node is still inside the caches we ** might get a bus error later if the cache line needs to be ** replaced. So - we have to flush the caches before we do ** anything. We also need to stall to make sure the flush has ** happened. However - when we get to this point we are inside ** the interrupt handler and we cannot just gckCOMMAND_Wait ** because it will wait forever. So - what we do here is we ** verify the type of the surface, flush the appropriate cache, ** mark the node as flushed, and issue another unlock to unmap ** the MMU. */ if (!Node->Virtual.contiguous && (Node->Virtual.locked == 1) #ifdef __QNXTO__ && !Node->Virtual.unlockPending #else && !Node->Virtual.pending #endif ) { if (Type == gcvSURF_BITMAP) { /* Flush 2D cache. */ flush = gcvFLUSH_2D; } else if (Type == gcvSURF_RENDER_TARGET) { /* Flush color cache. */ flush = gcvFLUSH_COLOR; } else if (Type == gcvSURF_DEPTH) { /* Flush depth cache. */ flush = gcvFLUSH_DEPTH; } else { /* No flush required. */ flush = (gceKERNEL_FLUSH) 0; } gcmkONERROR( gckHARDWARE_Flush(hardware, flush, gcvNULL, &requested)); if (requested != 0) { gcmkONERROR( gckCOMMAND_Reserve(command, requested, &buffer, &bufferSize)); needRelease = gcvTRUE; gcmkONERROR(gckHARDWARE_Flush(hardware, flush, buffer, &bufferSize)); gcmkONERROR( gckEVENT_Unlock(Node->Virtual.kernel->event, gcvKERNEL_PIXEL, Node, Type)); /* Mark node as pending. */ #ifdef __QNXNTO__ Node->Virtual.unlockPending = gcvTRUE; #else Node->Virtual.pending = gcvTRUE; #endif needRelease = gcvFALSE; gcmkONERROR(gckCOMMAND_Execute(command, requested)); pendingUnlock = gcvTRUE; } } if (!pendingUnlock) { if (Node->Virtual.locked == 0) { status = gcvSTATUS_MEMORY_UNLOCKED; goto OnError; } /* Decrement lock count. */ -- Node->Virtual.locked; /* See if we can unlock the resources. */ if (Node->Virtual.locked == 0) { /* Unlock the pages. */ #ifdef __QNXNTO__ gcmkONERROR( gckOS_UnlockPages(os, Node->Virtual.physical, Node->Virtual.userPID, Node->Virtual.bytes, Node->Virtual.logical)); #else gcmkONERROR( gckOS_UnlockPages(os, Node->Virtual.physical, Node->Virtual.bytes, Node->Virtual.logical)); #endif /* Free the page table. */ if (Node->Virtual.pageTable != gcvNULL) { gcmkONERROR( gckMMU_FreePages(Node->Virtual.kernel->mmu, Node->Virtual.pageTable, Node->Virtual.pageCount)); /* Mark page table as freed. */ Node->Virtual.pageTable = gcvNULL; } /* Mark node as unlocked. */ #ifdef __QNXTO Node->Virtual.unlockPending = gcvFALSE; #else Node->Virtual.pending = gcvFALSE; #endif } gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, "Unmapped virtual node 0x%x from 0x%08X", Node, Node->Virtual.address); } /* Release the mutex. */ gcmkVERIFY_OK(gckOS_ReleaseMutex(os, Node->Virtual.mutex)); } else { gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, "Scheduled unlock for virtual node 0x%x", Node); /* Schedule the surface to be unlocked. */ *Asynchroneous = gcvTRUE; } } /* Success. */ gcmkFOOTER_ARG("*Asynchroneous=%d", gcmOPT_VALUE(Asynchroneous)); return gcvSTATUS_OK; OnError: if (needRelease) { gcmkVERIFY_OK(gckCOMMAND_Release(command)); } if (acquired) { /* Release the mutex. */ gcmkVERIFY_OK(gckOS_ReleaseMutex(os, Node->Virtual.mutex)); } /* Return the status. */ gcmkFOOTER(); return status; }
/******************************************************************************* ** gckKERNEL_DeleteRecord ** ** Remove a database record from the database and delete its structure. ** ** INPUT: ** ** gckKERNEL Kernel ** Pointer to a gckKERNEL object. ** ** gcsDATABASE_PTR Database ** Pointer to a database structure. ** ** gceDATABASE_TYPE Type ** Type of the record to remove. ** ** gctPOINTER Data ** Data of the record to remove. ** ** OUTPUT: ** ** gctSIZE_T_PTR Bytes ** Pointer to a variable that receives the size of the record deleted. ** Can be gcvNULL if the size is not required. */ static gceSTATUS gckKERNEL_DeleteRecord( IN gckKERNEL Kernel, IN gcsDATABASE_PTR Database, IN gceDATABASE_TYPE Type, IN gctPOINTER Data, OUT gctSIZE_T_PTR Bytes OPTIONAL ) { gceSTATUS status; gctBOOL acquired = gcvFALSE; gcsDATABASE_RECORD_PTR record, previous; gctUINT32 slot = _GetSlot(Database, Data); gcmkHEADER_ARG("Kernel=0x%x Database=0x%x Type=%d Data=0x%x", Kernel, Database, Type, Data); /* Acquire the database mutex. */ gcmkONERROR( gckOS_AcquireMutex(Kernel->os, Kernel->db->dbMutex, gcvINFINITE)); acquired = gcvTRUE; /* Scan the database for this record. */ for (record = Database->list[slot], previous = gcvNULL; record != gcvNULL; record = record->next ) { if ((record->type == Type) && (record->data == Data) ) { /* Found it! */ break; } previous = record; } if (record == gcvNULL) { /* Ouch! This record is not found? */ gcmkONERROR(gcvSTATUS_INVALID_DATA); } if (Bytes != gcvNULL) { /* Return size of record. */ *Bytes = record->bytes; } /* Remove record from database. */ if (previous == gcvNULL) { Database->list[slot] = record->next; } else { previous->next = record->next; } /* Insert record in free list. */ record->next = Kernel->db->freeRecord; Kernel->db->freeRecord = record; /* Release the database mutex. */ gcmkONERROR(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex)); /* Success. */ gcmkFOOTER_ARG("*Bytes=%lu", gcmOPT_VALUE(Bytes)); return gcvSTATUS_OK; OnError: if (acquired) { /* Release the database mutex. */ gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex)); } /* Return the status. */ gcmkFOOTER(); return status; }