/*!
******************************************************************************

 @Function				SYSDEVKM_RemoveDevKmLisr

******************************************************************************/
IMG_VOID SYSDEVKM_RemoveDevKmLisr(
	IMG_HANDLE				hSysDevHandle
)
{
	SYSDEVKM_sDevice *	psDevice = (SYSDEVKM_sDevice *)hSysDevHandle;
	
	IMG_ASSERT(gSysDevInitialised);
	IMG_ASSERT(hSysDevHandle != IMG_NULL);
	if (hSysDevHandle == IMG_NULL)
	{
		return;
	}

	SYSDEVU_RegisterDevKmLisr(psDevice->ui32DeviceId, IMG_NULL, IMG_NULL);
}
/*!
******************************************************************************

 @Function				SYSDEVKM_RegisterDevKmLisr

******************************************************************************/
IMG_VOID SYSDEVKM_RegisterDevKmLisr(
	IMG_HANDLE				hSysDevHandle,
	SYSDEVKM_pfnDevKmLisr	pfnDevKmLisr,
    IMG_VOID *              pvParam
)
{
	SYSDEVKM_sDevice *	psDevice = (SYSDEVKM_sDevice *)hSysDevHandle;
	
	IMG_ASSERT(gSysDevInitialised);
	IMG_ASSERT(hSysDevHandle != IMG_NULL);
	if (hSysDevHandle == IMG_NULL)
	{
		return;
	}

	SYSDEVU_RegisterDevKmLisr(psDevice->ui32DeviceId, pfnDevKmLisr, pvParam);
}
/*!
 ******************************************************************************

 @Function				DMANKM_OpenDevice

 ******************************************************************************/
IMG_RESULT DMANKM_OpenDevice(IMG_HANDLE hDevHandle, DMAN_eOpenMode eOpenMode,
		IMG_HANDLE * phConnHandle, IMG_UINT32 * pui32ConnId) 
{
	DMANKM_sDevContext * psDevContext = (DMANKM_sDevContext *) hDevHandle;
	DMANKM_sConnContext * psConnContext;
	DMANKM_sConnContext * psInitConnContext = IMG_NULL;
	IMG_UINT32 ui32Result;
	IMG_HANDLE hProcessId;

	/* Check mode. */
	if ((eOpenMode != DMAN_OMODE_EXCLUSIVE)
			&& (eOpenMode != DMAN_OMODE_SHARED)) {
		IMG_ASSERT(IMG_FALSE);
		return IMG_ERROR_INVALID_PARAMETERS;
	}

	/* Loop over the device connections to see if this process already has a connection...*/
	hProcessId = SYSOSKM_GetProcessId();
	psConnContext = (DMANKM_sConnContext *) LST_first(&psDevContext->sConnList);
	while (psConnContext != IMG_NULL ) {
		/* If process already has a connection. */
		if (psConnContext->hProcessId == hProcessId) {
			/* Update the open count...*/
			psConnContext->ui32OpenCnt++;

			/* Return the connection handle and/or id...*/
			if (phConnHandle != IMG_NULL ) {
				*phConnHandle = psConnContext;
			}
			if (pui32ConnId != IMG_NULL ) {
				*pui32ConnId = psConnContext->ui32ConnId;
			}

			/* Return success...*/
			return IMG_SUCCESS;
		}

		/* Look at next connection. */
		psConnContext = (DMANKM_sConnContext *) LST_next(psConnContext);
	}

	/* See if we have a connection exclusive access required or only exclusive access available. */
	psConnContext = (DMANKM_sConnContext *) LST_first(&psDevContext->sConnList);
	if ((psConnContext != IMG_NULL )&&
	( (eOpenMode == DMAN_OMODE_EXCLUSIVE) ||
			(psConnContext->psDevContext->sDevRegister.ui32ConnFlags == DMAN_CFLAG_EXCLUSIVE) )
	){
	IMG_ASSERT(IMG_FALSE);
	return IMG_ERROR_DEVICE_UNAVAILABLE;
}

	/* Allocate connection context...*/
	psConnContext = IMG_MALLOC(sizeof(*psConnContext));
	if (psConnContext == IMG_NULL ) 
    {
        IMG_ASSERT(psConnContext != IMG_NULL);
		return IMG_ERROR_OUT_OF_MEMORY;
	}
	IMG_MEMSET(psConnContext, 0, sizeof(*psConnContext));

	/* Initialise list of resource allocator...*/
	LST_init(&psConnContext->sAttachList);

	/* Setup connection context...*/
	psConnContext->psDevContext = psDevContext;
	psConnContext->ui32OpenCnt = 1;
	psConnContext->hProcessId = hProcessId;

	/* Update the count of connections...*/
	psDevContext->ui32ConnCnt++;

	/* If this is the first connection...*/
	if (psDevContext->ui32ConnCnt == 1) {
		/* Create resource bucket for connections and attachments...*/
		RMAN_Initialise();
		ui32Result = RMAN_CreateBucket(&psDevContext->hResBHandle);
		IMG_ASSERT(ui32Result == IMG_SUCCESS);
		if (ui32Result != IMG_SUCCESS) {
			goto error_create_bucket;
		}
	}

	/* Add to list of connections...*/
	LST_add(&psDevContext->sConnList, psConnContext);
	ui32Result = RMAN_RegisterResource(psDevContext->hResBHandle,
			DMAN_CONN_TYPE_ID, IMG_NULL, psConnContext,
			&psConnContext->hResHandle, &psConnContext->ui32ConnId);
	IMG_ASSERT(ui32Result == IMG_SUCCESS);
	if (ui32Result != IMG_SUCCESS) {
		goto error_register_resource;
	}

	/* Register with the Process Manager in case the process dies...*/
	ui32Result = PMAN_Initialise();
	IMG_ASSERT(ui32Result == IMG_SUCCESS);
	if (ui32Result != IMG_SUCCESS) {
		goto error_pman_init;
	}
	PMAN_RegisterProcessLostCb(dmankm_fnProcessLostCb, psConnContext,
			&psConnContext->hProcLostCbHandle);
	IMG_ASSERT(ui32Result == IMG_SUCCESS);
	if (ui32Result != IMG_SUCCESS) {
		goto error_pman_register_cb;
	}

	/* If this the first connection and initialise function...*/
	if ((psDevContext->ui32ConnCnt == 1)
			&& (psDevContext->sDevRegister.pfnDevInit != IMG_NULL )) {
		/* Allocate implicit connection context...*/
		psInitConnContext = IMG_MALLOC(sizeof(*psInitConnContext));
		if (psInitConnContext == IMG_NULL ) 
        {
            IMG_ASSERT(psInitConnContext != IMG_NULL);
			ui32Result = IMG_ERROR_OUT_OF_MEMORY;
			goto error_init_conn_ctx;
		}
		IMG_MEMSET(psInitConnContext, 0, sizeof(*psInitConnContext));

		/* Associated this connection with the device and process...*/
		psInitConnContext->psDevContext = psDevContext;
		psInitConnContext->ui32OpenCnt = 1;
		psInitConnContext->hProcessId = hProcessId;

		/* Mark this as the init connection...*/
		psInitConnContext->bInitConn = IMG_TRUE;

		/* Add implicit to list of connections...*/
		LST_add(&psDevContext->sConnList, psInitConnContext);
		ui32Result = RMAN_RegisterResource(psDevContext->hResBHandle,
				DMAN_CONN_TYPE_ID, IMG_NULL, psInitConnContext,
				&psInitConnContext->hResHandle, &psInitConnContext->ui32ConnId);
		IMG_ASSERT(ui32Result == IMG_SUCCESS);
		if (ui32Result != IMG_SUCCESS) {
			goto error_register_resource_init_ctx;
		}

		IMG_ASSERT(
				(psDevContext->sDevRegister.ui32ConnFlags == DMAN_CFLAG_EXCLUSIVE) || (psDevContext->sDevRegister.ui32ConnFlags == DMAN_CFLAG_SHARED));

		/* If it's not a pseudo device...  */
		if ((psDevContext->sDevRegister.ui32DevFlags & DMAN_DFLAG_PSEUDO_DEVICE)
				== 0) {
			/* Open the device...*/
			ui32Result = SYSDEVU_OpenDevice(psDevContext->pszDeviceName,
					&psDevContext->hSysDevHandle);
			IMG_ASSERT(ui32Result == IMG_SUCCESS);
			if (ui32Result != IMG_SUCCESS) {
				goto error_open_device;
			}

			/* Power the device on.  */
			SYSDEVU_SetPowerState(psDevContext->hSysDevHandle,
					SYSOSKM_POWERSTATE_S0, IMG_FALSE);
		}

		ui32Result = psDevContext->sDevRegister.pfnDevInit(psDevContext,
				psInitConnContext, &psDevContext->pvDevInstanceData);
		if (ui32Result != IMG_SUCCESS) {
			REPORT(REPORT_MODULE_DMAN, REPORT_ERR, "dev init failed (%d)",
				ui32Result);
			goto error_dev_init;
		}

		/* If there is a Device Kernel mode HISR...*/
		if (psDevContext->sDevRegister.pfnDevKmHisr != IMG_NULL ) {
			IMG_ASSERT(psDevContext->sDevRegister.pfnDevKmLisr != IMG_NULL);
			ui32Result = SYSOSKM_CreateKmHisr(&dmankm_fnDevKmHisr, psDevContext,
					&psDevContext->hHISRHandle);
			IMG_ASSERT(ui32Result == IMG_SUCCESS);
			if (ui32Result != IMG_SUCCESS) {
				goto error_create_km_hisr;
			}
		}

		/* If there is a Device Kernel mode LISR...*/
		if (psDevContext->sDevRegister.pfnDevKmLisr != IMG_NULL ) {
			/* Register the LISR wrapper...*/
			SYSDEVU_RegisterDevKmLisr(psDevContext->hSysDevHandle,
					&dmankm_fnDevKmLisr, psDevContext);
		}
	}

	/* If connect/open function...*/
	if (psDevContext->sDevRegister.pfnDevConnect != IMG_NULL ) {
		ui32Result = psDevContext->sDevRegister.pfnDevConnect(psConnContext,
				psDevContext->pvDevInstanceData,
				&psConnContext->pvDevConnectionData);
		IMG_ASSERT(
				ui32Result == IMG_SUCCESS || ui32Result == IMG_ERROR_INTERRUPTED);
		if (ui32Result != IMG_SUCCESS && ui32Result != IMG_ERROR_INTERRUPTED) {
			goto error_dev_connect;
		}
	}

	/* Return the connection handle and/or id...*/
	if (phConnHandle != IMG_NULL ) {
		*phConnHandle = psConnContext;
	}
	if (pui32ConnId != IMG_NULL ) {
		*pui32ConnId = psConnContext->ui32ConnId;
	}

	/* Return success...*/
	return ui32Result;

	/* Error handling. */
error_dev_connect:
	/* If this not the first connection or there's no initialise function...*/
	if ((1 != psDevContext->ui32ConnCnt)
			|| (IMG_NULL == psDevContext->sDevRegister.pfnDevInit)) {
		/* ...skip de-initialisation of this part. */
		goto error_init_conn_ctx;
	}

	if (IMG_NULL != psDevContext->sDevRegister.pfnDevKmHisr) {
		SYSOSKM_DestroyKmHisr(psDevContext->hHISRHandle);
	}

error_create_km_hisr:
	if (IMG_NULL
			!= psDevContext->sDevRegister.pfnDevDeinit) {
		psDevContext->sDevRegister.pfnDevDeinit(psDevContext, psInitConnContext,
				psDevContext->pvDevInstanceData);
	}

error_dev_init:
	if ((psDevContext->sDevRegister.ui32DevFlags
			& DMAN_DFLAG_PSEUDO_DEVICE) == 0) {
		SYSDEVU_CloseDevice(psDevContext->hSysDevHandle);
	}

error_open_device:
	RMAN_FreeResource(psInitConnContext->hResHandle);

error_register_resource_init_ctx:
	LST_remove(&psDevContext->sConnList, psInitConnContext);
	IMG_FREE(psInitConnContext);

error_init_conn_ctx:
	PMAN_RemoveProcessLostCb(psConnContext->hProcLostCbHandle);
	/* release per-process resources in PMAN, allocated inside
	   PMAN_RegisterProcessLostCb. We have to use device disconnect,
	   although the device wasn't actually initialised, because no
	   other function is exposed by PMAN */
	PMAN_DevDisconnectComplete(hProcessId);

error_pman_register_cb:
error_pman_init:
	RMAN_FreeResource(psConnContext->hResHandle);

error_register_resource:
	LST_remove(&psDevContext->sConnList,psConnContext);
	if (1 == psDevContext->ui32ConnCnt) {
		RMAN_DestroyBucket(psDevContext->hResBHandle);
	}

error_create_bucket:
	psDevContext->ui32ConnCnt--;
	IMG_FREE(psConnContext);

	return ui32Result;
}