/*******************************************************************************
**
**	gckKERNEL_Dispatch
**
**	Dispatch a command received from the user HAL layer.
**
**	INPUT:
**
**		gckKERNEL Kernel
**			Pointer to an gckKERNEL object.
**
**		gctBOOL FromUser
**			whether the call is from the user space.
**
**		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
gckKERNEL_Dispatch(
	IN gckKERNEL Kernel,
	IN gctBOOL FromUser,
	IN OUT gcsHAL_INTERFACE * Interface
	)
{
	gceSTATUS status;
	gctUINT32 bitsPerPixel;
	gctSIZE_T bytes;
	gcuVIDMEM_NODE_PTR node;
	gctBOOL locked = gcvFALSE;
	gctPHYS_ADDR physical;
	gctUINT32 address;

	gcmkHEADER_ARG("Kernel=0x%x FromUser=%d Interface=0x%x",
				   Kernel, FromUser, Interface);

	/* Verify the arguments. */
	gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
	gcmkVERIFY_ARGUMENT(Interface != gcvNULL);

	gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_KERNEL,
				   "Dispatching command %d", Interface->command);

	/* Dispatch on command. */
	switch (Interface->command)
	{
	case gcvHAL_GET_BASE_ADDRESS:
		/* Get base address. */
		gcmkONERROR(
			gckOS_GetBaseAddress(Kernel->os,
								 &Interface->u.GetBaseAddress.baseAddress));
		break;

    case gcvHAL_QUERY_VIDEO_MEMORY:
        /* Query video memory size. */
        gcmkONERROR(gckKERNEL_QueryVideoMemory(Kernel, Interface));
		break;

	case gcvHAL_QUERY_CHIP_IDENTITY:
		/* Query chip identity. */
		gcmkONERROR(
			gckHARDWARE_QueryChipIdentity(
				Kernel->hardware,
				&Interface->u.QueryChipIdentity.chipModel,
				&Interface->u.QueryChipIdentity.chipRevision,
				&Interface->u.QueryChipIdentity.chipFeatures,
				&Interface->u.QueryChipIdentity.chipMinorFeatures,
				&Interface->u.QueryChipIdentity.chipMinorFeatures1));

		/* Query chip specifications. */
		gcmkONERROR(
			gckHARDWARE_QueryChipSpecs(
				Kernel->hardware,
				&Interface->u.QueryChipIdentity.streamCount,
				&Interface->u.QueryChipIdentity.registerMax,
				&Interface->u.QueryChipIdentity.threadCount,
				&Interface->u.QueryChipIdentity.shaderCoreCount,
				&Interface->u.QueryChipIdentity.vertexCacheSize,
				&Interface->u.QueryChipIdentity.vertexOutputBufferSize));
		break;

	case gcvHAL_MAP_MEMORY:
		physical = Interface->u.MapMemory.physical;

		/* Map memory. */
		gcmkONERROR(
			gckKERNEL_MapMemory(Kernel,
								physical,
								Interface->u.MapMemory.bytes,
								&Interface->u.MapMemory.logical));
		break;

	case gcvHAL_UNMAP_MEMORY:
		physical = Interface->u.UnmapMemory.physical;

		/* Unmap memory. */
		gcmkONERROR(
			gckKERNEL_UnmapMemory(Kernel,
								  physical,
								  Interface->u.UnmapMemory.bytes,
								  Interface->u.UnmapMemory.logical));
		break;

	case gcvHAL_ALLOCATE_NON_PAGED_MEMORY:
		/* Allocate non-paged memory. */
#ifdef __QNXNTO__
		if (FromUser)
		{
			gcmkONERROR(
				gckOS_AllocateNonPagedMemoryShmPool(
				Kernel->os,
				FromUser,
				Interface->pid,
				Interface->handle,
				&Interface->u.AllocateNonPagedMemory.bytes,
				&Interface->u.AllocateNonPagedMemory.physical,
				&Interface->u.AllocateNonPagedMemory.logical));
			break;
		}
#endif
		gcmkONERROR(
			gckOS_AllocateNonPagedMemory(
				Kernel->os,
				FromUser,
				&Interface->u.AllocateNonPagedMemory.bytes,
				&Interface->u.AllocateNonPagedMemory.physical,
				&Interface->u.AllocateNonPagedMemory.logical));
		break;

	case gcvHAL_FREE_NON_PAGED_MEMORY:
		physical = Interface->u.FreeNonPagedMemory.physical;

		/* Free non-paged memory. */
		gcmkONERROR(
			gckOS_FreeNonPagedMemory(Kernel->os,
									 Interface->u.FreeNonPagedMemory.bytes,
									 physical,
									 Interface->u.FreeNonPagedMemory.logical));
		break;

	case gcvHAL_ALLOCATE_CONTIGUOUS_MEMORY:
		/* Allocate contiguous memory. */
#ifdef __QNXNTO__
		if (FromUser)
		{
			gcmkONERROR(
				gckOS_AllocateNonPagedMemoryShmPool(
				Kernel->os,
				FromUser,
				Interface->pid,
				Interface->handle,
				&Interface->u.AllocateNonPagedMemory.bytes,
				&Interface->u.AllocateNonPagedMemory.physical,
				&Interface->u.AllocateNonPagedMemory.logical));
			break;
		}
#endif
		gcmkONERROR(
			gckOS_AllocateContiguous(
				Kernel->os,
				FromUser,
				&Interface->u.AllocateContiguousMemory.bytes,
				&Interface->u.AllocateContiguousMemory.physical,
				&Interface->u.AllocateContiguousMemory.logical));

		break;

	case gcvHAL_FREE_CONTIGUOUS_MEMORY:
		physical = Interface->u.FreeContiguousMemory.physical;

       /* Free contiguous memory. */
        gcmkONERROR(
            gckOS_FreeContiguous(Kernel->os,
                                 physical,
                                 Interface->u.FreeContiguousMemory.logical,
                                 Interface->u.FreeContiguousMemory.bytes));
        break;

	case gcvHAL_ALLOCATE_VIDEO_MEMORY:
		/* Align width and height to tiles. */
		gcmkONERROR(
			gckHARDWARE_AlignToTile(Kernel->hardware,
									Interface->u.AllocateVideoMemory.type,
									&Interface->u.AllocateVideoMemory.width,
									&Interface->u.AllocateVideoMemory.height,
									gcvNULL));

		/* Convert format into bytes per pixel and bytes per tile. */
		gcmkONERROR(
			gckHARDWARE_ConvertFormat(Kernel->hardware,
									  Interface->u.AllocateVideoMemory.format,
									  &bitsPerPixel,
									  gcvNULL));

		/* Compute number of bytes for the allocation. */
		bytes = Interface->u.AllocateVideoMemory.width * bitsPerPixel
			  * Interface->u.AllocateVideoMemory.height
			  * Interface->u.AllocateVideoMemory.depth / 8;

		/* Allocate memory. */
#ifdef __QNXNTO__
		gcmkONERROR(
			_AllocateMemory(Kernel,
							&Interface->u.AllocateVideoMemory.pool,
							bytes,
							64,
							Interface->u.AllocateVideoMemory.type,
							Interface->handle,
							&Interface->u.AllocateVideoMemory.node));
#else
		gcmkONERROR(
			_AllocateMemory(Kernel,
							&Interface->u.AllocateVideoMemory.pool,
							bytes,
							64,
							Interface->u.AllocateVideoMemory.type,
							&Interface->u.AllocateVideoMemory.node));
#endif
		break;

	case gcvHAL_ALLOCATE_LINEAR_VIDEO_MEMORY:
		/* Allocate memory. */
#ifdef __QNXNTO__
		gcmkONERROR(
			_AllocateMemory(Kernel,
							&Interface->u.AllocateLinearVideoMemory.pool,
							Interface->u.AllocateLinearVideoMemory.bytes,
							Interface->u.AllocateLinearVideoMemory.alignment,
							Interface->u.AllocateLinearVideoMemory.type,
							Interface->handle,
							&Interface->u.AllocateLinearVideoMemory.node));

		/* Set the current user pid in the node,
		 * which is used while locking memory. */
		gcmkVERIFY_OK(gckVIDMEM_SetPID(
				Interface->u.AllocateLinearVideoMemory.node,
				Interface->pid));
#else
		gcmkONERROR(
			_AllocateMemory(Kernel,
							&Interface->u.AllocateLinearVideoMemory.pool,
							Interface->u.AllocateLinearVideoMemory.bytes,
							Interface->u.AllocateLinearVideoMemory.alignment,
							Interface->u.AllocateLinearVideoMemory.type,
							&Interface->u.AllocateLinearVideoMemory.node));
#endif
		break;

    case gcvHAL_FREE_VIDEO_MEMORY:
#ifdef __QNXNTO__
        node = Interface->u.FreeVideoMemory.node;
        if (node->VidMem.memory->object.type == gcvOBJ_VIDMEM
         && node->VidMem.logical != gcvNULL)
        {
            gcmkONERROR(
                    gckKERNEL_UnmapVideoMemory(Kernel,
                                               node->VidMem.logical,
                                               Interface->pid,
                                               node->VidMem.bytes));
            node->VidMem.logical = gcvNULL;
        }
#endif
        /* Free video memory. */
        gcmkONERROR(
            gckVIDMEM_Free(Interface->u.FreeVideoMemory.node));
        break;

	case gcvHAL_LOCK_VIDEO_MEMORY:
		/* Lock video memory. */
		gcmkONERROR(
			gckVIDMEM_Lock(Interface->u.LockVideoMemory.node,
						   &Interface->u.LockVideoMemory.address));

		locked = gcvTRUE;

		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,
										 Interface->pid,
										 node->VidMem.bytes,
										 &node->VidMem.logical));
        }
		Interface->u.LockVideoMemory.memory = node->VidMem.logical;
#else
			gcmkONERROR(
				gckKERNEL_MapVideoMemory(Kernel,
										 FromUser,
										 Interface->u.LockVideoMemory.address,
										 &Interface->u.LockVideoMemory.memory));
#endif

#ifdef __QNXNTO__
			/* Add more information to node, which is used while unmapping. */
			gcmkVERIFY_OK(gckVIDMEM_SetPID(
					Interface->u.LockVideoMemory.node,
					Interface->pid));
#endif
		}

		else
		{
			/* Copy logical memory for virtual memory. */
			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
        break;

	case gcvHAL_UNLOCK_VIDEO_MEMORY:
		/* Unlock video memory. */
		node = Interface->u.UnlockVideoMemory.node;

        /* Unlock video memory. */
        gcmkONERROR(
            gckVIDMEM_Unlock(node,
                             Interface->u.UnlockVideoMemory.type,
                             &Interface->u.UnlockVideoMemory.asynchroneous));
        break;

	case gcvHAL_EVENT_COMMIT:
		/* Commit an event queue. */
		gcmkONERROR(
			gckEVENT_Commit(Kernel->event,
						    Interface->u.Event.queue));
        break;

    case gcvHAL_COMMIT:
        /* Commit a command and context buffer. */
        gcmkONERROR(
            gckCOMMAND_Commit(Kernel->command,
                              Interface->u.Commit.commandBuffer,
                              Interface->u.Commit.contextBuffer,
                              Interface->u.Commit.process));
        break;

    case gcvHAL_STALL:
        /* Stall the command queue. */
        gcmkONERROR(gckCOMMAND_Stall(Kernel->command));
        break;

	case gcvHAL_MAP_USER_MEMORY:
		/* Map user memory to DMA. */
		gcmkONERROR(
			gckOS_MapUserMemory(Kernel->os,
								Interface->u.MapUserMemory.memory,
								Interface->u.MapUserMemory.size,
								&Interface->u.MapUserMemory.info,
								&Interface->u.MapUserMemory.address));
		break;

	case gcvHAL_UNMAP_USER_MEMORY:
		address = Interface->u.MapUserMemory.address;

		/* Unmap user memory. */
		gcmkONERROR(
			gckOS_UnmapUserMemory(Kernel->os,
								  Interface->u.UnmapUserMemory.memory,
								  Interface->u.UnmapUserMemory.size,
								  Interface->u.UnmapUserMemory.info,
								  address));
		break;

#if !USE_NEW_LINUX_SIGNAL
	case gcvHAL_USER_SIGNAL:
     	gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_KERNEL,
				   "Dispatching gcvHAL_USER_SIGNAL %d", Interface->u.UserSignal.command);
		/* Dispatch depends on the user signal subcommands. */
		switch(Interface->u.UserSignal.command)
		{
		case gcvUSER_SIGNAL_CREATE:
			/* Create a signal used in the user space. */
			gcmkONERROR(
				gckOS_CreateUserSignal(Kernel->os,
									   Interface->u.UserSignal.manualReset,
                                       Interface->u.UserSignal.signalType,
									   &Interface->u.UserSignal.id));
			break;

		case gcvUSER_SIGNAL_DESTROY:
			/* Destroy the signal. */
			gcmkONERROR(
				gckOS_DestroyUserSignal(Kernel->os,
										Interface->u.UserSignal.id));
			break;

		case gcvUSER_SIGNAL_SIGNAL:
			/* Signal the signal. */
			gcmkONERROR(
				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. */
			gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
		}
        break;
#endif

    case gcvHAL_SET_POWER_MANAGEMENT_STATE:
		/* Set the power management state. */
		gcmkONERROR(
			gckHARDWARE_SetPowerManagementState(
				Kernel->hardware,
				Interface->u.SetPowerManagement.state));
		break;

    case gcvHAL_QUERY_POWER_MANAGEMENT_STATE:
        /* Chip is not idle. */
        Interface->u.QueryPowerManagement.isIdle = gcvFALSE;

		/* Query the power management state. */
        gcmkONERROR(gckHARDWARE_QueryPowerManagementState(
            Kernel->hardware,
            &Interface->u.QueryPowerManagement.state));

        /* Query the idle state. */
        gcmkONERROR(
            gckHARDWARE_QueryIdle(Kernel->hardware,
                                  &Interface->u.QueryPowerManagement.isIdle));
        break;

    case gcvHAL_READ_REGISTER:
#if gcdREGISTER_ACCESS_FROM_USER
        /* Read a register. */
        gcmkONERROR(
            gckOS_ReadRegister(Kernel->os,
                               Interface->u.ReadRegisterData.address,
                               &Interface->u.ReadRegisterData.data));
#else
		/* No access from user land to read registers. */
		Interface->u.ReadRegisterData.data = 0;
		status = gcvSTATUS_NOT_SUPPORTED;
#endif
        break;

    case gcvHAL_WRITE_REGISTER:
#if gcdREGISTER_ACCESS_FROM_USER
        /* Write a register. */
        gcmkONERROR(
            gckOS_WriteRegister(Kernel->os,
                                Interface->u.WriteRegisterData.address,
                                Interface->u.WriteRegisterData.data));
#else
		/* No access from user land to write registers. */
		status = gcvSTATUS_NOT_SUPPORTED;
#endif
        break;

    case gcvHAL_READ_ALL_PROFILE_REGISTERS:
#if VIVANTE_PROFILER
		/* Read all 3D profile registers. */
		gcmkONERROR(
			gckHARDWARE_QueryProfileRegisters(
				Kernel->hardware,
				&Interface->u.RegisterProfileData.counters));
#else
        status = gcvSTATUS_OK;
#endif
        break;

    case gcvHAL_PROFILE_REGISTERS_2D:
#if VIVANTE_PROFILER
		/* Read all 2D profile registers. */
		gcmkONERROR(
			gckHARDWARE_ProfileEngine2D(
				Kernel->hardware,
				Interface->u.RegisterProfileData2D.hwProfile2D));
#else
        status = gcvSTATUS_OK;
#endif
        break;

	case gcvHAL_GET_PROFILE_SETTING:
#if VIVANTE_PROFILER
		/* Get profile setting */
		Interface->u.GetProfileSetting.enable = Kernel->profileEnable;

		gcmkVERIFY_OK(
			gckOS_MemCopy(Interface->u.GetProfileSetting.fileName,
						  Kernel->profileFileName,
						  gcdMAX_PROFILE_FILE_NAME));
#endif

		status = gcvSTATUS_OK;
        break;

	case gcvHAL_SET_PROFILE_SETTING:
#if VIVANTE_PROFILER
		/* Set profile setting */
		Kernel->profileEnable = Interface->u.SetProfileSetting.enable;

		gcmkVERIFY_OK(
			gckOS_MemCopy(Kernel->profileFileName,
						  Interface->u.SetProfileSetting.fileName,
						  gcdMAX_PROFILE_FILE_NAME));
#endif

        status = gcvSTATUS_OK;
		break;

	case gcvHAL_QUERY_KERNEL_SETTINGS:
		/* Get kernel settings. */
		gcmkONERROR(
			gckKERNEL_QuerySettings(Kernel,
									&Interface->u.QueryKernelSettings.settings));
		break;

	case gcvHAL_RESET:
		/* Reset the hardware. */
		gcmkONERROR(
			gckHARDWARE_Reset(Kernel->hardware));
		break;

    case gcvHAL_DEBUG:
        /* Set debug level and zones. */
        if (Interface->u.Debug.set)
        {
            gckOS_SetDebugLevel(Interface->u.Debug.level);
            gckOS_SetDebugZones(Interface->u.Debug.zones,
                                Interface->u.Debug.enable);
        }

        if (Interface->u.Debug.message[0] != '\0')
        {
            /* Print a message to the debugger. */
            gcmkPRINT(Interface->u.Debug.message);
        }
        status = gcvSTATUS_OK;
        break;

    case gcvHAL_CACHE:
        if (Interface->u.Cache.invalidate)
        {
            /* Flush and invalidate the cache. */
            status = gckOS_CacheInvalidate(Kernel->os,
                                           Interface->u.Cache.process,
                                           Interface->u.Cache.logical,
                                           Interface->u.Cache.bytes);
        }
        else
        {
            /* Flush the cache. */
            status = gckOS_CacheFlush(Kernel->os,
                                      Interface->u.Cache.process,
                                      Interface->u.Cache.logical,
                                      Interface->u.Cache.bytes);
        }
		break;
    	
	default:
		/* Invalid command. */
		gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
	}

OnError:
	/* Save status. */
	Interface->status = status;

    if (gcmIS_ERROR(status))
    {
        if (locked)
        {
            /* Roll back the lock. */
            gcmkVERIFY_OK(
                gckVIDMEM_Unlock(Interface->u.LockVideoMemory.node,
                                 gcvSURF_TYPE_UNKNOWN,
                                 gcvNULL));
        }
    }

	/* Return the status. */
	gcmkFOOTER();
	return status;
}
Exemplo n.º 2
0
/*******************************************************************************
**
**	gckCOMMAND_Construct
**
**	Construct a new gckCOMMAND object.
**
**	INPUT:
**
**		gckKERNEL Kernel
**			Pointer to an gckKERNEL object.
**
**	OUTPUT:
**
**		gckCOMMAND * Command
**			Pointer to a variable that will hold the pointer to the gckCOMMAND
**			object.
*/
gceSTATUS
gckCOMMAND_Construct(
	IN gckKERNEL Kernel,
	OUT gckCOMMAND * Command
	)
{
    gckOS os;
	gckCOMMAND command = gcvNULL;
	gceSTATUS status;
	gctINT i;

	gcmkHEADER_ARG("Kernel=0x%x", Kernel);

	/* Verify the arguments. */
	gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
	gcmkVERIFY_ARGUMENT(Command != gcvNULL);

    /* Extract the gckOS object. */
    os = Kernel->os;

	/* Allocate the gckCOMMAND structure. */
	gcmkONERROR(
		gckOS_Allocate(os,
					   gcmSIZEOF(struct _gckCOMMAND),
					   (gctPOINTER *) &command));

	/* Initialize the gckCOMMAND object.*/
	command->object.type  = gcvOBJ_COMMAND;
	command->kernel       = Kernel;
	command->os           = os;
	command->mutexQueue   = gcvNULL;
	command->mutexContext = gcvNULL;

    /* No command queues created yet. */
	command->index = 0;
	for (i = 0; i < gcdCOMMAND_QUEUES; ++i)
	{
		command->queues[i].signal  = gcvNULL;
		command->queues[i].logical = gcvNULL;
	}

    /* Get the command buffer requirements. */
    gcmkONERROR(
    	gckHARDWARE_QueryCommandBuffer(Kernel->hardware,
                                       &command->alignment,
                                       &command->reservedHead,
                                       &command->reservedTail));

    /* No contexts available yet. */
    command->contextCounter = command->currentContext = 0;

    /* Create the command queue mutex. */
    gcmkONERROR(
    	gckOS_CreateMutex(os, &command->mutexQueue));

	/* Create the context switching mutex. */
	gcmkONERROR(
		gckOS_CreateMutex(os, &command->mutexContext));

	/* Get the page size from teh OS. */
	gcmkONERROR(
		gckOS_GetPageSize(os, &command->pageSize));

	/* Set hardware to pipe 0. */
	command->pipeSelect = 0;

    /* Pre-allocate the command queues. */
    for (i = 0; i < gcdCOMMAND_QUEUES; ++i)
    {
        gcmkONERROR(
            gckOS_AllocateNonPagedMemory(os,
                                         gcvFALSE,
                                         &command->pageSize,
                                         &command->queues[i].physical,
                                         &command->queues[i].logical));
        gcmkONERROR(
            gckOS_CreateSignal(os, gcvFALSE, &command->queues[i].signal));

		gcmkONERROR(
			gckOS_Signal(os, command->queues[i].signal, gcvTRUE));
	}

	/* No command queue in use yet. */
	command->index    = -1;
	command->logical  = gcvNULL;
	command->newQueue = gcvFALSE;
    command->submit   = gcvFALSE;

	/* Command is not yet running. */
	command->running = gcvFALSE;

	/* Command queue is idle. */
	command->idle = gcvTRUE;

	/* Commit stamp is zero. */
	command->commitStamp = 0;

    /* Return pointer to the gckCOMMAND object. */
	*Command = command;

	/* Success. */
	gcmkFOOTER_ARG("*Command=0x%x", *Command);
	return gcvSTATUS_OK;

OnError:
	/* Roll back. */
	if (command != gcvNULL)
	{
		if (command->mutexContext != gcvNULL)
		{
			gcmkVERIFY_OK(gckOS_DeleteMutex(os, command->mutexContext));
		}

		if (command->mutexQueue != gcvNULL)
		{
			gcmkVERIFY_OK(gckOS_DeleteMutex(os, command->mutexQueue));
		}

		for (i = 0; i < gcdCOMMAND_QUEUES; ++i)
		{
			if (command->queues[i].signal != gcvNULL)
			{
				gcmkVERIFY_OK(
					gckOS_DestroySignal(os, command->queues[i].signal));
			}

			if (command->queues[i].logical != gcvNULL)
			{
				gcmkVERIFY_OK(
					gckOS_FreeNonPagedMemory(os,
											 command->pageSize,
											 command->queues[i].physical,
											 command->queues[i].logical));
			}
		}

		gcmkVERIFY_OK(gckOS_Free(os, command));
	}

	/* Return the status. */
	gcmkFOOTER();
	return status;
}