ENGR00156850 gpu-viv: add gpu-viv driver source
[wandboard.git] / drivers / mxc / gpu-viv / hal / kernel / gc_hal_kernel_command.c
blobd49742f63f12be556556882c709f01406257542d
1 /****************************************************************************
3 * Copyright (C) 2005 - 2011 by Vivante Corp.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the license, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 *****************************************************************************/
24 #include "gc_hal_kernel_precomp.h"
25 #include "gc_hal_kernel_context.h"
27 #ifdef __QNXNTO__
28 #include <sys/slog.h>
29 #endif
31 #define _GC_OBJ_ZONE gcvZONE_COMMAND
33 /* When enabled, extra messages needed by the dump parser are left out. */
34 #define gcdSIMPLE_COMMAND_DUMP 1
36 /******************************************************************************\
37 ********************************* Support Code *********************************
38 \******************************************************************************/
40 /*******************************************************************************
42 ** _NewQueue
44 ** Allocate a new command queue.
46 ** INPUT:
48 ** gckCOMMAND Command
49 ** Pointer to an gckCOMMAND object.
51 ** OUTPUT:
53 ** gckCOMMAND Command
54 ** gckCOMMAND object has been updated with a new command queue.
56 static gceSTATUS
57 _NewQueue(
58 IN OUT gckCOMMAND Command
61 gceSTATUS status;
62 gctINT currentIndex, newIndex;
64 gcmkHEADER_ARG("Command=0x%x", Command);
66 /* Switch to the next command buffer. */
67 currentIndex = Command->index;
68 newIndex = (currentIndex + 1) % gcdCOMMAND_QUEUES;
70 /* Wait for availability. */
71 #if gcdDUMP_COMMAND && !gcdSIMPLE_COMMAND_DUMP
72 gcmkPRINT("@[kernel.waitsignal]");
73 #endif
75 gcmkONERROR(gckOS_WaitSignal(
76 Command->os,
77 Command->queues[newIndex].signal,
78 gcvINFINITE
79 ));
81 #if gcmIS_DEBUG(gcdDEBUG_TRACE)
82 if (newIndex < currentIndex)
84 Command->wrapCount += 1;
86 gcmkTRACE_ZONE_N(
87 gcvLEVEL_INFO, gcvZONE_COMMAND,
88 2 * 4,
89 "%s(%d): queue array wrapped around.\n",
90 __FUNCTION__, __LINE__
94 gcmkTRACE_ZONE_N(
95 gcvLEVEL_INFO, gcvZONE_COMMAND,
96 3 * 4,
97 "%s(%d): total queue wrap arounds %d.\n",
98 __FUNCTION__, __LINE__, Command->wrapCount
101 gcmkTRACE_ZONE_N(
102 gcvLEVEL_INFO, gcvZONE_COMMAND,
103 3 * 4,
104 "%s(%d): switched to queue %d.\n",
105 __FUNCTION__, __LINE__, newIndex
107 #endif
109 /* Update gckCOMMAND object with new command queue. */
110 Command->index = newIndex;
111 Command->newQueue = gcvTRUE;
112 Command->logical = Command->queues[newIndex].logical;
113 Command->offset = 0;
115 gcmkONERROR(
116 gckOS_GetPhysicalAddress(
117 Command->os,
118 Command->logical,
119 (gctUINT32 *) &Command->physical
122 if (currentIndex != -1)
124 /* Mark the command queue as available. */
125 gcmkONERROR(gckEVENT_Signal(
126 Command->kernel->eventObj,
127 Command->queues[currentIndex].signal,
128 gcvKERNEL_COMMAND
132 /* Success. */
133 gcmkFOOTER_ARG("Command->index=%d", Command->index);
134 return gcvSTATUS_OK;
136 OnError:
137 /* Return the status. */
138 gcmkFOOTER();
139 return status;
142 static gceSTATUS
143 _IncrementCommitAtom(
144 IN gckCOMMAND Command,
145 IN gctBOOL Increment
148 gceSTATUS status;
149 gckHARDWARE hardware;
150 gctINT32 atomValue;
151 gctBOOL powerAcquired = gcvFALSE;
153 gcmkHEADER_ARG("Command=0x%x", Command);
155 /* Extract the gckHARDWARE and gckEVENT objects. */
156 hardware = Command->kernel->hardware;
157 gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE);
159 /* Grab the power mutex. */
160 gcmkONERROR(gckOS_AcquireMutex(
161 Command->os, hardware->powerMutex, gcvINFINITE
163 powerAcquired = gcvTRUE;
165 /* Increment the commit atom. */
166 if (Increment)
168 gcmkONERROR(gckOS_AtomIncrement(
169 Command->os, Command->atomCommit, &atomValue
172 else
174 gcmkONERROR(gckOS_AtomDecrement(
175 Command->os, Command->atomCommit, &atomValue
179 /* Release the power mutex. */
180 gcmkONERROR(gckOS_ReleaseMutex(
181 Command->os, hardware->powerMutex
183 powerAcquired = gcvFALSE;
185 /* Success. */
186 gcmkFOOTER();
187 return gcvSTATUS_OK;
189 OnError:
190 if (powerAcquired)
192 /* Release the power mutex. */
193 gcmkVERIFY_OK(gckOS_ReleaseMutex(
194 Command->os, hardware->powerMutex
198 /* Return the status. */
199 gcmkFOOTER();
200 return status;
203 #if gcdSECURE_USER
204 static gceSTATUS
205 _ProcessHints(
206 IN gckCOMMAND Command,
207 IN gctUINT32 ProcessID,
208 IN gcoCMDBUF CommandBuffer
211 gceSTATUS status = gcvSTATUS_OK;
212 gckKERNEL kernel;
213 gctBOOL needCopy = gcvFALSE;
214 gcskSECURE_CACHE_PTR cache;
215 gctUINT8_PTR commandBufferLogical;
216 gctUINT8_PTR hintedData;
217 gctUINT32_PTR hintArray;
218 gctUINT i, hintCount;
220 gcmkHEADER_ARG(
221 "Command=0x%08X ProcessID=%d CommandBuffer=0x%08X",
222 Command, ProcessID, CommandBuffer
225 /* Verify the arguments. */
226 gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND);
228 /* Reset state array pointer. */
229 hintArray = gcvNULL;
231 /* Get the kernel object. */
232 kernel = Command->kernel;
234 /* Get the cache form the database. */
235 gcmkONERROR(gckKERNEL_GetProcessDBCache(kernel, ProcessID, &cache));
237 /* Determine the start of the command buffer. */
238 commandBufferLogical
239 = (gctUINT8_PTR) CommandBuffer->logical
240 + CommandBuffer->startOffset;
242 /* Determine the number of records in the state array. */
243 hintCount = CommandBuffer->hintArrayTail - CommandBuffer->hintArray;
245 /* Check wehther we need to copy the structures or not. */
246 gcmkONERROR(gckOS_QueryNeedCopy(Command->os, ProcessID, &needCopy));
248 /* Get access to the state array. */
249 if (needCopy)
251 gctUINT copySize;
253 if (Command->hintArrayAllocated &&
254 (Command->hintArraySize < CommandBuffer->hintArraySize))
256 gcmkONERROR(gcmkOS_SAFE_FREE(Command->os, Command->hintArray));
257 Command->hintArraySize = gcvFALSE;
260 if (!Command->hintArrayAllocated)
262 gctPOINTER pointer = gcvNULL;
264 gcmkONERROR(gckOS_Allocate(
265 Command->os,
266 CommandBuffer->hintArraySize,
267 &pointer
270 Command->hintArray = pointer;
271 Command->hintArrayAllocated = gcvTRUE;
272 Command->hintArraySize = CommandBuffer->hintArraySize;
275 hintArray = Command->hintArray;
276 copySize = hintCount * gcmSIZEOF(gctUINT32);
278 gcmkONERROR(gckOS_CopyFromUserData(
279 Command->os,
280 hintArray,
281 CommandBuffer->hintArray,
282 copySize
285 else
287 gctPOINTER pointer = gcvNULL;
289 gcmkONERROR(gckOS_MapUserPointer(
290 Command->os,
291 CommandBuffer->hintArray,
292 CommandBuffer->hintArraySize,
293 &pointer
296 hintArray = pointer;
299 /* Scan through the buffer. */
300 for (i = 0; i < hintCount; i += 1)
302 /* Determine the location of the hinted data. */
303 hintedData = commandBufferLogical + hintArray[i];
305 /* Map handle into physical address. */
306 gcmkONERROR(gckKERNEL_MapLogicalToPhysical(
307 kernel, cache, (gctPOINTER) hintedData
311 OnError:
312 /* Get access to the state array. */
313 if (!needCopy && (hintArray != gcvNULL))
315 gcmkVERIFY_OK(gckOS_UnmapUserPointer(
316 Command->os,
317 CommandBuffer->hintArray,
318 CommandBuffer->hintArraySize,
319 hintArray
323 /* Return the status. */
324 gcmkFOOTER();
325 return status;
327 #endif
330 /******************************************************************************\
331 ****************************** gckCOMMAND API Code ******************************
332 \******************************************************************************/
334 /*******************************************************************************
336 ** gckCOMMAND_Construct
338 ** Construct a new gckCOMMAND object.
340 ** INPUT:
342 ** gckKERNEL Kernel
343 ** Pointer to an gckKERNEL object.
345 ** OUTPUT:
347 ** gckCOMMAND * Command
348 ** Pointer to a variable that will hold the pointer to the gckCOMMAND
349 ** object.
351 gceSTATUS
352 gckCOMMAND_Construct(
353 IN gckKERNEL Kernel,
354 OUT gckCOMMAND * Command
357 gckOS os;
358 gckCOMMAND command = gcvNULL;
359 gceSTATUS status;
360 gctINT i;
361 gctPOINTER pointer = gcvNULL;
363 gcmkHEADER_ARG("Kernel=0x%x", Kernel);
365 /* Verify the arguments. */
366 gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
367 gcmkVERIFY_ARGUMENT(Command != gcvNULL);
369 /* Extract the gckOS object. */
370 os = Kernel->os;
372 /* Allocate the gckCOMMAND structure. */
373 gcmkONERROR(gckOS_Allocate(os, gcmSIZEOF(struct _gckCOMMAND), &pointer));
374 command = pointer;
376 /* Reset the entire object. */
377 gcmkONERROR(gckOS_ZeroMemory(command, gcmSIZEOF(struct _gckCOMMAND)));
379 /* Initialize the gckCOMMAND object.*/
380 command->object.type = gcvOBJ_COMMAND;
381 command->kernel = Kernel;
382 command->os = os;
384 /* Get the command buffer requirements. */
385 gcmkONERROR(gckHARDWARE_QueryCommandBuffer(
386 Kernel->hardware,
387 &command->alignment,
388 &command->reservedHead,
389 &command->reservedTail
392 /* Create the command queue mutex. */
393 gcmkONERROR(gckOS_CreateMutex(os, &command->mutexQueue));
395 /* Create the context switching mutex. */
396 gcmkONERROR(gckOS_CreateMutex(os, &command->mutexContext));
398 /* Create the power management semaphore. */
399 gcmkONERROR(gckOS_CreateSemaphore(os, &command->powerSemaphore));
401 /* Create the commit atom. */
402 gcmkONERROR(gckOS_AtomConstruct(os, &command->atomCommit));
404 /* Get the page size from teh OS. */
405 gcmkONERROR(gckOS_GetPageSize(os, &command->pageSize));
407 /* Get process ID. */
408 gcmkONERROR(gckOS_GetProcessID(&command->kernelProcessID));
410 /* Set hardware to pipe 0. */
411 command->pipeSelect = gcvPIPE_INVALID;
413 /* Pre-allocate the command queues. */
414 for (i = 0; i < gcdCOMMAND_QUEUES; ++i)
416 gcmkONERROR(gckOS_AllocateNonPagedMemory(
418 gcvFALSE,
419 &command->pageSize,
420 &command->queues[i].physical,
421 &command->queues[i].logical
424 gcmkONERROR(gckOS_CreateSignal(
425 os, gcvFALSE, &command->queues[i].signal
428 gcmkONERROR(gckOS_Signal(
429 os, command->queues[i].signal, gcvTRUE
433 /* No command queue in use yet. */
434 command->index = -1;
435 command->logical = gcvNULL;
436 command->newQueue = gcvFALSE;
438 /* Command is not yet running. */
439 command->running = gcvFALSE;
441 /* Command queue is idle. */
442 command->idle = gcvTRUE;
444 /* Commit stamp is zero. */
445 command->commitStamp = 0;
447 /* END event signal not created. */
448 command->endEventSignal = gcvNULL;
450 /* Return pointer to the gckCOMMAND object. */
451 *Command = command;
453 /* Success. */
454 gcmkFOOTER_ARG("*Command=0x%x", *Command);
455 return gcvSTATUS_OK;
457 OnError:
458 /* Roll back. */
459 if (command != gcvNULL)
461 if (command->atomCommit != gcvNULL)
463 gcmkVERIFY_OK(gckOS_AtomDestroy(os, command->atomCommit));
466 if (command->powerSemaphore != gcvNULL)
468 gcmkVERIFY_OK(gckOS_DestroySemaphore(os, command->powerSemaphore));
471 if (command->mutexContext != gcvNULL)
473 gcmkVERIFY_OK(gckOS_DeleteMutex(os, command->mutexContext));
476 if (command->mutexQueue != gcvNULL)
478 gcmkVERIFY_OK(gckOS_DeleteMutex(os, command->mutexQueue));
481 for (i = 0; i < gcdCOMMAND_QUEUES; ++i)
483 if (command->queues[i].signal != gcvNULL)
485 gcmkVERIFY_OK(gckOS_DestroySignal(
486 os, command->queues[i].signal
490 if (command->queues[i].logical != gcvNULL)
492 gcmkVERIFY_OK(gckOS_FreeNonPagedMemory(
494 command->pageSize,
495 command->queues[i].physical,
496 command->queues[i].logical
501 gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os, command));
504 /* Return the status. */
505 gcmkFOOTER();
506 return status;
509 /*******************************************************************************
511 ** gckCOMMAND_Destroy
513 ** Destroy an gckCOMMAND object.
515 ** INPUT:
517 ** gckCOMMAND Command
518 ** Pointer to an gckCOMMAND object to destroy.
520 ** OUTPUT:
522 ** Nothing.
524 gceSTATUS
525 gckCOMMAND_Destroy(
526 IN gckCOMMAND Command
529 gctINT i;
531 gcmkHEADER_ARG("Command=0x%x", Command);
533 /* Verify the arguments. */
534 gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND);
536 /* Stop the command queue. */
537 gcmkVERIFY_OK(gckCOMMAND_Stop(Command));
539 for (i = 0; i < gcdCOMMAND_QUEUES; ++i)
541 gcmkASSERT(Command->queues[i].signal != gcvNULL);
542 gcmkVERIFY_OK(gckOS_DestroySignal(
543 Command->os, Command->queues[i].signal
546 gcmkASSERT(Command->queues[i].logical != gcvNULL);
547 gcmkVERIFY_OK(gckOS_FreeNonPagedMemory(
548 Command->os,
549 Command->pageSize,
550 Command->queues[i].physical,
551 Command->queues[i].logical
555 /* END event signal. */
556 if (Command->endEventSignal != gcvNULL)
558 gcmkVERIFY_OK(gckOS_DestroySignal(
559 Command->os, Command->endEventSignal
563 /* Delete the context switching mutex. */
564 gcmkVERIFY_OK(gckOS_DeleteMutex(Command->os, Command->mutexContext));
566 /* Delete the command queue mutex. */
567 gcmkVERIFY_OK(gckOS_DeleteMutex(Command->os, Command->mutexQueue));
569 /* Destroy the power management semaphore. */
570 gcmkVERIFY_OK(gckOS_DestroySemaphore(Command->os, Command->powerSemaphore));
572 /* Destroy the commit atom. */
573 gcmkVERIFY_OK(gckOS_AtomDestroy(Command->os, Command->atomCommit));
575 #if gcdSECURE_USER
576 /* Free state array. */
577 if (Command->hintArrayAllocated)
579 gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Command->os, Command->hintArray));
580 Command->hintArrayAllocated = gcvFALSE;
582 #endif
584 /* Mark object as unknown. */
585 Command->object.type = gcvOBJ_UNKNOWN;
587 /* Free the gckCOMMAND object. */
588 gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Command->os, Command));
590 /* Success. */
591 gcmkFOOTER_NO();
592 return gcvSTATUS_OK;
595 /*******************************************************************************
597 ** gckCOMMAND_EnterCommit
599 ** Acquire command queue synchronization objects.
601 ** INPUT:
603 ** gckCOMMAND Command
604 ** Pointer to an gckCOMMAND object to destroy.
606 ** gctBOOL FromPower
607 ** Determines whether the call originates from inside the power
608 ** management or not.
610 ** OUTPUT:
612 ** Nothing.
614 gceSTATUS
615 gckCOMMAND_EnterCommit(
616 IN gckCOMMAND Command,
617 IN gctBOOL FromPower
620 gceSTATUS status;
621 gckHARDWARE hardware;
622 gctBOOL atomIncremented = gcvFALSE;
623 gctBOOL semaAcquired = gcvFALSE;
625 gcmkHEADER_ARG("Command=0x%x", Command);
627 /* Extract the gckHARDWARE and gckEVENT objects. */
628 hardware = Command->kernel->hardware;
629 gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE);
631 if (!FromPower)
633 /* Increment COMMIT atom to let power management know that a commit is
634 ** in progress. */
635 gcmkONERROR(_IncrementCommitAtom(Command, gcvTRUE));
636 atomIncremented = gcvTRUE;
638 /* Notify the system the GPU has a commit. */
639 gcmkONERROR(gckOS_Broadcast(Command->os,
640 hardware,
641 gcvBROADCAST_GPU_COMMIT));
643 /* Acquire the power management semaphore. */
644 gcmkONERROR(gckOS_AcquireSemaphore(Command->os,
645 Command->powerSemaphore));
646 semaAcquired = gcvTRUE;
649 /* Grab the conmmand queue mutex. */
650 gcmkONERROR(gckOS_AcquireMutex(Command->os,
651 Command->mutexQueue,
652 gcvINFINITE));
654 /* Success. */
655 gcmkFOOTER();
656 return gcvSTATUS_OK;
658 OnError:
659 if (semaAcquired)
661 /* Release the power management semaphore. */
662 gcmkVERIFY_OK(gckOS_ReleaseSemaphore(
663 Command->os, Command->powerSemaphore
667 if (atomIncremented)
669 /* Decrement the commit atom. */
670 gcmkVERIFY_OK(_IncrementCommitAtom(
671 Command, gcvFALSE
675 /* Return the status. */
676 gcmkFOOTER();
677 return status;
680 /*******************************************************************************
682 ** gckCOMMAND_ExitCommit
684 ** Release command queue synchronization objects.
686 ** INPUT:
688 ** gckCOMMAND Command
689 ** Pointer to an gckCOMMAND object to destroy.
691 ** gctBOOL FromPower
692 ** Determines whether the call originates from inside the power
693 ** management or not.
695 ** OUTPUT:
697 ** Nothing.
699 gceSTATUS
700 gckCOMMAND_ExitCommit(
701 IN gckCOMMAND Command,
702 IN gctBOOL FromPower
705 gceSTATUS status;
707 gcmkHEADER_ARG("Command=0x%x", Command);
709 /* Release the power mutex. */
710 gcmkONERROR(gckOS_ReleaseMutex(Command->os, Command->mutexQueue));
712 if (!FromPower)
714 /* Release the power management semaphore. */
715 gcmkONERROR(gckOS_ReleaseSemaphore(Command->os,
716 Command->powerSemaphore));
718 /* Decrement the commit atom. */
719 gcmkONERROR(_IncrementCommitAtom(Command, gcvFALSE));
722 /* Success. */
723 gcmkFOOTER();
724 return gcvSTATUS_OK;
726 OnError:
727 /* Return the status. */
728 gcmkFOOTER();
729 return status;
732 /*******************************************************************************
734 ** gckCOMMAND_Start
736 ** Start up the command queue.
738 ** INPUT:
740 ** gckCOMMAND Command
741 ** Pointer to an gckCOMMAND object to start.
743 ** OUTPUT:
745 ** Nothing.
747 gceSTATUS
748 gckCOMMAND_Start(
749 IN gckCOMMAND Command
752 gceSTATUS status;
753 gckHARDWARE hardware;
754 gctUINT32 waitOffset;
755 gctSIZE_T waitLinkBytes;
757 gcmkHEADER_ARG("Command=0x%x", Command);
759 /* Verify the arguments. */
760 gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND);
762 if (Command->running)
764 /* Command queue already running. */
765 gcmkFOOTER_NO();
766 return gcvSTATUS_OK;
769 /* Extract the gckHARDWARE object. */
770 hardware = Command->kernel->hardware;
771 gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE);
773 if (Command->logical == gcvNULL)
775 /* Start at beginning of a new queue. */
776 gcmkONERROR(_NewQueue(Command));
779 /* Start at beginning of page. */
780 Command->offset = 0;
782 /* Set abvailable number of bytes for WAIT/LINK command sequence. */
783 waitLinkBytes = Command->pageSize;
785 /* Append WAIT/LINK. */
786 gcmkONERROR(gckHARDWARE_WaitLink(
787 hardware,
788 Command->logical,
790 &waitLinkBytes,
791 &waitOffset,
792 &Command->waitSize
795 Command->waitLogical = (gctUINT8_PTR) Command->logical + waitOffset;
796 Command->waitPhysical = (gctUINT8_PTR) Command->physical + waitOffset;
798 #if gcdNONPAGED_MEMORY_CACHEABLE
799 /* Flush the cache for the wait/link. */
800 gcmkONERROR(gckOS_CacheClean(
801 Command->os,
802 Command->kernelProcessID,
803 gcvNULL,
804 Command->physical,
805 Command->logical,
806 waitLinkBytes
808 #endif
810 /* Adjust offset. */
811 Command->offset = waitLinkBytes;
812 Command->newQueue = gcvFALSE;
814 /* Enable command processor. */
815 #ifdef __QNXNTO__
816 gcmkONERROR(gckHARDWARE_Execute(
817 hardware,
818 Command->logical,
819 Command->physical,
820 gcvTRUE,
821 waitLinkBytes
823 #else
824 gcmkONERROR(gckHARDWARE_Execute(
825 hardware,
826 Command->logical,
827 waitLinkBytes
829 #endif
831 /* Command queue is running. */
832 Command->running = gcvTRUE;
834 /* Success. */
835 gcmkFOOTER_NO();
836 return gcvSTATUS_OK;
838 OnError:
839 /* Return the status. */
840 gcmkFOOTER();
841 return status;
844 /*******************************************************************************
846 ** gckCOMMAND_Stop
848 ** Stop the command queue.
850 ** INPUT:
852 ** gckCOMMAND Command
853 ** Pointer to an gckCOMMAND object to stop.
855 ** OUTPUT:
857 ** Nothing.
859 gceSTATUS
860 gckCOMMAND_Stop(
861 IN gckCOMMAND Command
864 gckHARDWARE hardware;
865 gceSTATUS status;
866 gctUINT32 idle;
868 gcmkHEADER_ARG("Command=0x%x", Command);
870 /* Verify the arguments. */
871 gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND);
873 if (!Command->running)
875 /* Command queue is not running. */
876 gcmkFOOTER_NO();
877 return gcvSTATUS_OK;
880 /* Extract the gckHARDWARE object. */
881 hardware = Command->kernel->hardware;
882 gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE);
884 if (gckHARDWARE_IsFeatureAvailable(hardware,
885 gcvFEATURE_END_EVENT) == gcvSTATUS_TRUE)
887 /* Allocate the signal. */
888 if (Command->endEventSignal == gcvNULL)
890 gcmkONERROR(gckOS_CreateSignal(Command->os,
891 gcvTRUE,
892 &Command->endEventSignal));
895 /* Append the END EVENT command to trigger the signal. */
896 gcmkONERROR(gckEVENT_Stop(Command->kernel->eventObj,
897 Command->kernelProcessID,
898 Command->waitPhysical,
899 Command->waitLogical,
900 Command->endEventSignal,
901 &Command->waitSize));
903 else
905 /* Replace last WAIT with END. */
906 gcmkONERROR(gckHARDWARE_End(
907 hardware, Command->waitLogical, &Command->waitSize
910 /* Update queue tail pointer. */
911 gcmkONERROR(gckHARDWARE_UpdateQueueTail(Command->kernel->hardware,
912 Command->logical,
913 Command->offset));
915 #if gcdNONPAGED_MEMORY_CACHEABLE
916 /* Flush the cache for the END. */
917 gcmkONERROR(gckOS_CacheClean(
918 Command->os,
919 Command->kernelProcessID,
920 gcvNULL,
921 Command->waitPhysical,
922 Command->waitLogical,
923 Command->waitSize
925 #endif
927 /* Wait for idle. */
928 gcmkONERROR(gckHARDWARE_GetIdle(hardware, gcvTRUE, &idle));
931 /* Command queue is no longer running. */
932 Command->running = gcvFALSE;
934 /* Success. */
935 gcmkFOOTER_NO();
936 return gcvSTATUS_OK;
938 OnError:
939 /* Return the status. */
940 gcmkFOOTER();
941 return status;
944 /*******************************************************************************
946 ** gckCOMMAND_Commit
948 ** Commit a command buffer to the command queue.
950 ** INPUT:
952 ** gckCOMMAND Command
953 ** Pointer to a gckCOMMAND object.
955 ** gckCONTEXT Context
956 ** Pointer to a gckCONTEXT object.
958 ** gcoCMDBUF CommandBuffer
959 ** Pointer to a gcoCMDBUF object.
961 ** gcsSTATE_DELTA_PTR StateDelta
962 ** Pointer to the state delta.
964 ** gctUINT32 ProcessID
965 ** Current process ID.
967 ** OUTPUT:
969 ** Nothing.
971 gceSTATUS
972 gckCOMMAND_Commit(
973 IN gckCOMMAND Command,
974 IN gckCONTEXT Context,
975 IN gcoCMDBUF CommandBuffer,
976 IN gcsSTATE_DELTA_PTR StateDelta,
977 IN gcsQUEUE_PTR EventQueue,
978 IN gctUINT32 ProcessID
981 #if gcdNULL_DRIVER
982 /* Context switch required? */
983 if ((Context != gcvNULL) && (Command->currContext != Context))
985 /* Yes, merge in the deltas. */
986 gckCONTEXT_Update(Context, ProcessID, StateDelta);
988 /* Update the current context. */
989 Command->currContext = Context;
992 /* Do nothing with infinite hardware. */
993 return gcvSTATUS_OK;
994 #else
995 gceSTATUS status;
996 gckHARDWARE hardware;
997 gcsCONTEXT_PTR contextBuffer;
999 gctBOOL needCopy = gcvFALSE;
1001 struct _gcoCMDBUF _commandBufferObject;
1002 gcoCMDBUF commandBufferObject = gcvNULL;
1003 gctBOOL commandBufferMapped = gcvFALSE;
1004 gctPHYS_ADDR commandBufferPhysical;
1005 gctUINT8_PTR commandBufferLogical;
1006 gctUINT8_PTR commandBufferLink;
1007 gctUINT commandBufferSize;
1009 gctBOOL commitEntered = gcvFALSE;
1010 gctBOOL contextAcquired = gcvFALSE;
1012 gctSIZE_T nopBytes;
1013 gctSIZE_T pipeBytes;
1014 gctSIZE_T linkBytes;
1015 gctSIZE_T bytes;
1016 gctUINT32 offset;
1018 gctPHYS_ADDR entryPhysical;
1019 gctPOINTER entryLogical;
1020 gctSIZE_T entryBytes;
1022 gctPHYS_ADDR exitPhysical;
1023 gctPOINTER exitLogical;
1024 gctSIZE_T exitBytes;
1026 gctPHYS_ADDR waitLinkPhysical;
1027 gctPOINTER waitLinkLogical;
1028 gctSIZE_T waitLinkBytes;
1030 gctPHYS_ADDR waitPhysical;
1031 gctPOINTER waitLogical;
1032 gctUINT32 waitOffset;
1033 gctSIZE_T waitSize;
1035 gcsQUEUE _eventRecord;
1036 gcsQUEUE_PTR eventRecord = gcvNULL;
1037 gcsQUEUE_PTR nextEventRecord;
1039 #if gcdDUMP_COMMAND
1040 gctPOINTER contextDumpLogical = gcvNULL;
1041 gctSIZE_T contextDumpBytes = 0;
1043 gctPOINTER bufferDumpLogical = gcvNULL;
1044 gctSIZE_T bufferDumpBytes = 0;
1045 #endif
1047 gctPOINTER pointer = gcvNULL;
1049 gcmkHEADER_ARG(
1050 "Command=0x%x CommandBuffer=0x%x ProcessID=%d",
1051 Command, CommandBuffer, ProcessID
1054 /* Verify the arguments. */
1055 gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND);
1057 /* Acquire the command queue. */
1058 gcmkONERROR(gckCOMMAND_EnterCommit(Command, gcvFALSE));
1059 commitEntered = gcvTRUE;
1061 /* Acquire the context switching mutex. */
1062 gcmkONERROR(gckOS_AcquireMutex(
1063 Command->os, Command->mutexContext, gcvINFINITE
1065 contextAcquired = gcvTRUE;
1067 /* Extract the gckHARDWARE and gckEVENT objects. */
1068 hardware = Command->kernel->hardware;
1070 /* Check wehther we need to copy the structures or not. */
1071 gcmkONERROR(gckOS_QueryNeedCopy(Command->os, ProcessID, &needCopy));
1073 if (needCopy)
1075 commandBufferObject = &_commandBufferObject;
1077 gcmkONERROR(gckOS_CopyFromUserData(
1078 Command->os,
1079 commandBufferObject,
1080 CommandBuffer,
1081 gcmSIZEOF(struct _gcoCMDBUF)
1084 gcmkVERIFY_OBJECT(commandBufferObject, gcvOBJ_COMMANDBUFFER);
1086 else
1088 gcmkONERROR(gckOS_MapUserPointer(
1089 Command->os,
1090 CommandBuffer,
1091 gcmSIZEOF(struct _gcoCMDBUF),
1092 &pointer
1095 commandBufferObject = pointer;
1097 gcmkVERIFY_OBJECT(commandBufferObject, gcvOBJ_COMMANDBUFFER);
1098 commandBufferMapped = gcvTRUE;
1101 /* Query the size of NOP command. */
1102 gcmkONERROR(gckHARDWARE_Nop(
1103 hardware, gcvNULL, &nopBytes
1106 /* Query the size of pipe select command sequence. */
1107 gcmkONERROR(gckHARDWARE_PipeSelect(
1108 hardware, gcvNULL, gcvPIPE_3D, &pipeBytes
1111 /* Query the size of LINK command. */
1112 gcmkONERROR(gckHARDWARE_Link(
1113 hardware, gcvNULL, gcvNULL, 0, &linkBytes
1116 /* Compute the command buffer entry and the size. */
1117 commandBufferLogical
1118 = (gctUINT8_PTR) commandBufferObject->logical
1119 + commandBufferObject->startOffset;
1121 gcmkONERROR(gckOS_GetPhysicalAddress(
1122 Command->os,
1123 commandBufferLogical,
1124 (gctUINT32_PTR)&commandBufferPhysical
1127 commandBufferSize
1128 = commandBufferObject->offset
1129 + Command->reservedTail
1130 - commandBufferObject->startOffset;
1132 /* Context switch required? */
1133 if (Context == gcvNULL)
1135 /* See if we have to switch pipes for the command buffer. */
1136 if (commandBufferObject->entryPipe == Command->pipeSelect)
1138 /* Skip pipe switching sequence. */
1139 offset = pipeBytes;
1141 else
1143 /* The current hardware and the entry command buffer pipes
1144 ** are different, switch to the correct pipe. */
1145 gcmkONERROR(gckHARDWARE_PipeSelect(
1146 Command->kernel->hardware,
1147 commandBufferLogical,
1148 commandBufferObject->entryPipe,
1149 &pipeBytes
1152 /* Do not skip pipe switching sequence. */
1153 offset = 0;
1156 /* Compute the entry. */
1157 entryPhysical = (gctUINT8_PTR) commandBufferPhysical + offset;
1158 entryLogical = commandBufferLogical + offset;
1159 entryBytes = commandBufferSize - offset;
1161 else if (Command->currContext != Context)
1163 /* Temporary disable context length oprimization. */
1164 Context->dirty = gcvTRUE;
1166 /* Get the current context buffer. */
1167 contextBuffer = Context->buffer;
1169 /* Yes, merge in the deltas. */
1170 gcmkONERROR(gckCONTEXT_Update(Context, ProcessID, StateDelta));
1172 /* Determine context entry and exit points. */
1173 if (0)
1175 /* Reset 2D dirty flag. */
1176 Context->dirty2D = gcvFALSE;
1178 if (Context->dirty || commandBufferObject->using3D)
1180 /***************************************************************
1181 ** SWITCHING CONTEXT: 2D and 3D are used.
1184 /* Reset 3D dirty flag. */
1185 Context->dirty3D = gcvFALSE;
1187 /* Compute the entry. */
1188 if (Command->pipeSelect == gcvPIPE_2D)
1190 entryPhysical = (gctUINT8_PTR) contextBuffer->physical + pipeBytes;
1191 entryLogical = (gctUINT8_PTR) contextBuffer->logical + pipeBytes;
1192 entryBytes = Context->bufferSize - pipeBytes;
1194 else
1196 entryPhysical = (gctUINT8_PTR) contextBuffer->physical;
1197 entryLogical = (gctUINT8_PTR) contextBuffer->logical;
1198 entryBytes = Context->bufferSize;
1201 /* See if we have to switch pipes between the context
1202 and command buffers. */
1203 if (commandBufferObject->entryPipe == gcvPIPE_3D)
1205 /* Skip pipe switching sequence. */
1206 offset = pipeBytes;
1208 else
1210 /* The current hardware and the initial context pipes are
1211 different, switch to the correct pipe. */
1212 gcmkONERROR(gckHARDWARE_PipeSelect(
1213 Command->kernel->hardware,
1214 commandBufferLogical,
1215 commandBufferObject->entryPipe,
1216 &pipeBytes
1219 /* Do not skip pipe switching sequence. */
1220 offset = 0;
1223 /* Ensure the NOP between 2D and 3D is in place so that the
1224 execution falls through from 2D to 3D. */
1225 gcmkONERROR(gckHARDWARE_Nop(
1226 hardware,
1227 contextBuffer->link2D,
1228 &nopBytes
1231 /* Generate a LINK from the context buffer to
1232 the command buffer. */
1233 gcmkONERROR(gckHARDWARE_Link(
1234 hardware,
1235 contextBuffer->link3D,
1236 commandBufferLogical + offset,
1237 commandBufferSize - offset,
1238 &linkBytes
1241 /* Mark context as not dirty. */
1242 Context->dirty = gcvFALSE;
1244 else
1246 /***************************************************************
1247 ** SWITCHING CONTEXT: 2D only command buffer.
1250 /* Mark 3D as dirty. */
1251 Context->dirty3D = gcvTRUE;
1253 /* Compute the entry. */
1254 if (Command->pipeSelect == gcvPIPE_2D)
1256 entryPhysical = (gctUINT8_PTR) contextBuffer->physical + pipeBytes;
1257 entryLogical = (gctUINT8_PTR) contextBuffer->logical + pipeBytes;
1258 entryBytes = Context->entryOffset3D - pipeBytes;
1260 else
1262 entryPhysical = (gctUINT8_PTR) contextBuffer->physical;
1263 entryLogical = (gctUINT8_PTR) contextBuffer->logical;
1264 entryBytes = Context->entryOffset3D;
1267 /* Store the current context buffer. */
1268 Context->dirtyBuffer = contextBuffer;
1270 /* See if we have to switch pipes between the context
1271 and command buffers. */
1272 if (commandBufferObject->entryPipe == gcvPIPE_2D)
1274 /* Skip pipe switching sequence. */
1275 offset = pipeBytes;
1277 else
1279 /* The current hardware and the initial context pipes are
1280 different, switch to the correct pipe. */
1281 gcmkONERROR(gckHARDWARE_PipeSelect(
1282 Command->kernel->hardware,
1283 commandBufferLogical,
1284 commandBufferObject->entryPipe,
1285 &pipeBytes
1288 /* Do not skip pipe switching sequence. */
1289 offset = 0;
1292 /* 3D is not used, generate a LINK from the end of 2D part of
1293 the context buffer to the command buffer. */
1294 gcmkONERROR(gckHARDWARE_Link(
1295 hardware,
1296 contextBuffer->link2D,
1297 commandBufferLogical + offset,
1298 commandBufferSize - offset,
1299 &linkBytes
1304 /* Not using 2D. */
1305 else
1307 /* Mark 2D as dirty. */
1308 Context->dirty2D = gcvTRUE;
1310 /* Store the current context buffer. */
1311 Context->dirtyBuffer = contextBuffer;
1313 if (Context->dirty || commandBufferObject->using3D)
1315 /***************************************************************
1316 ** SWITCHING CONTEXT: 3D only command buffer.
1319 /* Reset 3D dirty flag. */
1320 Context->dirty3D = gcvFALSE;
1322 /* Determine context buffer entry offset. */
1323 offset = (Command->pipeSelect == gcvPIPE_3D)
1325 /* Skip pipe switching sequence. */
1326 ? Context->entryOffset3D + pipeBytes
1328 /* Do not skip pipe switching sequence. */
1329 : Context->entryOffset3D;
1331 /* Compute the entry. */
1332 entryPhysical = (gctUINT8_PTR) contextBuffer->physical + offset;
1333 entryLogical = (gctUINT8_PTR) contextBuffer->logical + offset;
1334 entryBytes = Context->bufferSize - offset;
1336 /* See if we have to switch pipes between the context
1337 and command buffers. */
1338 if (commandBufferObject->entryPipe == gcvPIPE_3D)
1340 /* Skip pipe switching sequence. */
1341 offset = pipeBytes;
1343 else
1345 /* The current hardware and the initial context pipes are
1346 different, switch to the correct pipe. */
1347 gcmkONERROR(gckHARDWARE_PipeSelect(
1348 Command->kernel->hardware,
1349 commandBufferLogical,
1350 commandBufferObject->entryPipe,
1351 &pipeBytes
1354 /* Do not skip pipe switching sequence. */
1355 offset = 0;
1358 /* Generate a LINK from the context buffer to
1359 the command buffer. */
1360 gcmkONERROR(gckHARDWARE_Link(
1361 hardware,
1362 contextBuffer->link3D,
1363 commandBufferLogical + offset,
1364 commandBufferSize - offset,
1365 &linkBytes
1368 else
1370 /***************************************************************
1371 ** SWITCHING CONTEXT: "XD" command buffer - neither 2D nor 3D.
1374 /* Mark 3D as dirty. */
1375 Context->dirty3D = gcvTRUE;
1377 /* Compute the entry. */
1378 if (Command->pipeSelect == gcvPIPE_3D)
1380 entryPhysical
1381 = (gctUINT8_PTR) contextBuffer->physical
1382 + Context->entryOffsetXDFrom3D;
1384 entryLogical
1385 = (gctUINT8_PTR) contextBuffer->logical
1386 + Context->entryOffsetXDFrom3D;
1388 entryBytes
1389 = Context->bufferSize
1390 - Context->entryOffsetXDFrom3D;
1392 else
1394 entryPhysical
1395 = (gctUINT8_PTR) contextBuffer->physical
1396 + Context->entryOffsetXDFrom2D;
1398 entryLogical
1399 = (gctUINT8_PTR) contextBuffer->logical
1400 + Context->entryOffsetXDFrom2D;
1402 entryBytes
1403 = Context->totalSize
1404 - Context->entryOffsetXDFrom2D;
1407 /* See if we have to switch pipes between the context
1408 and command buffers. */
1409 if (commandBufferObject->entryPipe == gcvPIPE_3D)
1411 /* Skip pipe switching sequence. */
1412 offset = pipeBytes;
1414 else
1416 /* The current hardware and the initial context pipes are
1417 different, switch to the correct pipe. */
1418 gcmkONERROR(gckHARDWARE_PipeSelect(
1419 Command->kernel->hardware,
1420 commandBufferLogical,
1421 commandBufferObject->entryPipe,
1422 &pipeBytes
1425 /* Do not skip pipe switching sequence. */
1426 offset = 0;
1429 /* Generate a LINK from the context buffer to
1430 the command buffer. */
1431 gcmkONERROR(gckHARDWARE_Link(
1432 hardware,
1433 contextBuffer->link3D,
1434 commandBufferLogical + offset,
1435 commandBufferSize - offset,
1436 &linkBytes
1441 #if gcdNONPAGED_MEMORY_CACHEABLE
1442 /* Flush the context buffer cache. */
1443 gcmkONERROR(gckOS_CacheClean(
1444 Command->os,
1445 Command->kernelProcessID,
1446 gcvNULL,
1447 entryPhysical,
1448 entryLogical,
1449 entryBytes
1451 #endif
1453 /* Update the current context. */
1454 Command->currContext = Context;
1456 #if gcdDUMP_COMMAND
1457 contextDumpLogical = entryLogical;
1458 contextDumpBytes = entryBytes;
1459 #endif
1462 /* Same context. */
1463 else
1465 /* Determine context entry and exit points. */
1466 if (commandBufferObject->using2D && Context->dirty2D)
1468 /* Reset 2D dirty flag. */
1469 Context->dirty2D = gcvFALSE;
1471 /* Get the "dirty" context buffer. */
1472 contextBuffer = Context->dirtyBuffer;
1474 if (commandBufferObject->using3D && Context->dirty3D)
1476 /* Reset 3D dirty flag. */
1477 Context->dirty3D = gcvFALSE;
1479 /* Compute the entry. */
1480 if (Command->pipeSelect == gcvPIPE_2D)
1482 entryPhysical = (gctUINT8_PTR) contextBuffer->physical + pipeBytes;
1483 entryLogical = (gctUINT8_PTR) contextBuffer->logical + pipeBytes;
1484 entryBytes = Context->bufferSize - pipeBytes;
1486 else
1488 entryPhysical = (gctUINT8_PTR) contextBuffer->physical;
1489 entryLogical = (gctUINT8_PTR) contextBuffer->logical;
1490 entryBytes = Context->bufferSize;
1493 /* See if we have to switch pipes between the context
1494 and command buffers. */
1495 if (commandBufferObject->entryPipe == gcvPIPE_3D)
1497 /* Skip pipe switching sequence. */
1498 offset = pipeBytes;
1500 else
1502 /* The current hardware and the initial context pipes are
1503 different, switch to the correct pipe. */
1504 gcmkONERROR(gckHARDWARE_PipeSelect(
1505 Command->kernel->hardware,
1506 commandBufferLogical,
1507 commandBufferObject->entryPipe,
1508 &pipeBytes
1511 /* Do not skip pipe switching sequence. */
1512 offset = 0;
1515 /* Ensure the NOP between 2D and 3D is in place so that the
1516 execution falls through from 2D to 3D. */
1517 gcmkONERROR(gckHARDWARE_Nop(
1518 hardware,
1519 contextBuffer->link2D,
1520 &nopBytes
1523 /* Generate a LINK from the context buffer to
1524 the command buffer. */
1525 gcmkONERROR(gckHARDWARE_Link(
1526 hardware,
1527 contextBuffer->link3D,
1528 commandBufferLogical + offset,
1529 commandBufferSize - offset,
1530 &linkBytes
1533 else
1535 /* Compute the entry. */
1536 if (Command->pipeSelect == gcvPIPE_2D)
1538 entryPhysical = (gctUINT8_PTR) contextBuffer->physical + pipeBytes;
1539 entryLogical = (gctUINT8_PTR) contextBuffer->logical + pipeBytes;
1540 entryBytes = Context->entryOffset3D - pipeBytes;
1542 else
1544 entryPhysical = (gctUINT8_PTR) contextBuffer->physical;
1545 entryLogical = (gctUINT8_PTR) contextBuffer->logical;
1546 entryBytes = Context->entryOffset3D;
1549 /* See if we have to switch pipes between the context
1550 and command buffers. */
1551 if (commandBufferObject->entryPipe == gcvPIPE_2D)
1553 /* Skip pipe switching sequence. */
1554 offset = pipeBytes;
1556 else
1558 /* The current hardware and the initial context pipes are
1559 different, switch to the correct pipe. */
1560 gcmkONERROR(gckHARDWARE_PipeSelect(
1561 Command->kernel->hardware,
1562 commandBufferLogical,
1563 commandBufferObject->entryPipe,
1564 &pipeBytes
1567 /* Do not skip pipe switching sequence. */
1568 offset = 0;
1571 /* 3D is not used, generate a LINK from the end of 2D part of
1572 the context buffer to the command buffer. */
1573 gcmkONERROR(gckHARDWARE_Link(
1574 hardware,
1575 contextBuffer->link2D,
1576 commandBufferLogical + offset,
1577 commandBufferSize - offset,
1578 &linkBytes
1582 else
1584 if (commandBufferObject->using3D && Context->dirty3D)
1586 /* Reset 3D dirty flag. */
1587 Context->dirty3D = gcvFALSE;
1589 /* Get the "dirty" context buffer. */
1590 contextBuffer = Context->dirtyBuffer;
1592 /* Determine context buffer entry offset. */
1593 offset = (Command->pipeSelect == gcvPIPE_3D)
1595 /* Skip pipe switching sequence. */
1596 ? Context->entryOffset3D + pipeBytes
1598 /* Do not skip pipe switching sequence. */
1599 : Context->entryOffset3D;
1601 /* Compute the entry. */
1602 entryPhysical = (gctUINT8_PTR) contextBuffer->physical + offset;
1603 entryLogical = (gctUINT8_PTR) contextBuffer->logical + offset;
1604 entryBytes = Context->bufferSize - offset;
1606 /* See if we have to switch pipes between the context
1607 and command buffers. */
1608 if (commandBufferObject->entryPipe == gcvPIPE_3D)
1610 /* Skip pipe switching sequence. */
1611 offset = pipeBytes;
1613 else
1615 /* The current hardware and the initial context pipes are
1616 different, switch to the correct pipe. */
1617 gcmkONERROR(gckHARDWARE_PipeSelect(
1618 Command->kernel->hardware,
1619 commandBufferLogical,
1620 commandBufferObject->entryPipe,
1621 &pipeBytes
1624 /* Do not skip pipe switching sequence. */
1625 offset = 0;
1628 /* Generate a LINK from the context buffer to
1629 the command buffer. */
1630 gcmkONERROR(gckHARDWARE_Link(
1631 hardware,
1632 contextBuffer->link3D,
1633 commandBufferLogical + offset,
1634 commandBufferSize - offset,
1635 &linkBytes
1638 else
1640 /* See if we have to switch pipes for the command buffer. */
1641 if (commandBufferObject->entryPipe == Command->pipeSelect)
1643 /* Skip pipe switching sequence. */
1644 offset = pipeBytes;
1646 else
1648 /* The current hardware and the entry command buffer pipes
1649 ** are different, switch to the correct pipe. */
1650 gcmkONERROR(gckHARDWARE_PipeSelect(
1651 Command->kernel->hardware,
1652 commandBufferLogical,
1653 commandBufferObject->entryPipe,
1654 &pipeBytes
1657 /* Do not skip pipe switching sequence. */
1658 offset = 0;
1661 /* Compute the entry. */
1662 entryPhysical = (gctUINT8_PTR) commandBufferPhysical + offset;
1663 entryLogical = commandBufferLogical + offset;
1664 entryBytes = commandBufferSize - offset;
1669 #if gcdDUMP_COMMAND
1670 bufferDumpLogical = commandBufferLogical + offset;
1671 bufferDumpBytes = commandBufferSize - offset;
1672 #endif
1674 #if gcdSECURE_USER
1675 /* Process user hints. */
1676 gcmkONERROR(_ProcessHints(Command, ProcessID, commandBufferObject));
1677 #endif
1679 /* Get the current offset. */
1680 offset = Command->offset;
1682 /* Compute number of bytes left in current kernel command queue. */
1683 bytes = Command->pageSize - offset;
1685 /* Query the size of WAIT/LINK command sequence. */
1686 gcmkONERROR(gckHARDWARE_WaitLink(
1687 hardware,
1688 gcvNULL,
1689 offset,
1690 &waitLinkBytes,
1691 gcvNULL,
1692 gcvNULL
1695 /* Is there enough space in the current command queue? */
1696 if (bytes < waitLinkBytes)
1698 /* No, create a new one. */
1699 gcmkONERROR(_NewQueue(Command));
1701 /* Get the new current offset. */
1702 offset = Command->offset;
1704 /* Recompute the number of bytes in the new kernel command queue. */
1705 bytes = Command->pageSize - offset;
1706 gcmkASSERT(bytes >= waitLinkBytes);
1709 /* Compute the location if WAIT/LINK command sequence. */
1710 waitLinkPhysical = (gctUINT8_PTR) Command->physical + offset;
1711 waitLinkLogical = (gctUINT8_PTR) Command->logical + offset;
1713 /* Determine the location to jump to for the command buffer being
1714 ** scheduled. */
1715 if (Command->newQueue)
1717 /* New command queue, jump to the beginning of it. */
1718 exitPhysical = Command->physical;
1719 exitLogical = Command->logical;
1720 exitBytes = Command->offset + waitLinkBytes;
1722 else
1724 /* Still within the preexisting command queue, jump to the new
1725 WAIT/LINK command sequence. */
1726 exitPhysical = waitLinkPhysical;
1727 exitLogical = waitLinkLogical;
1728 exitBytes = waitLinkBytes;
1731 /* Add a new WAIT/LINK command sequence. When the command buffer which is
1732 currently being scheduled is fully executed by the GPU, the FE will
1733 jump to this WAIT/LINK sequence. */
1734 gcmkONERROR(gckHARDWARE_WaitLink(
1735 hardware,
1736 waitLinkLogical,
1737 offset,
1738 &waitLinkBytes,
1739 &waitOffset,
1740 &waitSize
1743 /* Compute the location if WAIT command. */
1744 waitPhysical = (gctUINT8_PTR) waitLinkPhysical + waitOffset;
1745 waitLogical = (gctUINT8_PTR) waitLinkLogical + waitOffset;
1747 #if gcdNONPAGED_MEMORY_CACHEABLE
1748 /* Flush the command queue cache. */
1749 gcmkONERROR(gckOS_CacheClean(
1750 Command->os,
1751 Command->kernelProcessID,
1752 gcvNULL,
1753 exitPhysical,
1754 exitLogical,
1755 exitBytes
1757 #endif
1759 /* Determine the location of the LINK command in the command buffer. */
1760 commandBufferLink
1761 = (gctUINT8_PTR) commandBufferObject->logical
1762 + commandBufferObject->offset;
1764 /* Generate a LINK from the end of the command buffer being scheduled
1765 back to the kernel command queue. */
1766 gcmkONERROR(gckHARDWARE_Link(
1767 hardware,
1768 commandBufferLink,
1769 exitLogical,
1770 exitBytes,
1771 &linkBytes
1774 #if gcdNONPAGED_MEMORY_CACHEABLE
1775 /* Flush the command buffer cache. */
1776 gcmkONERROR(gckOS_CacheClean(
1777 Command->os,
1778 ProcessID,
1779 gcvNULL,
1780 commandBufferPhysical,
1781 commandBufferLogical,
1782 commandBufferSize
1784 #endif
1786 /* Generate a LINK from the previous WAIT/LINK command sequence to the
1787 entry determined above (either the context or the command buffer).
1788 This LINK replaces the WAIT instruction from the previous WAIT/LINK
1789 pair, therefore we use WAIT metrics for generation of this LINK.
1790 This action will execute the entire sequence. */
1791 gcmkONERROR(gckHARDWARE_Link(
1792 hardware,
1793 Command->waitLogical,
1794 entryLogical,
1795 entryBytes,
1796 &Command->waitSize
1799 #if gcdNONPAGED_MEMORY_CACHEABLE
1800 /* Flush the cache for the link. */
1801 gcmkONERROR(gckOS_CacheClean(
1802 Command->os,
1803 Command->kernelProcessID,
1804 gcvNULL,
1805 Command->waitPhysical,
1806 Command->waitLogical,
1807 Command->waitSize
1809 #endif
1811 gcmkDUMPCOMMAND(
1812 Command->os,
1813 Command->waitLogical,
1814 Command->waitSize,
1815 gceDUMP_BUFFER_LINK,
1816 gcvFALSE
1819 gcmkDUMPCOMMAND(
1820 Command->os,
1821 contextDumpLogical,
1822 contextDumpBytes,
1823 gceDUMP_BUFFER_CONTEXT,
1824 gcvFALSE
1827 gcmkDUMPCOMMAND(
1828 Command->os,
1829 bufferDumpLogical,
1830 bufferDumpBytes,
1831 gceDUMP_BUFFER_USER,
1832 gcvFALSE
1835 gcmkDUMPCOMMAND(
1836 Command->os,
1837 waitLinkLogical,
1838 waitLinkBytes,
1839 gceDUMP_BUFFER_WAITLINK,
1840 gcvFALSE
1843 /* Update the current pipe. */
1844 Command->pipeSelect = commandBufferObject->exitPipe;
1846 /* Update command queue offset. */
1847 Command->offset += waitLinkBytes;
1848 Command->newQueue = gcvFALSE;
1850 /* Update address of last WAIT. */
1851 Command->waitPhysical = waitPhysical;
1852 Command->waitLogical = waitLogical;
1853 Command->waitSize = waitSize;
1855 /* Update queue tail pointer. */
1856 gcmkONERROR(gckHARDWARE_UpdateQueueTail(
1857 hardware, Command->logical, Command->offset
1860 #if gcdDUMP_COMMAND && !gcdSIMPLE_COMMAND_DUMP
1861 gcmkPRINT("@[kernel.commit]");
1862 #endif
1864 /* Release the context switching mutex. */
1865 gcmkONERROR(gckOS_ReleaseMutex(Command->os, Command->mutexContext));
1866 contextAcquired = gcvFALSE;
1868 /* Release the command queue. */
1869 gcmkONERROR(gckCOMMAND_ExitCommit(Command, gcvFALSE));
1870 commitEntered = gcvFALSE;
1872 /* Loop while there are records in the queue. */
1873 while (EventQueue != gcvNULL)
1875 if (needCopy)
1877 /* Point to stack record. */
1878 eventRecord = &_eventRecord;
1880 /* Copy the data from the client. */
1881 gcmkONERROR(gckOS_CopyFromUserData(
1882 Command->os, eventRecord, EventQueue, gcmSIZEOF(gcsQUEUE)
1885 else
1887 /* Map record into kernel memory. */
1888 gcmkONERROR(gckOS_MapUserPointer(Command->os,
1889 EventQueue,
1890 gcmSIZEOF(gcsQUEUE),
1891 &pointer));
1893 eventRecord = pointer;
1896 /* Append event record to event queue. */
1897 gcmkONERROR(gckEVENT_AddList(
1898 Command->kernel->eventObj, &eventRecord->iface, gcvKERNEL_PIXEL, gcvTRUE
1901 /* Next record in the queue. */
1902 nextEventRecord = eventRecord->next;
1904 if (!needCopy)
1906 /* Unmap record from kernel memory. */
1907 gcmkONERROR(gckOS_UnmapUserPointer(
1908 Command->os, EventQueue, gcmSIZEOF(gcsQUEUE), (gctPOINTER *) eventRecord
1911 eventRecord = gcvNULL;
1914 EventQueue = nextEventRecord;
1917 /* Submit events. */
1918 gcmkONERROR(gckEVENT_Submit(Command->kernel->eventObj, gcvTRUE, gcvFALSE));
1920 /* Unmap the command buffer pointer. */
1921 if (commandBufferMapped)
1923 gcmkONERROR(gckOS_UnmapUserPointer(
1924 Command->os,
1925 CommandBuffer,
1926 gcmSIZEOF(struct _gcoCMDBUF),
1927 commandBufferObject
1930 commandBufferMapped = gcvFALSE;
1933 /* Return status. */
1934 gcmkFOOTER();
1935 return gcvSTATUS_OK;
1937 OnError:
1938 if ((eventRecord != gcvNULL) && !needCopy)
1940 /* Roll back. */
1941 gcmkVERIFY_OK(gckOS_UnmapUserPointer(
1942 Command->os,
1943 EventQueue,
1944 gcmSIZEOF(gcsQUEUE),
1945 (gctPOINTER *) eventRecord
1949 if (contextAcquired)
1951 /* Release the context switching mutex. */
1952 gcmkVERIFY_OK(gckOS_ReleaseMutex(Command->os, Command->mutexContext));
1955 if (commitEntered)
1957 /* Release the command queue mutex. */
1958 gcmkVERIFY_OK(gckCOMMAND_ExitCommit(Command, gcvFALSE));
1961 /* Unmap the command buffer pointer. */
1962 if (commandBufferMapped)
1964 gcmkVERIFY_OK(gckOS_UnmapUserPointer(
1965 Command->os,
1966 CommandBuffer,
1967 gcmSIZEOF(struct _gcoCMDBUF),
1968 commandBufferObject
1972 /* Return status. */
1973 gcmkFOOTER();
1974 return status;
1975 #endif
1978 /*******************************************************************************
1980 ** gckCOMMAND_Reserve
1982 ** Reserve space in the command queue. Also acquire the command queue mutex.
1984 ** INPUT:
1986 ** gckCOMMAND Command
1987 ** Pointer to an gckCOMMAND object.
1989 ** gctSIZE_T RequestedBytes
1990 ** Number of bytes previously reserved.
1992 ** OUTPUT:
1994 ** gctPOINTER * Buffer
1995 ** Pointer to a variable that will receive the address of the reserved
1996 ** space.
1998 ** gctSIZE_T * BufferSize
1999 ** Pointer to a variable that will receive the number of bytes
2000 ** available in the command queue.
2002 gceSTATUS
2003 gckCOMMAND_Reserve(
2004 IN gckCOMMAND Command,
2005 IN gctSIZE_T RequestedBytes,
2006 OUT gctPOINTER * Buffer,
2007 OUT gctSIZE_T * BufferSize
2010 gceSTATUS status;
2011 gctSIZE_T bytes;
2012 gctSIZE_T requiredBytes;
2013 gctUINT32 requestedAligned;
2015 gcmkHEADER_ARG("Command=0x%x RequestedBytes=%lu", Command, RequestedBytes);
2017 /* Verify the arguments. */
2018 gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND);
2020 /* Compute aligned number of reuested bytes. */
2021 requestedAligned = gcmALIGN(RequestedBytes, Command->alignment);
2023 /* Another WAIT/LINK command sequence will have to be appended after
2024 the requested area being reserved. Compute the number of bytes
2025 required for WAIT/LINK at the location after the reserved area. */
2026 gcmkONERROR(gckHARDWARE_WaitLink(
2027 Command->kernel->hardware,
2028 gcvNULL,
2029 Command->offset + requestedAligned,
2030 &requiredBytes,
2031 gcvNULL,
2032 gcvNULL
2035 /* Compute total number of bytes required. */
2036 requiredBytes += requestedAligned;
2038 /* Compute number of bytes available in command queue. */
2039 bytes = Command->pageSize - Command->offset;
2041 /* Is there enough space in the current command queue? */
2042 if (bytes < requiredBytes)
2044 /* Create a new command queue. */
2045 gcmkONERROR(_NewQueue(Command));
2047 /* Recompute the number of bytes in the new kernel command queue. */
2048 bytes = Command->pageSize - Command->offset;
2050 /* Still not enough space? */
2051 if (bytes < requiredBytes)
2053 /* Rare case, not enough room in command queue. */
2054 gcmkONERROR(gcvSTATUS_BUFFER_TOO_SMALL);
2058 /* Return pointer to empty slot command queue. */
2059 *Buffer = (gctUINT8 *) Command->logical + Command->offset;
2061 /* Return number of bytes left in command queue. */
2062 *BufferSize = bytes;
2064 /* Success. */
2065 gcmkFOOTER_ARG("*Buffer=0x%x *BufferSize=%lu", *Buffer, *BufferSize);
2066 return gcvSTATUS_OK;
2068 OnError:
2069 /* Return status. */
2070 gcmkFOOTER();
2071 return status;
2074 /*******************************************************************************
2076 ** gckCOMMAND_Execute
2078 ** Execute a previously reserved command queue by appending a WAIT/LINK command
2079 ** sequence after it and modifying the last WAIT into a LINK command. The
2080 ** command FIFO mutex will be released whether this function succeeds or not.
2082 ** INPUT:
2084 ** gckCOMMAND Command
2085 ** Pointer to an gckCOMMAND object.
2087 ** gctSIZE_T RequestedBytes
2088 ** Number of bytes previously reserved.
2090 ** OUTPUT:
2092 ** Nothing.
2094 gceSTATUS
2095 gckCOMMAND_Execute(
2096 IN gckCOMMAND Command,
2097 IN gctSIZE_T RequestedBytes
2100 gceSTATUS status;
2102 gctPHYS_ADDR waitLinkPhysical;
2103 gctUINT8_PTR waitLinkLogical;
2104 gctUINT32 waitLinkOffset;
2105 gctSIZE_T waitLinkBytes;
2107 gctPHYS_ADDR waitPhysical;
2108 gctPOINTER waitLogical;
2109 gctUINT32 waitOffset;
2110 gctSIZE_T waitBytes;
2112 gctPHYS_ADDR execPhysical;
2113 gctPOINTER execLogical;
2114 gctSIZE_T execBytes;
2116 gcmkHEADER_ARG("Command=0x%x RequestedBytes=%lu", Command, RequestedBytes);
2118 /* Verify the arguments. */
2119 gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND);
2121 /* Compute offset for WAIT/LINK. */
2122 waitLinkOffset = Command->offset + RequestedBytes;
2124 /* Compute number of bytes left in command queue. */
2125 waitLinkBytes = Command->pageSize - waitLinkOffset;
2127 /* Compute the location if WAIT/LINK command sequence. */
2128 waitLinkPhysical = (gctUINT8_PTR) Command->physical + waitLinkOffset;
2129 waitLinkLogical = (gctUINT8_PTR) Command->logical + waitLinkOffset;
2131 /* Append WAIT/LINK in command queue. */
2132 gcmkONERROR(gckHARDWARE_WaitLink(
2133 Command->kernel->hardware,
2134 waitLinkLogical,
2135 waitLinkOffset,
2136 &waitLinkBytes,
2137 &waitOffset,
2138 &waitBytes
2141 /* Compute the location if WAIT command. */
2142 waitPhysical = (gctUINT8_PTR) waitLinkPhysical + waitOffset;
2143 waitLogical = waitLinkLogical + waitOffset;
2145 /* Determine the location to jump to for the command buffer being
2146 ** scheduled. */
2147 if (Command->newQueue)
2149 /* New command queue, jump to the beginning of it. */
2150 execPhysical = Command->physical;
2151 execLogical = Command->logical;
2152 execBytes = waitLinkOffset + waitLinkBytes;
2154 else
2156 /* Still within the preexisting command queue, jump directly to the
2157 reserved area. */
2158 execPhysical = (gctUINT8 *) Command->physical + Command->offset;
2159 execLogical = (gctUINT8 *) Command->logical + Command->offset;
2160 execBytes = RequestedBytes + waitLinkBytes;
2163 #if gcdNONPAGED_MEMORY_CACHEABLE
2164 /* Flush the cache. */
2165 gcmkONERROR(gckOS_CacheClean(
2166 Command->os,
2167 Command->kernelProcessID,
2168 gcvNULL,
2169 execPhysical,
2170 execLogical,
2171 execBytes
2173 #endif
2175 /* Convert the last WAIT into a LINK. */
2176 gcmkONERROR(gckHARDWARE_Link(
2177 Command->kernel->hardware,
2178 Command->waitLogical,
2179 execLogical,
2180 execBytes,
2181 &Command->waitSize
2184 #if gcdNONPAGED_MEMORY_CACHEABLE
2185 /* Flush the cache. */
2186 gcmkONERROR(gckOS_CacheClean(
2187 Command->os,
2188 Command->kernelProcessID,
2189 gcvNULL,
2190 Command->waitPhysical,
2191 Command->waitLogical,
2192 Command->waitSize
2194 #endif
2196 gcmkDUMPCOMMAND(
2197 Command->os,
2198 Command->waitLogical,
2199 Command->waitSize,
2200 gceDUMP_BUFFER_LINK,
2201 gcvFALSE
2204 gcmkDUMPCOMMAND(
2205 Command->os,
2206 execLogical,
2207 execBytes,
2208 gceDUMP_BUFFER_KERNEL,
2209 gcvFALSE
2212 /* Update the pointer to the last WAIT. */
2213 Command->waitPhysical = waitPhysical;
2214 Command->waitLogical = waitLogical;
2215 Command->waitSize = waitBytes;
2217 /* Update the command queue. */
2218 Command->offset += RequestedBytes + waitLinkBytes;
2219 Command->newQueue = gcvFALSE;
2221 /* Update queue tail pointer. */
2222 gcmkONERROR(gckHARDWARE_UpdateQueueTail(
2223 Command->kernel->hardware, Command->logical, Command->offset
2226 #if gcdDUMP_COMMAND && !gcdSIMPLE_COMMAND_DUMP
2227 gcmkPRINT("@[kernel.execute]");
2228 #endif
2230 /* Success. */
2231 gcmkFOOTER_NO();
2232 return gcvSTATUS_OK;
2234 OnError:
2235 /* Return the status. */
2236 gcmkFOOTER();
2237 return status;
2240 /*******************************************************************************
2242 ** gckCOMMAND_Stall
2244 ** The calling thread will be suspended until the command queue has been
2245 ** completed.
2247 ** INPUT:
2249 ** gckCOMMAND Command
2250 ** Pointer to an gckCOMMAND object.
2252 ** gctBOOL FromPower
2253 ** Determines whether the call originates from inside the power
2254 ** management or not.
2256 ** OUTPUT:
2258 ** Nothing.
2260 gceSTATUS
2261 gckCOMMAND_Stall(
2262 IN gckCOMMAND Command,
2263 IN gctBOOL FromPower
2266 #if gcdNULL_DRIVER
2267 /* Do nothing with infinite hardware. */
2268 return gcvSTATUS_OK;
2269 #else
2270 gckOS os;
2271 gckHARDWARE hardware;
2272 gckEVENT eventObject;
2273 gceSTATUS status;
2274 gctSIGNAL signal = gcvNULL;
2275 gctUINT timer = 0;
2277 gcmkHEADER_ARG("Command=0x%x", Command);
2279 /* Verify the arguments. */
2280 gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND);
2282 /* Extract the gckOS object pointer. */
2283 os = Command->os;
2284 gcmkVERIFY_OBJECT(os, gcvOBJ_OS);
2286 /* Extract the gckHARDWARE object pointer. */
2287 hardware = Command->kernel->hardware;
2288 gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE);
2290 /* Extract the gckEVENT object pointer. */
2291 eventObject = Command->kernel->eventObj;
2292 gcmkVERIFY_OBJECT(eventObject, gcvOBJ_EVENT);
2294 /* Allocate the signal. */
2295 gcmkONERROR(gckOS_CreateSignal(os, gcvTRUE, &signal));
2297 /* Append the EVENT command to trigger the signal. */
2298 gcmkONERROR(gckEVENT_Signal(eventObject, signal, gcvKERNEL_PIXEL));
2300 /* Submit the event queue. */
2301 gcmkONERROR(gckEVENT_Submit(eventObject, gcvTRUE, FromPower));
2303 #if gcdDUMP_COMMAND && !gcdSIMPLE_COMMAND_DUMP
2304 gcmkPRINT("@[kernel.stall]");
2305 #endif
2307 if (status == gcvSTATUS_CHIP_NOT_READY)
2309 /* Error. */
2310 goto OnError;
2315 /* Wait for the signal. */
2316 status = gckOS_WaitSignal(os, signal, gcdGPU_ADVANCETIMER);
2318 if (status == gcvSTATUS_TIMEOUT)
2320 #if gcmIS_DEBUG(gcdDEBUG_CODE)
2321 gctUINT32 idle;
2323 /* Read idle register. */
2324 gcmkVERIFY_OK(gckHARDWARE_GetIdle(
2325 hardware, gcvFALSE, &idle
2328 gcmkTRACE(
2329 gcvLEVEL_ERROR,
2330 "%s(%d): idle=%08x",
2331 __FUNCTION__, __LINE__, idle
2334 gcmkONERROR(gckOS_MemoryBarrier(os, gcvNULL));
2336 #ifdef __QNXNTO__
2337 gctUINT32 reg_cmdbuf_fetch;
2338 gctUINT32 reg_intr;
2340 gcmkVERIFY_OK(gckOS_ReadRegisterEx(
2341 Command->kernel->hardware->os, Command->kernel->core, 0x0664, &reg_cmdbuf_fetch
2344 if (idle == 0x7FFFFFFE)
2347 * GPU is idle so there should not be pending interrupts.
2348 * Just double check.
2350 * Note that reading interrupt register clears it.
2351 * That's why we don't read it in all cases.
2353 gcmkVERIFY_OK(gckOS_ReadRegisterEx(
2354 Command->kernel->hardware->os, Command->kernel->core, 0x10, &reg_intr
2357 slogf(
2358 _SLOG_SETCODE(1, 0),
2359 _SLOG_CRITICAL,
2360 "GALcore: Stall timeout (idle = 0x%X, command buffer fetch = 0x%X, interrupt = 0x%X)",
2361 idle, reg_cmdbuf_fetch, reg_intr
2364 else
2366 slogf(
2367 _SLOG_SETCODE(1, 0),
2368 _SLOG_CRITICAL,
2369 "GALcore: Stall timeout (idle = 0x%X, command buffer fetch = 0x%X)",
2370 idle, reg_cmdbuf_fetch
2373 #endif
2374 #endif
2375 /* Advance timer. */
2376 timer += gcdGPU_ADVANCETIMER;
2379 while (gcmIS_ERROR(status)
2380 #if gcdGPU_TIMEOUT
2381 && (timer < Command->kernel->timeOut)
2382 #endif
2385 /* Bail out on timeout. */
2386 if (gcmIS_ERROR(status))
2388 /* Broadcast the stuck GPU. */
2389 gcmkONERROR(gckOS_Broadcast(
2390 os, hardware, gcvBROADCAST_GPU_STUCK
2393 gcmkONERROR(gcvSTATUS_GPU_NOT_RESPONDING);
2396 /* Delete the signal. */
2397 gcmkVERIFY_OK(gckOS_DestroySignal(os, signal));
2399 /* Success. */
2400 gcmkFOOTER_NO();
2401 return gcvSTATUS_OK;
2403 OnError:
2404 if (signal != gcvNULL)
2406 /* Free the signal. */
2407 gcmkVERIFY_OK(gckOS_DestroySignal(os, signal));
2410 /* Return the status. */
2411 gcmkFOOTER();
2412 return status;
2413 #endif
2416 /*******************************************************************************
2418 ** gckCOMMAND_Attach
2420 ** Attach user process.
2422 ** INPUT:
2424 ** gckCOMMAND Command
2425 ** Pointer to a gckCOMMAND object.
2427 ** gctUINT32 ProcessID
2428 ** Current process ID.
2430 ** OUTPUT:
2432 ** gckCONTEXT * Context
2433 ** Pointer to a variable that will receive a pointer to a new
2434 ** gckCONTEXT object.
2436 ** gctSIZE_T * StateCount
2437 ** Pointer to a variable that will receive the number of states
2438 ** in the context buffer.
2440 gceSTATUS
2441 gckCOMMAND_Attach(
2442 IN gckCOMMAND Command,
2443 OUT gckCONTEXT * Context,
2444 OUT gctSIZE_T * StateCount,
2445 IN gctUINT32 ProcessID
2448 gceSTATUS status;
2449 gctBOOL acquired = gcvFALSE;
2451 gcmkHEADER_ARG("Command=0x%x", Command);
2453 /* Verify the arguments. */
2454 gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND);
2456 /* Acquire the context switching mutex. */
2457 gcmkONERROR(gckOS_AcquireMutex(
2458 Command->os, Command->mutexContext, gcvINFINITE
2460 acquired = gcvTRUE;
2462 /* Construct a gckCONTEXT object. */
2463 gcmkONERROR(gckCONTEXT_Construct(
2464 Command->os,
2465 Command->kernel->hardware,
2466 ProcessID,
2467 Context
2470 /* Return the number of states in the context. */
2471 * StateCount = (* Context)->stateCount;
2473 /* Release the context switching mutex. */
2474 gcmkONERROR(gckOS_ReleaseMutex(Command->os, Command->mutexContext));
2475 acquired = gcvFALSE;
2477 /* Success. */
2478 gcmkFOOTER_ARG("*Context=0x%x", *Context);
2479 return gcvSTATUS_OK;
2481 OnError:
2482 /* Release mutex. */
2483 if (acquired)
2485 /* Release the context switching mutex. */
2486 gcmkVERIFY_OK(gckOS_ReleaseMutex(Command->os, Command->mutexContext));
2487 acquired = gcvFALSE;
2490 /* Return the status. */
2491 gcmkFOOTER();
2492 return status;
2495 /*******************************************************************************
2497 ** gckCOMMAND_Detach
2499 ** Detach user process.
2501 ** INPUT:
2503 ** gckCOMMAND Command
2504 ** Pointer to a gckCOMMAND object.
2506 ** gckCONTEXT Context
2507 ** Pointer to a gckCONTEXT object to be destroyed.
2509 ** OUTPUT:
2511 ** Nothing.
2513 gceSTATUS
2514 gckCOMMAND_Detach(
2515 IN gckCOMMAND Command,
2516 IN gckCONTEXT Context
2519 gceSTATUS status;
2520 gctBOOL acquired = gcvFALSE;
2522 gcmkHEADER_ARG("Command=0x%x Context=0x%x", Command, Context);
2524 /* Verify the arguments. */
2525 gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND);
2527 /* Acquire the context switching mutex. */
2528 gcmkONERROR(gckOS_AcquireMutex(
2529 Command->os, Command->mutexContext, gcvINFINITE
2531 acquired = gcvTRUE;
2533 /* Construct a gckCONTEXT object. */
2534 gcmkONERROR(gckCONTEXT_Destroy(Context));
2536 /* Release the context switching mutex. */
2537 gcmkONERROR(gckOS_ReleaseMutex(Command->os, Command->mutexContext));
2538 acquired = gcvFALSE;
2540 /* Return the status. */
2541 gcmkFOOTER();
2542 return gcvSTATUS_OK;
2544 OnError:
2545 /* Release mutex. */
2546 if (acquired)
2548 /* Release the context switching mutex. */
2549 gcmkVERIFY_OK(gckOS_ReleaseMutex(Command->os, Command->mutexContext));
2550 acquired = gcvFALSE;
2553 /* Return the status. */
2554 gcmkFOOTER();
2555 return status;