Bool ReUseSurface(GALINFOPTR galInfo, PixmapPtr pPixmap, Viv2DPixmapPtr toBeUpdatedpPix) { GenericSurfacePtr surf = gcvNULL; gctUINT alignedWidth, alignedHeight; gctUINT bytesPerPixel; alignedWidth = gcmALIGN(pPixmap->drawable.width, WIDTH_ALIGNMENT); alignedHeight = gcmALIGN(pPixmap->drawable.height, HEIGHT_ALIGNMENT); bytesPerPixel = BITSTOBYTES(pPixmap->drawable.bitsPerPixel); /* The same as CreatSurface */ if (bytesPerPixel < 2) { bytesPerPixel = 2; } surf = (GenericSurfacePtr)toBeUpdatedpPix->mVidMemInfo; if ( surf && surf->mVideoNode.mSizeInBytes >= (alignedWidth * alignedHeight * bytesPerPixel)) { surf->mTiling = gcvLINEAR; surf->mAlignedWidth = alignedWidth; surf->mAlignedHeight = alignedHeight; surf->mStride = alignedWidth * bytesPerPixel; surf->mRotation = gcvSURF_0_DEGREE; surf->mLogicalAddr = surf->mVideoNode.mLogicalAddr; surf->mIsWrapped = gcvFALSE; if ( surf->mData ) pixman_image_unref( (pixman_image_t *)surf->mData ); surf->mData = gcvNULL; TRACE_EXIT(TRUE); } TRACE_EXIT(FALSE); }
/*Creating and Destroying Functions*/ Bool CreateSurface(GALINFOPTR galInfo, PixmapPtr pPixmap, Viv2DPixmapPtr pPix) { GenericSurfacePtr surf = gcvNULL; VIVGPUPtr gpuctx = (VIVGPUPtr) galInfo->mGpu; gctUINT alignedWidth, alignedHeight; gctUINT bytesPerPixel; alignedWidth = gcmALIGN(pPixmap->drawable.width, WIDTH_ALIGNMENT); alignedHeight = gcmALIGN(pPixmap->drawable.height, HEIGHT_ALIGNMENT); bytesPerPixel = BITSTOBYTES(pPixmap->drawable.bitsPerPixel); /*QUICK FIX*/ if (bytesPerPixel < 2) { bytesPerPixel = 2; } if (!VIV2DGPUSurfaceAlloc(gpuctx, alignedWidth, alignedHeight, bytesPerPixel, &surf)) { TRACE_ERROR("Surface Creation Error\n"); TRACE_EXIT(FALSE); } pPix->mVidMemInfo = surf; TRACE_EXIT(TRUE); }
Bool WrapSurface(PixmapPtr pPixmap, void * logical, unsigned int physical, Viv2DPixmapPtr pPix) { gceSTATUS status = gcvSTATUS_OK; gctUINT alignedWidth, alignedHeight; gctUINT bytesPerPixel; GenericSurfacePtr surf = gcvNULL; gctPOINTER mHandle = gcvNULL; status = gcoOS_Allocate(gcvNULL, sizeof (GenericSurface), &mHandle); if (status != gcvSTATUS_OK) { TRACE_ERROR("Unable to allocate generic surface\n"); TRACE_EXIT(FALSE); } memset(mHandle, 0, sizeof (GenericSurface)); surf = (GenericSurfacePtr) mHandle; alignedWidth = gcmALIGN(pPixmap->drawable.width, WIDTH_ALIGNMENT); alignedHeight = gcmALIGN(pPixmap->drawable.height, HEIGHT_ALIGNMENT); bytesPerPixel = BITSTOBYTES(pPixmap->drawable.bitsPerPixel); surf->mVideoNode.mSizeInBytes = alignedWidth * bytesPerPixel * alignedHeight; surf->mVideoNode.mPool = gcvPOOL_USER; surf->mVideoNode.mPhysicalAddr = physical; surf->mVideoNode.mLogicalAddr = (gctPOINTER) logical; surf->mBytesPerPixel = bytesPerPixel; surf->mTiling = gcvLINEAR; surf->mAlignedWidth = alignedWidth; surf->mAlignedHeight = alignedHeight; surf->mStride = alignedWidth * bytesPerPixel; surf->mRotation = gcvSURF_0_DEGREE; surf->mLogicalAddr = surf->mVideoNode.mLogicalAddr; surf->mIsWrapped = gcvTRUE; pPix->mVidMemInfo = surf; TRACE_EXIT(TRUE); }
gceSTATUS gcfDumpData( IN gcoOS Os, IN gctSTRING Tag, IN gctPOINTER Logical, IN gctSIZE_T Bytes ) { gctUINT32_PTR ptr = (gctUINT32_PTR) Logical; gctSIZE_T bytes = gcmALIGN(Bytes, 4); if (!setDumpFlag) return gcvSTATUS_OK; while (bytes >= 16) { gcmDUMP(Os, " 0x%08X 0x%08X 0x%08X 0x%08X", ptr[0], ptr[1], ptr[2], ptr[3]); ptr += 4; bytes -= 16; } switch (bytes) { case 12: gcmDUMP(Os, " 0x%08X 0x%08X 0x%08X", ptr[0], ptr[1], ptr[2]); break; case 8: gcmDUMP(Os, " 0x%08X 0x%08X", ptr[0], ptr[1]); break; case 4: gcmDUMP(Os, " 0x%08X", ptr[0]); break; } gcmDUMP(Os, "] -- %s", Tag); return gcvSTATUS_OK; }
/******************************************************************************* ** ** gckHEAP_Allocate ** ** Allocate data from the heap. ** ** INPUT: ** ** gckHEAP Heap ** Pointer to a gckHEAP object. ** ** IN gctSIZE_T Bytes ** Number of byte to allocate. ** ** OUTPUT: ** ** gctPOINTER * Memory ** Pointer to a variable that will hold the address of the allocated ** memory. */ gceSTATUS gckHEAP_Allocate( IN gckHEAP Heap, IN gctSIZE_T Bytes, OUT gctPOINTER * Memory ) { gctBOOL acquired = gcvFALSE; gcskHEAP_PTR heap; gceSTATUS status; gctSIZE_T bytes; gcskNODE_PTR node, used, prevFree = gcvNULL; gctPOINTER memory = gcvNULL; gcmkHEADER_ARG("Heap=0x%x Bytes=%lu", Heap, Bytes); /* Verify the arguments. */ gcmkVERIFY_OBJECT(Heap, gcvOBJ_HEAP); gcmkVERIFY_ARGUMENT(Bytes > 0); gcmkVERIFY_ARGUMENT(Memory != gcvNULL); /* Determine number of bytes required for a node. */ bytes = gcmALIGN(Bytes + gcmSIZEOF(gcskNODE), 8); /* Acquire the mutex. */ gcmkONERROR( gckOS_AcquireMutex(Heap->os, Heap->mutex, gcvINFINITE)); acquired = gcvTRUE; /* Check if this allocation is bigger than the default allocation size. */ if (bytes > Heap->allocationSize - gcmSIZEOF(gcskHEAP) - gcmSIZEOF(gcskNODE)) { /* Adjust allocation size. */ Heap->allocationSize = bytes * 2; } else if (Heap->heap != gcvNULL) { gctINT i; /* 2 retries, since we might need to compact. */ for (i = 0; i < 2; ++i) { /* Walk all the heaps. */ for (heap = Heap->heap; heap != gcvNULL; heap = heap->next) { /* Check if this heap has enough bytes to hold the request. */ if (bytes <= heap->size - gcmSIZEOF(gcskNODE)) { prevFree = gcvNULL; /* Walk the chain of free nodes. */ for (node = heap->freeList; node != gcvNULL; node = node->next ) { gcmkASSERT(node->next != gcdIN_USE); /* Check if this free node has enough bytes. */ if (node->bytes >= bytes) { /* Use the node. */ goto UseNode; } /* Save current free node for linked list management. */ prevFree = node; } } } if (i == 0) { /* Compact the heap. */ gcmkVERIFY_OK(_CompactKernelHeap(Heap)); #if gcmIS_DEBUG(gcdDEBUG_CODE) gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP, "===== KERNEL HEAP ====="); gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP, "Number of allocations : %12u", Heap->allocCount); gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP, "Number of bytes allocated : %12llu", Heap->allocBytes); gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP, "Maximum allocation size : %12llu", Heap->allocBytesMax); gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP, "Total number of bytes allocated : %12llu", Heap->allocBytesTotal); gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP, "Number of heaps : %12u", Heap->heapCount); gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP, "Heap memory in bytes : %12llu", Heap->heapMemory); gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP, "Maximum number of heaps : %12u", Heap->heapCountMax); gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP, "Maximum heap memory in bytes : %12llu", Heap->heapMemoryMax); #endif } } } /* Release the mutex. */ gcmkONERROR( gckOS_ReleaseMutex(Heap->os, Heap->mutex)); acquired = gcvFALSE; /* Allocate a new heap. */ gcmkONERROR( gckOS_AllocateMemory(Heap->os, Heap->allocationSize, &memory)); gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HEAP, "Allocated heap 0x%x (%lu bytes)", memory, Heap->allocationSize); /* Acquire the mutex. */ gcmkONERROR( gckOS_AcquireMutex(Heap->os, Heap->mutex, gcvINFINITE)); acquired = gcvTRUE; /* Use the allocated memory as the heap. */ heap = (gcskHEAP_PTR) memory; /* Insert this heap to the head of the chain. */ heap->next = Heap->heap; heap->prev = gcvNULL; heap->size = Heap->allocationSize - gcmSIZEOF(gcskHEAP); if (heap->next != gcvNULL) { heap->next->prev = heap; } Heap->heap = heap; /* Mark the end of the heap. */ node = (gcskNODE_PTR) ( (gctUINT8_PTR) heap + Heap->allocationSize - gcmSIZEOF(gcskNODE) ); node->bytes = 0; node->next = gcvNULL; /* Create a free list. */ node = (gcskNODE_PTR) (heap + 1); heap->freeList = node; /* Initialize the free list. */ node->bytes = heap->size - gcmSIZEOF(gcskNODE); node->next = gcvNULL; /* No previous free. */ prevFree = gcvNULL; #if VIVANTE_PROFILER || gcmIS_DEBUG(gcdDEBUG_CODE) /* Update profiling. */ Heap->heapCount += 1; Heap->heapMemory += Heap->allocationSize; if (Heap->heapCount > Heap->heapCountMax) { Heap->heapCountMax = Heap->heapCount; } if (Heap->heapMemory > Heap->heapMemoryMax) { Heap->heapMemoryMax = Heap->heapMemory; } #endif UseNode: /* Verify some stuff. */ gcmkASSERT(heap != gcvNULL); gcmkASSERT(node != gcvNULL); gcmkASSERT(node->bytes >= bytes); if (heap->prev != gcvNULL) { /* Unlink the heap from the linked list. */ heap->prev->next = heap->next; if (heap->next != gcvNULL) { heap->next->prev = heap->prev; } /* Move the heap to the front of the list. */ heap->next = Heap->heap; heap->prev = gcvNULL; Heap->heap = heap; heap->next->prev = heap; } /* Check if there is enough free space left after usage for another free ** node. */ if (node->bytes - bytes >= gcmSIZEOF(gcskNODE)) { /* Allocated used space from the back of the free list. */ used = (gcskNODE_PTR) ((gctUINT8_PTR) node + node->bytes - bytes); /* Adjust the number of free bytes. */ node->bytes -= bytes; gcmkASSERT(node->bytes >= gcmSIZEOF(gcskNODE)); } else { /* Remove this free list from the chain. */ if (prevFree == gcvNULL) { heap->freeList = node->next; } else { prevFree->next = node->next; } /* Consume the entire free node. */ used = (gcskNODE_PTR) node; bytes = node->bytes; } /* Mark node as used. */ used->bytes = bytes; used->next = gcdIN_USE; #if gcmIS_DEBUG(gcdDEBUG_CODE) used->timeStamp = ++Heap->timeStamp; #endif #if VIVANTE_PROFILER || gcmIS_DEBUG(gcdDEBUG_CODE) /* Update profile counters. */ Heap->allocCount += 1; Heap->allocBytes += bytes; Heap->allocBytesMax = gcmMAX(Heap->allocBytes, Heap->allocBytesMax); Heap->allocBytesTotal += bytes; #endif /* Release the mutex. */ gcmkVERIFY_OK( gckOS_ReleaseMutex(Heap->os, Heap->mutex)); /* Return pointer to memory. */ *Memory = used + 1; /* Success. */ gcmkFOOTER_ARG("*Memory=0x%x", *Memory); return gcvSTATUS_OK; OnError: if (acquired) { /* Release the mutex. */ gcmkVERIFY_OK( gckOS_ReleaseMutex(Heap->os, Heap->mutex)); } if (memory != gcvNULL) { /* Free the heap memory. */ gckOS_FreeMemory(Heap->os, memory); } /* Return the status. */ gcmkFOOTER(); return status; }
static gctSIZE_T _MeasureState( IN gcoVGHARDWARE Hardware, IN gcsVGCONTEXT_INIT_PTR InitInfo, IN gctSIZE_T BufferIndex, IN gctUINT32 StateIndex, IN gctUINT32 ResetValue, IN gctSIZE_T ValueCount ) { gctSIZE_T result; gctSIZE_T mapLast; /* End of buffer alignement? */ if (ValueCount == 0) { /* Save information for this LoadState. */ InitInfo->bufferIndex = gcmALIGN(BufferIndex, 2); /* Return end of buffer. */ result = 0; } else { /* See if we can append this state to the previous one. */ if (StateIndex == InitInfo->stateIndex) { /* Update the state index. */ InitInfo->stateIndex += ValueCount; /* Return number of slots required. */ result = ValueCount; } else { /* Determine if we need alignment. */ gctSIZE_T align = BufferIndex & 1; /* Save information for this LoadState. */ InitInfo->bufferIndex = BufferIndex + align; InitInfo->stateIndex = StateIndex + ValueCount; /* Return number of slots required. */ result = align + 1 + ValueCount; } /* Determine the new map size. */ mapLast = StateIndex + ValueCount - 1; /* Update the map dimensions as necessary. */ #ifndef __QNXNTO__ if (mapLast > Hardware->context.mapLast) { Hardware->context.mapLast = mapLast; } if (StateIndex < Hardware->context.mapFirst) { Hardware->context.mapFirst = StateIndex; } #else if (mapLast > Hardware->pContext->mapLast) { Hardware->pContext->mapLast = mapLast; } if (StateIndex < Hardware->pContext->mapFirst) { Hardware->pContext->mapFirst = StateIndex; } #endif } /* Return size for load state. */ return result; }
/******************************************************************************* ** ** gcoBUFFER_Commit ** ** Commit the command buffer to the hardware. ** ** INPUT: ** ** gcoBUFFER Buffer ** Pointer to a gcoBUFFER object. ** ** gcePIPE_SELECT CurrentPipe ** Current graphics pipe. ** ** gcsSTATE_DELTA_PTR StateDelta ** Pointer to the state delta. ** ** gcoQUEUE Queue ** Pointer to a gcoQUEUE object. ** ** OUTPUT: ** ** Nothing. */ gceSTATUS gcoBUFFER_Commit( IN gcoBUFFER Buffer, IN gcePIPE_SELECT CurrentPipe, IN gcsSTATE_DELTA_PTR StateDelta, IN gcoQUEUE Queue ) { gcsHAL_INTERFACE iface; gceSTATUS status; gcoCMDBUF current; gcmHEADER_ARG("Buffer=0x%x Queue=0x%x", Buffer, Queue); /* Verify the arguments. */ gcmVERIFY_OBJECT(Buffer, gcvOBJ_BUFFER); gcmVERIFY_OBJECT(Queue, gcvOBJ_QUEUE); /* Grab current command buffer. */ current = Buffer->currentCommandBuffer; if (current == gcvNULL) { /* No command buffer, done. */ gcmFOOTER_NO(); return gcvSTATUS_OK; } if (current->offset - current->startOffset <= Buffer->info.reservedHead) { /* Commit the event queue. */ status = gcoQUEUE_Commit(Queue); gcmFOOTER(); return status; } /* Make sure the tail got aligned properly. */ current->offset = gcmALIGN(current->offset, Buffer->info.alignment); if (gcPLS.hal->dump != gcvNULL) { /* Dump the command buffer. */ gcmVERIFY_OK( gcoDUMP_DumpData(gcPLS.hal->dump, gcvTAG_COMMAND, 0, current->offset - current->startOffset - Buffer->info.reservedHead, (gctUINT8_PTR) current->logical + current->startOffset + Buffer->info.reservedHead)); } /* The current pipe becomes the exit pipe for the current command buffer. */ current->exitPipe = CurrentPipe; /* Send command and context buffer to hardware. */ iface.command = gcvHAL_COMMIT; iface.u.Commit.context = #ifndef VIVANTE_NO_3D (current->using2D && !current->using3D) ? gcvNULL : Buffer->context; #else gcvNULL; #endif iface.u.Commit.commandBuffer = current; iface.u.Commit.delta = StateDelta; iface.u.Commit.queue = Queue->head; /* Call kernel service. */ gcmONERROR( gcoOS_DeviceControl(gcvNULL, IOCTL_GCHAL_INTERFACE, &iface, gcmSIZEOF(iface), &iface, gcmSIZEOF(iface))); gcmONERROR(iface.status); /* Free the event queue. */ gcmONERROR(gcoQUEUE_Free(Queue)); /* Advance the offset for next commit. */ current->startOffset = current->offset + Buffer->info.reservedTail; if (current->bytes - current->startOffset > Buffer->totalReserved) { /* Adjust buffer offset and size. */ current->offset = current->startOffset + Buffer->info.reservedHead; current->free = current->bytes - current->offset - Buffer->info.alignment - Buffer->info.reservedTail; } else { /* Buffer is full. */ current->startOffset = current->bytes; current->offset = current->bytes; current->free = 0; } /* The exit pipe becomes the entry pipe for the next command buffer. */ current->entryPipe = current->exitPipe; #if gcdSECURE_USER /* Reset the state array tail. */ current->hintArrayTail = current->hintArray; #endif /* Reset usage flags. */ current->using2D = gcvFALSE; current->using3D = gcvFALSE; current->usingFilterBlit = gcvFALSE; current->usingPalette = gcvFALSE; /* Success. */ gcmFOOTER_NO(); return gcvSTATUS_OK; OnError: /* Return the status. */ gcmFOOTER(); return status; }
/******************************************************************************* ** ** gcoBUFFER_Reserve ** ** Reserve a number of bytes in the buffer. ** ** INPUT: ** ** gcoBUFFER Buffer ** Pointer to an gcoBUFFER object. ** ** gctSIZE_T Bytes ** Number of bytes to reserve. ** ** gctBOOL Aligned ** gcvTRUE if the data needs to be aligned to 64-bit. ** ** OUTPUT: ** ** gctUINT32_PTR ** AddressHints ** Pointer to a variable that receives the current position in the ** state hint array. gcvNULL is allowed. ** ** gctPOINTER * Memory ** Pointer to a variable that will hold the address of location in the ** buffer that has been reserved. */ gceSTATUS gcoBUFFER_Reserve( IN gcoBUFFER Buffer, IN gctSIZE_T Bytes, IN gctBOOL Aligned, OUT gcoCMDBUF * Reserve ) { gceSTATUS status = gcvSTATUS_OK; gcoCMDBUF current; gctSIZE_T alignBytes, bytes; gctUINT offset; gcmHEADER_ARG("Buffer=0x%x Bytes=%lu Aligned=%d Reserve=0x%x", Buffer, Bytes, Aligned, Reserve); /* Verify the arguments. */ gcmVERIFY_OBJECT(Buffer, gcvOBJ_BUFFER); gcmDEBUG_VERIFY_ARGUMENT(Reserve != gcvNULL); /* Get the current command buffer. */ current = Buffer->currentCommandBuffer; /* Compute the number of aligned bytes. */ alignBytes = Aligned ? ( gcmALIGN(current->offset, Buffer->info.alignment) - current->offset ) : 0; /* Compute the number of required bytes. */ bytes = Bytes + alignBytes; if (bytes > current->free) { gcsHAL_INTERFACE iface; if (bytes > Buffer->maxSize - Buffer->totalReserved) { /* This just won't fit! */ gcmFATAL("FATAL: Command of %lu bytes is too big!", Bytes); gcmONERROR(gcvSTATUS_OUT_OF_MEMORY); } /* Sent event to signal when command buffer completes. */ iface.command = gcvHAL_SIGNAL; iface.u.Signal.signal = Buffer->signal [Buffer->currentCommandBufferIndex]; iface.u.Signal.auxSignal = gcvNULL; iface.u.Signal.process = gcoOS_GetCurrentProcessID(); iface.u.Signal.fromWhere = gcvKERNEL_COMMAND; /* Send event. */ gcmONERROR( gcoHARDWARE_CallEvent(&iface)); /* Commit current command buffer. */ gcmONERROR( gcoHARDWARE_Commit()); /* Grab a new command buffer. */ gcmONERROR( gcoBUFFER_GetCMDBUF(Buffer)); /* Get the pointer. */ current = Buffer->currentCommandBuffer; /* Calculate total bytes again. */ alignBytes = 0; bytes = Bytes; } gcmASSERT(current != gcvNULL); gcmASSERT(bytes <= current->free); /* Determine the data offset. */ offset = current->offset + alignBytes; /* Update the last reserved location. */ current->lastReserve = (gctUINT8_PTR) current->logical + offset; current->lastOffset = offset; /* Adjust command buffer size. */ current->offset += bytes; current->free -= bytes; /* Set the result. */ * Reserve = current; /* Success. */ gcmFOOTER(); return gcvSTATUS_OK; OnError: /* Return the status. */ gcmFOOTER(); return status; }
/******************************************************************************* ** ** gckVIDMEM_Construct ** ** Construct a new gckVIDMEM object. ** ** INPUT: ** ** gckOS Os ** Pointer to an gckOS object. ** ** gctUINT32 BaseAddress ** Base address for the video memory heap. ** ** gctSIZE_T Bytes ** Number of bytes in the video memory heap. ** ** gctSIZE_T Threshold ** Minimum number of bytes beyond am allocation before the node is ** split. Can be used as a minimum alignment requirement. ** ** gctSIZE_T BankSize ** Number of bytes per physical memory bank. Used by bank ** optimization. ** ** OUTPUT: ** ** gckVIDMEM * Memory ** Pointer to a variable that will hold the pointer to the gckVIDMEM ** object. */ gceSTATUS gckVIDMEM_Construct( IN gckOS Os, IN gctUINT32 BaseAddress, IN gctSIZE_T Bytes, IN gctSIZE_T Threshold, IN gctSIZE_T BankSize, OUT gckVIDMEM * Memory ) { gckVIDMEM memory = gcvNULL; gceSTATUS status; gcuVIDMEM_NODE_PTR node; gctINT i, banks = 0; gcmkHEADER_ARG("Os=0x%x BaseAddress=%08x Bytes=%lu Threshold=%lu " "BankSize=%lu", Os, BaseAddress, Bytes, Threshold, BankSize); /* Verify the arguments. */ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); gcmkVERIFY_ARGUMENT(Bytes > 0); gcmkVERIFY_ARGUMENT(Memory != gcvNULL); /* Allocate the gckVIDMEM object. */ gcmkONERROR( gckOS_Allocate(Os, gcmSIZEOF(struct _gckVIDMEM), (gctPOINTER *) &memory)); /* Initialize the gckVIDMEM object. */ memory->object.type = gcvOBJ_VIDMEM; memory->os = Os; /* Set video memory heap information. */ memory->baseAddress = BaseAddress; memory->bytes = Bytes; memory->freeBytes = Bytes; memory->threshold = Threshold; memory->mutex = gcvNULL; BaseAddress = 0; /* Walk all possible banks. */ for (i = 0; i < gcmCOUNTOF(memory->sentinel); ++i) { gctSIZE_T bytes; if (BankSize == 0) { /* Use all bytes for the first bank. */ bytes = Bytes; } else { /* Compute number of bytes for this bank. */ bytes = gcmALIGN(BaseAddress + 1, BankSize) - BaseAddress; if (bytes > Bytes) { /* Make sure we don't exceed the total number of bytes. */ bytes = Bytes; } } if (bytes == 0) { /* Mark heap is not used. */ memory->sentinel[i].VidMem.next = memory->sentinel[i].VidMem.prev = memory->sentinel[i].VidMem.nextFree = memory->sentinel[i].VidMem.prevFree = gcvNULL; continue; } /* Allocate one gcuVIDMEM_NODE union. */ gcmkONERROR( gckOS_Allocate(Os, gcmSIZEOF(gcuVIDMEM_NODE), (gctPOINTER *) &node)); /* Initialize gcuVIDMEM_NODE union. */ node->VidMem.memory = memory; node->VidMem.next = node->VidMem.prev = node->VidMem.nextFree = node->VidMem.prevFree = &memory->sentinel[i]; node->VidMem.offset = BaseAddress; node->VidMem.bytes = bytes; node->VidMem.alignment = 0; node->VidMem.physical = 0; node->VidMem.pool = gcvPOOL_UNKNOWN; node->VidMem.locked = 0; #ifdef __QNXNTO__ node->VidMem.logical = gcvNULL; node->VidMem.handle = 0; #endif /* Initialize the linked list of nodes. */ memory->sentinel[i].VidMem.next = memory->sentinel[i].VidMem.prev = memory->sentinel[i].VidMem.nextFree = memory->sentinel[i].VidMem.prevFree = node; /* Mark sentinel. */ memory->sentinel[i].VidMem.bytes = 0; /* Adjust address for next bank. */ BaseAddress += bytes; Bytes -= bytes; banks ++; } /* Assign all the bank mappings. */ memory->mapping[gcvSURF_RENDER_TARGET] = banks - 1; memory->mapping[gcvSURF_BITMAP] = banks - 1; if (banks > 1) --banks; memory->mapping[gcvSURF_DEPTH] = banks - 1; memory->mapping[gcvSURF_HIERARCHICAL_DEPTH] = banks - 1; if (banks > 1) --banks; memory->mapping[gcvSURF_TEXTURE] = banks - 1; if (banks > 1) --banks; memory->mapping[gcvSURF_VERTEX] = banks - 1; if (banks > 1) --banks; memory->mapping[gcvSURF_INDEX] = banks - 1; if (banks > 1) --banks; memory->mapping[gcvSURF_TILE_STATUS] = banks - 1; if (banks > 1) --banks; memory->mapping[gcvSURF_TYPE_UNKNOWN] = 0; gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, "[GALCORE] INDEX: bank %d", memory->mapping[gcvSURF_INDEX]); gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, "[GALCORE] VERTEX: bank %d", memory->mapping[gcvSURF_VERTEX]); gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, "[GALCORE] TEXTURE: bank %d", memory->mapping[gcvSURF_TEXTURE]); gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, "[GALCORE] RENDER_TARGET: bank %d", memory->mapping[gcvSURF_RENDER_TARGET]); gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, "[GALCORE] DEPTH: bank %d", memory->mapping[gcvSURF_DEPTH]); gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, "[GALCORE] TILE_STATUS: bank %d", memory->mapping[gcvSURF_TILE_STATUS]); /* Allocate the mutex. */ gcmkONERROR(gckOS_CreateMutex(Os, &memory->mutex)); /* Return pointer to the gckVIDMEM object. */ *Memory = memory; /* Success. */ gcmkFOOTER_ARG("*Memory=0x%x", *Memory); return gcvSTATUS_OK; OnError: /* Roll back. */ if (memory != gcvNULL) { if (memory->mutex != gcvNULL) { /* Delete the mutex. */ gcmkVERIFY_OK(gckOS_DeleteMutex(Os, memory->mutex)); } for (i = 0; i < banks; ++i) { /* Free the heap. */ gcmkASSERT(memory->sentinel[i].VidMem.next != gcvNULL); gcmkVERIFY_OK(gckOS_Free(Os, memory->sentinel[i].VidMem.next)); } /* Free the object. */ gcmkVERIFY_OK(gckOS_Free(Os, memory)); } /* Return the status. */ gcmkFOOTER(); return status; }
/******************************************************************************* ** ** gckKERNEL_Dispatch ** ** Dispatch a command received from the user HAL layer. ** ** INPUT: ** ** gckKERNEL Kernel ** Pointer to an gckKERNEL object. ** ** gcsHAL_INTERFACE * Interface ** Pointer to a gcsHAL_INTERFACE structure that defines the command to ** be dispatched. ** ** OUTPUT: ** ** gcsHAL_INTERFACE * Interface ** Pointer to a gcsHAL_INTERFACE structure that receives any data to be ** returned. */ gceSTATUS gckVGKERNEL_Dispatch( IN gckKERNEL Kernel, IN gctBOOL FromUser, IN OUT gcsHAL_INTERFACE * Interface ) { gceSTATUS status; gcsHAL_INTERFACE * kernelInterface = Interface; gcuVIDMEM_NODE_PTR node; gctUINT32 processID; gcmkHEADER_ARG("Kernel=0x%x Interface=0x%x ", Kernel, Interface); /* Verify the arguments. */ gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); gcmkVERIFY_ARGUMENT(Interface != gcvNULL); gcmkONERROR(gckOS_GetProcessID(&processID)); /* Dispatch on command. */ switch (Interface->command) { case gcvHAL_QUERY_VIDEO_MEMORY: /* Query video memory size. */ gcmkERR_BREAK(gckKERNEL_QueryVideoMemory( Kernel, kernelInterface )); break; case gcvHAL_QUERY_CHIP_IDENTITY: /* Query chip identity. */ gcmkERR_BREAK(gckVGHARDWARE_QueryChipIdentity( Kernel->vg->hardware, &kernelInterface->u.QueryChipIdentity.chipModel, &kernelInterface->u.QueryChipIdentity.chipRevision, &kernelInterface->u.QueryChipIdentity.chipFeatures, &kernelInterface->u.QueryChipIdentity.chipMinorFeatures, &kernelInterface->u.QueryChipIdentity.chipMinorFeatures2 )); break; case gcvHAL_QUERY_COMMAND_BUFFER: /* Query command buffer information. */ gcmkERR_BREAK(gckKERNEL_QueryCommandBuffer( Kernel, &kernelInterface->u.QueryCommandBuffer.information )); break; case gcvHAL_ALLOCATE_NON_PAGED_MEMORY: /* Allocate non-paged memory. */ gcmkERR_BREAK(gckOS_AllocateContiguous( Kernel->os, gcvTRUE, &kernelInterface->u.AllocateNonPagedMemory.bytes, &kernelInterface->u.AllocateNonPagedMemory.physical, &kernelInterface->u.AllocateNonPagedMemory.logical )); break; case gcvHAL_FREE_NON_PAGED_MEMORY: /* Unmap user logical out of physical memory first. */ gcmkERR_BREAK(gckOS_UnmapUserLogical( Kernel->os, kernelInterface->u.AllocateNonPagedMemory.physical, kernelInterface->u.AllocateNonPagedMemory.bytes, kernelInterface->u.AllocateNonPagedMemory.logical )); /* Free non-paged memory. */ gcmkERR_BREAK(gckOS_FreeNonPagedMemory( Kernel->os, kernelInterface->u.AllocateNonPagedMemory.bytes, kernelInterface->u.AllocateNonPagedMemory.physical, kernelInterface->u.AllocateNonPagedMemory.logical )); break; case gcvHAL_ALLOCATE_CONTIGUOUS_MEMORY: /* Allocate contiguous memory. */ gcmkERR_BREAK(gckOS_AllocateContiguous( Kernel->os, gcvTRUE, &kernelInterface->u.AllocateNonPagedMemory.bytes, &kernelInterface->u.AllocateNonPagedMemory.physical, &kernelInterface->u.AllocateNonPagedMemory.logical )); break; case gcvHAL_FREE_CONTIGUOUS_MEMORY: /* Unmap user logical out of physical memory first. */ gcmkERR_BREAK(gckOS_UnmapUserLogical( Kernel->os, kernelInterface->u.AllocateNonPagedMemory.physical, kernelInterface->u.AllocateNonPagedMemory.bytes, kernelInterface->u.AllocateNonPagedMemory.logical )); /* Free contiguous memory. */ gcmkERR_BREAK(gckOS_FreeContiguous( Kernel->os, kernelInterface->u.AllocateNonPagedMemory.physical, kernelInterface->u.AllocateNonPagedMemory.logical, kernelInterface->u.AllocateNonPagedMemory.bytes )); break; case gcvHAL_ALLOCATE_VIDEO_MEMORY: { gctSIZE_T bytes; gctUINT32 bitsPerPixel; gctUINT32 bits; /* Align width and height to tiles. */ gcmkERR_BREAK(gckVGHARDWARE_AlignToTile( Kernel->vg->hardware, kernelInterface->u.AllocateVideoMemory.type, &kernelInterface->u.AllocateVideoMemory.width, &kernelInterface->u.AllocateVideoMemory.height )); /* Convert format into bytes per pixel and bytes per tile. */ gcmkERR_BREAK(gckVGHARDWARE_ConvertFormat( Kernel->vg->hardware, kernelInterface->u.AllocateVideoMemory.format, &bitsPerPixel, gcvNULL )); /* Compute number of bits for the allocation. */ bits = kernelInterface->u.AllocateVideoMemory.width * kernelInterface->u.AllocateVideoMemory.height * kernelInterface->u.AllocateVideoMemory.depth * bitsPerPixel; /* Compute number of bytes for the allocation. */ bytes = gcmALIGN(bits, 8) / 8; /* Allocate memory. */ gcmkERR_BREAK(gckKERNEL_AllocateLinearMemory( Kernel, &kernelInterface->u.AllocateVideoMemory.pool, bytes, 64, kernelInterface->u.AllocateVideoMemory.type, &kernelInterface->u.AllocateVideoMemory.node )); } break; case gcvHAL_ALLOCATE_LINEAR_VIDEO_MEMORY: /* Allocate memory. */ gcmkERR_BREAK(gckKERNEL_AllocateLinearMemory( Kernel, &kernelInterface->u.AllocateLinearVideoMemory.pool, kernelInterface->u.AllocateLinearVideoMemory.bytes, kernelInterface->u.AllocateLinearVideoMemory.alignment, kernelInterface->u.AllocateLinearVideoMemory.type, &kernelInterface->u.AllocateLinearVideoMemory.node )); gcmkERR_BREAK(gckKERNEL_AddProcessDB(Kernel, processID, gcvDB_VIDEO_MEMORY, Interface->u.AllocateLinearVideoMemory.node, gcvNULL, kernelInterface->u.AllocateLinearVideoMemory.bytes )); break; case gcvHAL_FREE_VIDEO_MEMORY: #ifdef __QNXNTO__ /* Unmap the video memory */ node = Interface->u.FreeVideoMemory.node; if ((node->VidMem.memory->object.type == gcvOBJ_VIDMEM) && (node->VidMem.logical != gcvNULL)) { gckKERNEL_UnmapVideoMemory(Kernel, node->VidMem.logical, processID, node->VidMem.bytes); node->VidMem.logical = gcvNULL; } #endif /* __QNXNTO__ */ /* Free video memory. */ gcmkERR_BREAK(gckVIDMEM_Free( Interface->u.FreeVideoMemory.node )); gcmkERR_BREAK(gckKERNEL_RemoveProcessDB( Kernel, processID, gcvDB_VIDEO_MEMORY, Interface->u.FreeVideoMemory.node )); break; case gcvHAL_MAP_MEMORY: /* Map memory. */ gcmkERR_BREAK(gckKERNEL_MapMemory( Kernel, kernelInterface->u.MapMemory.physical, kernelInterface->u.MapMemory.bytes, &kernelInterface->u.MapMemory.logical )); break; case gcvHAL_UNMAP_MEMORY: /* Unmap memory. */ gcmkERR_BREAK(gckKERNEL_UnmapMemory( Kernel, kernelInterface->u.MapMemory.physical, kernelInterface->u.MapMemory.bytes, kernelInterface->u.MapMemory.logical )); break; case gcvHAL_MAP_USER_MEMORY: /* Map user memory to DMA. */ gcmkERR_BREAK(gckOS_MapUserMemory( Kernel->os, gcvCORE_VG, kernelInterface->u.MapUserMemory.memory, kernelInterface->u.MapUserMemory.physical, kernelInterface->u.MapUserMemory.size, &kernelInterface->u.MapUserMemory.info, &kernelInterface->u.MapUserMemory.address )); break; case gcvHAL_UNMAP_USER_MEMORY: /* Unmap user memory. */ gcmkERR_BREAK(gckOS_UnmapUserMemory( Kernel->os, gcvCORE_VG, kernelInterface->u.UnmapUserMemory.memory, kernelInterface->u.UnmapUserMemory.size, kernelInterface->u.UnmapUserMemory.info, kernelInterface->u.UnmapUserMemory.address )); break; case gcvHAL_LOCK_VIDEO_MEMORY: /* Lock video memory. */ gcmkERR_BREAK( gckVIDMEM_Lock(Kernel, Interface->u.LockVideoMemory.node, gcvFALSE, &Interface->u.LockVideoMemory.address)); node = Interface->u.LockVideoMemory.node; if (node->VidMem.memory->object.type == gcvOBJ_VIDMEM) { /* Map video memory address into user space. */ #ifdef __QNXNTO__ if (node->VidMem.logical == gcvNULL) { gcmkONERROR( gckKERNEL_MapVideoMemory(Kernel, FromUser, Interface->u.LockVideoMemory.address, processID, node->VidMem.bytes, &node->VidMem.logical)); } Interface->u.LockVideoMemory.memory = node->VidMem.logical; #else gcmkERR_BREAK( gckKERNEL_MapVideoMemoryEx(Kernel, gcvCORE_VG, FromUser, Interface->u.LockVideoMemory.address, &Interface->u.LockVideoMemory.memory)); #endif } else { Interface->u.LockVideoMemory.memory = node->Virtual.logical; /* Success. */ status = gcvSTATUS_OK; } #if gcdSECURE_USER /* Return logical address as physical address. */ Interface->u.LockVideoMemory.address = gcmPTR2INT(Interface->u.LockVideoMemory.memory); #endif gcmkERR_BREAK( gckKERNEL_AddProcessDB(Kernel, processID, gcvDB_VIDEO_MEMORY_LOCKED, Interface->u.LockVideoMemory.node, gcvNULL, 0)); break; case gcvHAL_UNLOCK_VIDEO_MEMORY: /* Unlock video memory. */ node = Interface->u.UnlockVideoMemory.node; #if gcdSECURE_USER /* Save node information before it disappears. */ if (node->VidMem.memory->object.type == gcvOBJ_VIDMEM) { logical = gcvNULL; bytes = 0; } else { logical = node->Virtual.logical; bytes = node->Virtual.bytes; } #endif /* Unlock video memory. */ gcmkERR_BREAK( gckVIDMEM_Unlock(Kernel, node, Interface->u.UnlockVideoMemory.type, &Interface->u.UnlockVideoMemory.asynchroneous, gcvFALSE)); #if gcdSECURE_USER /* Flush the translation cache for virtual surfaces. */ if (logical != gcvNULL) { gcmkVERIFY_OK(gckKERNEL_FlushTranslationCache(Kernel, cache, logical, bytes)); } #endif if (Interface->u.UnlockVideoMemory.asynchroneous == gcvFALSE) { /* There isn't a event to unlock this node, remove record now */ gcmkERR_BREAK( gckKERNEL_RemoveProcessDB(Kernel, processID, gcvDB_VIDEO_MEMORY_LOCKED, Interface->u.UnlockVideoMemory.node)); } break; case gcvHAL_USER_SIGNAL: #if !USE_NEW_LINUX_SIGNAL /* Dispatch depends on the user signal subcommands. */ switch(Interface->u.UserSignal.command) { case gcvUSER_SIGNAL_CREATE: /* Create a signal used in the user space. */ gcmkERR_BREAK( gckOS_CreateUserSignal(Kernel->os, Interface->u.UserSignal.manualReset, &Interface->u.UserSignal.id)); gcmkVERIFY_OK( gckKERNEL_AddProcessDB(Kernel, processID, gcvDB_SIGNAL, gcmINT2PTR(Interface->u.UserSignal.id), gcvNULL, 0)); break; case gcvUSER_SIGNAL_DESTROY: /* Destroy the signal. */ gcmkERR_BREAK( gckOS_DestroyUserSignal(Kernel->os, Interface->u.UserSignal.id)); gcmkVERIFY_OK(gckKERNEL_RemoveProcessDB( Kernel, processID, gcvDB_SIGNAL, gcmINT2PTR(Interface->u.UserSignal.id))); break; case gcvUSER_SIGNAL_SIGNAL: /* Signal the signal. */ gcmkERR_BREAK( gckOS_SignalUserSignal(Kernel->os, Interface->u.UserSignal.id, Interface->u.UserSignal.state)); break; case gcvUSER_SIGNAL_WAIT: /* Wait on the signal. */ status = gckOS_WaitUserSignal(Kernel->os, Interface->u.UserSignal.id, Interface->u.UserSignal.wait); break; default: /* Invalid user signal command. */ gcmkERR_BREAK(gcvSTATUS_INVALID_ARGUMENT); } #endif break; case gcvHAL_COMMIT: /* Commit a command and context buffer. */ gcmkERR_BREAK(gckVGCOMMAND_Commit( Kernel->vg->command, kernelInterface->u.VGCommit.context, kernelInterface->u.VGCommit.queue, kernelInterface->u.VGCommit.entryCount, kernelInterface->u.VGCommit.taskTable )); break; case gcvHAL_VERSION: kernelInterface->u.Version.major = gcvVERSION_MAJOR; kernelInterface->u.Version.minor = gcvVERSION_MINOR; kernelInterface->u.Version.patch = gcvVERSION_PATCH; kernelInterface->u.Version.build = gcvVERSION_BUILD; status = gcvSTATUS_OK; break; case gcvHAL_GET_BASE_ADDRESS: /* Get base address. */ gcmkERR_BREAK( gckOS_GetBaseAddress(Kernel->os, &kernelInterface->u.GetBaseAddress.baseAddress)); break; default: /* Invalid command. */ status = gcvSTATUS_INVALID_ARGUMENT; } OnError: /* Save status. */ kernelInterface->status = status; gcmkFOOTER(); /* Return the status. */ return status; }
return gcvSTATUS_OK; } gceSTATUS gcfDumpBuffer( IN gcoOS Os, IN gctSTRING Tag, IN gctUINT32 Physical, IN gctPOINTER Logical, IN gctUINT32 Offset, IN gctSIZE_T Bytes ) { gctUINT32_PTR ptr = (gctUINT32_PTR) Logical + (Offset >> 2); gctSIZE_T bytes = gcmALIGN(Bytes + (Offset & 3), 4); if (!setDumpFlag) return gcvSTATUS_OK; #if gcdDUMP_COMMAND static gctPOINTER dumpLock; if (dumpLock == gcvNULL) { /* TODO: Delete this mutex somehow. */ gcmVERIFY_OK(gcoOS_CreateMutex(Os, &dumpLock)); } /* Acquire mutex to avoid multiple threads dumping data at the same time. */
gceSTATUS glfCreateNamedObject( IN glsCONTEXT_PTR Context, IN glsNAMEDOBJECTLIST_PTR List, IN gctUINT32 Name, IN glfNAMEDOBJECTDESTRUCTOR ObjectDestructor, OUT glsNAMEDOBJECT_PTR * ObjectWrapper ) { gceSTATUS status = gcvSTATUS_OK; gcmHEADER_ARG("Context=0x%x List=0x%x Name=%u ObjectDestructor=0x%x ObjectWrapper=0x%x", Context, List, Name, ObjectDestructor, ObjectWrapper); do { glsNAMEDOBJECT_PTR wrapper; glsNAMEDOBJECT_PTR* entry; gctUINT32 index; /* Are there available wrappers in the free list? */ wrapper = List->freeList; if ((Name == 0) && (wrapper != gcvNULL)) { /* Remove the first wrapper from the list. */ List->freeList = wrapper->next; } /* Create a new wrapper. */ else { /* Create a new wrapper. */ wrapper = gcvNULL; /* Generate a name if not specified. */ if (Name == 0) { if (List->nextName == 0) { /* No more names. */ status = gcvSTATUS_OUT_OF_RESOURCES; break; } Name = List->nextName++; } else if (Name > List->nextName) { /* Name for the object has been explicitly specified by the application and it is greater then the next automatic would-be name. Update the next name with the value from the application; this creates a hole in the name range. */ List->nextName = Name + 1; } else if (List->freeList != gcvNULL) { /* Name for the object has been explicitly specified by the application and it is less then or equal to the next automatic would-be name. This means the name most likely lives inside the free list. */ if (List->freeList->name == Name) { wrapper = List->freeList; List->freeList = wrapper->next; } else { glsNAMEDOBJECT_PTR previous = List->freeList; for (wrapper = List->freeList; wrapper != gcvNULL; wrapper = wrapper->next) { if (wrapper->name == Name) { previous->next = wrapper->next; break; } previous = wrapper; } } } if (wrapper == gcvNULL) { /* Allocate wrapper and the user object. */ gcmERR_BREAK(gcoOS_Allocate( gcvNULL, gcmALIGN(sizeof(glsNAMEDOBJECT), 4) + List->objectSize, (gctPOINTER) &wrapper )); /* Set the objects's name. */ wrapper->name = Name; /* Set the object pointer. */ wrapper->object = ((gctUINT8_PTR) wrapper) + gcmALIGN(sizeof(glsNAMEDOBJECT), 4); wrapper->referenceCount = 0; wrapper->listBelonged = List; } } /* Set the destructor. */ wrapper->deleteObject = ObjectDestructor; /* Determine the hash table index. */ index = wrapper->name % glvNAMEDOBJECT_HASHTABLE_SIZE; /* Shortcut to the table entry. */ entry = &List->hashTable[index]; /* Insert into the chain. */ wrapper->next = *entry; *entry = wrapper; glfReferenceNamedObject(wrapper); /* Set the result. */ *ObjectWrapper = wrapper; } while (gcvFALSE); gcmFOOTER(); return status; }
/******************************************************************************* ** ** gckCOMMAND_Reserve ** ** Reserve space in the command queue. Also acquire the command queue mutex. ** ** INPUT: ** ** gckCOMMAND Command ** Pointer to an gckCOMMAND object. ** ** gctSIZE_T RequestedBytes ** Number of bytes previously reserved. ** ** OUTPUT: ** ** gctPOINTER * Buffer ** Pointer to a variable that will receive the address of the reserved ** space. ** ** gctSIZE_T * BufferSize ** Pointer to a variable that will receive the number of bytes ** available in the command queue. */ gceSTATUS gckCOMMAND_Reserve( IN gckCOMMAND Command, IN gctSIZE_T RequestedBytes, IN gctBOOL Locking, OUT gctPOINTER * Buffer, OUT gctSIZE_T * BufferSize ) { gceSTATUS status; gctSIZE_T requiredBytes, bytes; gctBOOL acquired = gcvFALSE; gcmkHEADER_ARG("Command=0x%x RequestedBytes=%lu Locking=%d", Command, RequestedBytes, Locking); /* Verify the arguments. */ gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND); if (!Locking) { /* Grab the conmmand queue mutex. */ gcmkONERROR( gckOS_AcquireMutex(Command->os, Command->mutexQueue, gcvINFINITE)); acquired = gcvTRUE; } /* Compute number of bytes required for WAIT/LINK. */ gcmkONERROR( gckHARDWARE_WaitLink(Command->kernel->hardware, gcvNULL, Command->offset + gcmALIGN(RequestedBytes, Command->alignment), &requiredBytes, gcvNULL, gcvNULL)); /* Compute total number of bytes required. */ requiredBytes += gcmALIGN(RequestedBytes, Command->alignment); /* Compute number of bytes available in command queue. */ bytes = Command->pageSize - Command->offset; if (bytes < requiredBytes) { /* Create a new command queue. */ gcmkONERROR(_NewQueue(Command, gcvTRUE)); /* Recompute number of bytes available in command queue. */ bytes = Command->pageSize - Command->offset; if (bytes < requiredBytes) { /* Rare case, not enough room in command queue. */ gcmkONERROR(gcvSTATUS_BUFFER_TOO_SMALL); } } /* Return pointer to empty slot command queue. */ *Buffer = (gctUINT8 *) Command->logical + Command->offset; /* Return number of bytes left in command queue. */ *BufferSize = bytes; /* Success. */ gcmkFOOTER_ARG("*Buffer=0x%x *BufferSize=%lu", *Buffer, *BufferSize); return gcvSTATUS_OK; OnError: if (acquired) { /* Release command queue mutex on error. */ gcmkVERIFY_OK( gckOS_ReleaseMutex(Command->os, Command->mutexQueue)); } /* Return status. */ gcmkFOOTER(); return status; }