/*
 * dspbridge/src/api/linux/DSPStrm.c
 *
 * DSP-BIOS Bridge driver support functions for TI OMAP processors.
 *
 * Copyright (C) 2007 Texas Instruments, Inc.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 of the License.
 *
 * This program is distributed .as is. WITHOUT ANY WARRANTY of any kind,
 * whether express or implied; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 */

/*
 *  ======== DSPStrm.c ========
 *  Description:
 *      This is the source for the DSP/BIOS Bridge API stream module. The
 *      parameters are validated at the API level, but the bulk of the
 *      work is done at the driver level through the PM STRM module.
 *
 *  Public Functions:
 *      DSPStream_AllocateBuffers
 *      DSPStream_Close
 *      DSPStream_FreeBuffers
 *      DSPStream_GetInfo
 *      DSPStream_Idle
 *      DSPStream_Issue
 *      DSPStream_Open
 *      DSPStream_Reclaim
 *      DSPStream_RegisterNotify
 *      DSPStream_Select
 *
 *! Revision History
 *! ================
 *! 13-Mar-2002 map Checking for invalid direction in DSPStream_Open()
 *! 12-Mar-2002 map Checking for invalid node handle in
 *!                 DSPStream_Open().
 *! 11-Mar-2002 map Checking that bufsize is not smaller than specified
 *!                  number of bytes in buffer in DSPStream_Issue().
 *! 06-Jan-2002 ag  STRMMODE_ZEROCOPY(SM buffer swap) enabled.
 *! 17-Dec-2001 ag  STRMMODE_RDMA(DDMA) enabled.
 *! 04-Dec-2001 ag  Changed user event name string in DSPStream_Open().
 *!                 Added stream direction and index.
 *! 16-Nov-2001 ag  Added SM allocation for streaming.
 *! 07-Jun-2001 sg  Made buffer allocate/free fxn names plural.
 *! 18-May-2001 jeh Close event handle in DSPStream_Open() if failure.
 *! 11-Apr-2001 rr: DSPStream_UnPrepareBuffer checks for pBuffer == NULL
 *!                 (not for *pBuffer).
 *! 13-Dec-2000 jeh Return DSP_EPOINTER, not DSP_EHANDLE in
 *!					DSPStream_Select() for NULL pointers.
 *!					Also set *pMask to 0 if nStreams is 0.
 *! 05-Dec-2000 jeh Return DSP_ESIZE, not DSP_EVALUE in DSPStream_GetInfo,
 *!                 set status to DSP_SOK in DSPStream_UnprepareBuffer().
 *! 10-Nov-2000 rr: DSP_PBUFFER modified to BYTE *. RegisterNotify
 *!                 catches Invalid Events and Masks.
 *! 23-Oct-2000 jeh Free buffers in DSPStream_FreeBuffer().
 *! 28-Sep-2000 jeh Removed DSP_BUFFERATTR param from DSP_StreamAllocateBuffer.
 *! 07-Sep-2000 jeh Changed type HANDLE in DSPStream_RegisterNotify to
 *!                 DSP_HNOTIFICATION.
 *! 04-Aug-2000 rr: Name changed to DSPStrm.c
 *! 27-Jul-2000 rr: Types updated to ver 0.8 API.
 *! 18-Jul-2000 rr: STRM API calls into the Class driver.
 *!                 Only parameters are validated here.
 *! 15-May-2000 gp: Return DSP_EHANDLE fromo DSPStream_Close().
 *! 19-Apr-2000 ww: Updated based on code review.
 *! 12-Apr-2000 ww: Created based on DirectDSP API specification, Version 0.6.
 *
 */

/*  ----------------------------------- Host OS */
#include <host_os.h>

/*  ----------------------------------- DSP/BIOS Bridge */
#include <std.h>
#include <dbdefs.h>
#include <errbase.h>

/*  ----------------------------------- OS Adaptation Layer */
#include <csl.h>

/*  ----------------------------------- Others */
#include <dsptrap.h>
#include <memry.h>

/*  ----------------------------------- This */
#include "_dbdebug.h"

#include <DSPStream.h>

/*  ----------------------------------- Defines, Data Structures, Typedefs */
#define STRM_MAXLOCKPAGES       64

/*  ----------------------------------- Globals */
extern int hMediaFile;		/* class driver handle */

/*  ----------------------------------- Function Prototypes */
static DSP_STATUS GetStrmInfo(DSP_HSTREAM hStream, struct STRM_INFO *pStrmInfo,
			      UINT uStreamInfoSize);

/*
 *  ======== DSPStream_AllocateBuffers ========
 *  Purpose:
 *      Allocate data buffers for use with a specific stream.
 */
DBAPI DSPStream_AllocateBuffers(DSP_HSTREAM hStream, UINT uSize,
			  OUT BYTE **apBuffer, UINT uNumBufs)
{
	UINT i;
	UINT uAllocated = 0;
	DSP_STATUS status = DSP_SOK;
	Trapped_Args tempStruct;
	PVOID pBuf = NULL;
	struct STRM_INFO strmInfo;
	struct DSP_STREAMINFO userInfo;

	DEBUGMSG(DSPAPI_ZONE_FUNCTION,
			(TEXT("NODE: DSPStream_AllocateBuffers:\r\n")));
	if (!hStream) {
		/* Invalid pointer */
		status = DSP_EHANDLE;
		DEBUGMSG(DSPAPI_ZONE_ERROR,
				(TEXT("NODE: DSPStream_AllocateBuffers: "
						"hStrm is Invalid \r\n")));
		return status;
	}
	if (!apBuffer) {
		/* Invalid parameter */
		status = DSP_EPOINTER;
		DEBUGMSG(DSPAPI_ZONE_ERROR,
			(TEXT("NODE: DSPStream_AllocateBuffers: "
				"Invalid pointer in the Input\r\n")));
		return status;
	}
	for (i = 0; i < uNumBufs; i++)
		apBuffer[i] = NULL;

	strmInfo.pUser = &userInfo;
	status = GetStrmInfo(hStream, &strmInfo, sizeof(struct DSP_STREAMINFO));
	if (!DSP_SUCCEEDED(status)) {
		status = DSP_EFAIL;
		DEBUGMSG(DSPAPI_ZONE_ERROR,
			(TEXT("DSPStream_AllocateBuffers: "
				"DSP_FAILED to get strm info\r\n")));
		return status;
	}
	if (strmInfo.uSegment > 0) {
		/* Alloc SM */
		tempStruct.ARGS_STRM_ALLOCATEBUFFER.hStream = hStream;
		tempStruct.ARGS_STRM_ALLOCATEBUFFER.uSize = uSize;
		tempStruct.ARGS_STRM_ALLOCATEBUFFER.apBuffer = apBuffer;
		tempStruct.ARGS_STRM_ALLOCATEBUFFER.uNumBufs = uNumBufs;
		/* Call DSP Trap */
		status = DSPTRAP_Trap(&tempStruct,
				CMD_STRM_ALLOCATEBUFFER_OFFSET);
	} else {
		/* Allocate local buffers */
		for (i = 0; i < uNumBufs; i++) {
			pBuf = MEM_Alloc(uSize, MEM_NONPAGED);
			if (!pBuf) {
				status = DSP_EMEMORY;
				uAllocated = i;
				break;
			} else
				apBuffer[i] = pBuf;

		}
		if (DSP_FAILED(status)) {
			/* Free buffers allocated so far */
			for (i = 0; i < uAllocated; i++) {
				MEM_Free(apBuffer[i]);
				apBuffer[i] = NULL;
			}
		}
	}
	return status;
}

/*
 *  ======== DSPStream_Close ========
 *  Purpose:
 *      Close a stream and free the underlying stream object.
 */
DBAPI DSPStream_Close(DSP_HSTREAM hStream)
{
#ifndef LINUX
	HANDLE hEvent;
#endif
	DSP_STATUS status = DSP_SOK;
	Trapped_Args tempStruct;
	struct STRM_INFO strmInfo;
	struct DSP_STREAMINFO userInfo;
	struct CMM_OBJECT *hCmm = NULL;	/* SM Mgr handle */
	struct CMM_INFO pInfo;	/* CMM info; use for virtual space allocation */

	DEBUGMSG(DSPAPI_ZONE_FUNCTION, (TEXT("NODE: DSPStream_Close:\r\n")));

	if (!hStream) {
		/* Invalid pointer */
		status = DSP_EHANDLE;
		DEBUGMSG(DSPAPI_ZONE_ERROR,
			(TEXT("NODE: DSPStream_Close: hStrm is Invalid \r\n")));
		return status;
	}
	/* Unmap stream's process virtual space, if any */
	strmInfo.pUser = &userInfo;
	status = GetStrmInfo(hStream, &strmInfo, sizeof(struct DSP_STREAMINFO));
	if (!DSP_SUCCEEDED(status)) {
		status = DSP_EFAIL;
		DEBUGMSG(DSPAPI_ZONE_ERROR, (TEXT("NODE: DSPStream_Close: "
					"ERROR in Getting Strm Info \r\n")));
		return status;
	}
	if (strmInfo.pVirtBase != NULL) {
		/* Get segment size.
		 >0 is SM segment. Get default SM Mgr */
		tempStruct.ARGS_CMM_GETHANDLE.hProcessor = NULL;
		tempStruct.ARGS_CMM_GETHANDLE.phCmmMgr = &hCmm;
		status = DSPTRAP_Trap(&tempStruct, CMD_CMM_GETHANDLE_OFFSET);
		if (DSP_SUCCEEDED(status)) {
			/* Get SM segment info from CMM */
			tempStruct.ARGS_CMM_GETINFO.hCmmMgr = hCmm;
			tempStruct.ARGS_CMM_GETINFO.pCmmInfo = &pInfo;
			status = DSPTRAP_Trap(&tempStruct,
					CMD_CMM_GETINFO_OFFSET);
		}
		/* strmInfo.uSegment is probably already OK here,
		   so following checks may not be required */
		if (DSP_SUCCEEDED(status) &&
			(pInfo.ulNumGPPSMSegs >= strmInfo.uSegment)) {
			/* segInfo index starts at 0 */
			if ((pInfo.segInfo[strmInfo.uSegment-1].dwSegBasePa
				!= 0) && (pInfo.segInfo[strmInfo.uSegment-1]\
					.ulTotalSegSize) > 0) {
				if (munmap(strmInfo.pVirtBase,
					pInfo.segInfo[strmInfo.uSegment-1]\
						.ulTotalSegSize)) {
					status = DSP_EFAIL;
				}
			}
		} else
			status = DSP_EBADSEGID;	/*no SM segments */

	}
#ifndef LINUX			/* Events are handled in kernel */
	if (DSP_SUCCEEDED(status)) {
		/* Get the user event from the stream */
		/* Set up the structure */
		tempStruct.ARGS_STRM_GETEVENTHANDLE.hStream = hStream;
		tempStruct.ARGS_STRM_GETEVENTHANDLE.phEvent = &hEvent;
		status = DSPTRAP_Trap(&tempStruct,
				CMD_STRM_GETEVENTHANDLE_OFFSET);
	}
#endif
	if (DSP_SUCCEEDED(status)) {
		/* Now close the stream */
		tempStruct.ARGS_STRM_CLOSE.hStream = hStream;
		status = DSPTRAP_Trap(&tempStruct, CMD_STRM_CLOSE_OFFSET);
	}
#ifndef LINUX			/* Events are handled in kernel */
	if (DSP_SUCCEEDED(status))
		CloseHandle(hEvent);

#endif
	return status;
}

/*
 *  ======== DSPStream_FreeBuffers ========
 *  Purpose:
 *      Free a previously allocated stream data buffer.
 */
DBAPI DSPStream_FreeBuffers(DSP_HSTREAM hStream, IN BYTE **apBuffer,
		UINT uNumBufs)
{
	UINT i;
	DSP_STATUS status = DSP_SOK;
	Trapped_Args tempStruct;
	struct STRM_INFO strmInfo;
	struct DSP_STREAMINFO userInfo;

	DEBUGMSG(DSPAPI_ZONE_FUNCTION,
			(TEXT("NODE:DSPStream_FreeBuffers:\r\n")));

	if (!hStream) {
		/* Invalid pointer */
		status = DSP_EHANDLE;
		DEBUGMSG(DSPAPI_ZONE_ERROR,
			(TEXT("NODE: DSPStream_FreeBuffers: "
						"hStrm is Invalid \r\n")));
		goto func_end;
	}
	if (!apBuffer) {
		/* Invalid parameter */
		status = DSP_EPOINTER;
		DEBUGMSG(DSPAPI_ZONE_ERROR,
				(TEXT("NODE: DSPStream_FreeBuffers: "
					"Invalid pointer in the Input\r\n")));
		goto func_end;
	}
	strmInfo.pUser = &userInfo;	/* need valid user info ptr */
	status = GetStrmInfo(hStream, &strmInfo, sizeof(struct DSP_STREAMINFO));
	if (!DSP_SUCCEEDED(status)) {
		DEBUGMSG(DSPAPI_ZONE_ERROR,
				(TEXT("DSPStream_FreeBuffers. "
						"Free Failed. Bad mode.")));
		status = DSP_EFAIL;
		goto func_end;
	}
	if (strmInfo.uSegment > 0) {
		/* Free SM allocations */
		tempStruct.ARGS_STRM_FREEBUFFER.hStream = hStream;
		tempStruct.ARGS_STRM_FREEBUFFER.apBuffer = apBuffer;
		tempStruct.ARGS_STRM_FREEBUFFER.uNumBufs = uNumBufs;
		/* Call DSP Trap */
		status = DSPTRAP_Trap(&tempStruct, CMD_STRM_FREEBUFFER_OFFSET);
		if (DSP_FAILED(status)) {
			DEBUGMSG(DSPAPI_ZONE_ERROR,
				(TEXT("DSPStream_FreeBuffers: "
						 "Failed to Free Buf")));
			status = DSP_EFAIL;
		}
	} else {
		for (i = 0; i < uNumBufs; i++) {
			/* Free local allocation */
			if (apBuffer[i]) {
				MEM_Free((PVOID)apBuffer[i]);
				apBuffer[i] = NULL;
			}
		}	/* end for */
	}
func_end:
	/* Return DSP_SOK if OS calls returned 0 */
	if (status == 0)
		status = DSP_SOK;

	return status;
}

/*
 *  ======== DSPStream_GetInfo ========
 *  Purpose:
 *      Get information about a stream.
 */
DBAPI DSPStream_GetInfo(DSP_HSTREAM hStream,
		  OUT struct DSP_STREAMINFO *pStreamInfo, UINT uStreamInfoSize)
{
	DSP_STATUS status = DSP_SOK;
	struct STRM_INFO strmInfo;/* include stream's private virt addr info */

	DEBUGMSG(DSPAPI_ZONE_FUNCTION, (TEXT("NODE: DSPStream_GetInfo:\r\n")));

	strmInfo.pUser = pStreamInfo;
	status = GetStrmInfo(hStream, &strmInfo, uStreamInfoSize);
	/* Return DSP_SOK if OS calls returned 0 */
	if (status == 0)
		status = DSP_SOK;

	return status;
}

/*
 *  ======== DSPStream_Idle ========
 *  Purpose:
 *      Terminate I/O with a particular stream, and (optionally)
 *      flush output data buffers.
 */
DBAPI DSPStream_Idle(DSP_HSTREAM hStream, bool bFlush)
{
	DSP_STATUS status = DSP_SOK;
	Trapped_Args tempStruct;

	DEBUGMSG(DSPAPI_ZONE_FUNCTION, (TEXT("NODE: DSPStream_Idle:\r\n")));

	if (hStream) {
		/* Set up the structure */
		/* Call DSP Trap */
		tempStruct.ARGS_STRM_IDLE.hStream = hStream;
		tempStruct.ARGS_STRM_IDLE.bFlush = bFlush;
		status = DSPTRAP_Trap(&tempStruct, CMD_STRM_IDLE_OFFSET);
	} else {
		/* Invalid pointer */
		status = DSP_EHANDLE;
		DEBUGMSG(DSPAPI_ZONE_ERROR, (TEXT("NODE: DSPStream_Idle: "
						"hStrm is Invalid \r\n")));
	}
	return status;
}

/*
 *  ======== DSPStream_Issue ========
 *  Purpose:
 *      Send a buffer of data to a stream.
 */
DBAPI DSPStream_Issue(DSP_HSTREAM hStream, IN BYTE *pBuffer,
		ULONG dwDataSize, ULONG dwBufSize, IN DWORD dwArg)
{
	DSP_STATUS status = DSP_SOK;
	Trapped_Args tempStruct;

	DEBUGMSG(DSPAPI_ZONE_FUNCTION, (TEXT("NODE: DSPStream_Issue:\r\n")));

	if (hStream) {
		/* Check the size of the buffer */
		if (pBuffer) {
			/* Check that the size isn't too small */
			if (dwDataSize > dwBufSize) {
				status = DSP_EINVALIDARG;
				DEBUGMSG(DSPAPI_ZONE_ERROR,
					(TEXT("NODE: DSPStream_Issue: "
					"Invalid argument in the Input\r\n")));
			} else {
				/* Set up the structure */
				tempStruct.ARGS_STRM_ISSUE.hStream = hStream;
				tempStruct.ARGS_STRM_ISSUE.pBuffer = pBuffer;
				tempStruct.ARGS_STRM_ISSUE.dwBytes = dwDataSize;
				tempStruct.ARGS_STRM_ISSUE.dwBufSize =
							dwBufSize;
				tempStruct.ARGS_STRM_ISSUE.dwArg = dwArg;
				/* Call DSP Trap */
				status = DSPTRAP_Trap(&tempStruct,
					CMD_STRM_ISSUE_OFFSET);
				/* Return DSP_SOK if OS calls returned 0 */
				if (status == 0)
					status = DSP_SOK;

			}
		} else {
			/* Invalid parameter */
			status = DSP_EPOINTER;
			DEBUGMSG(DSPAPI_ZONE_ERROR,
				(TEXT("NODE: DSPStream_Issue: "
					"Invalid pointer in the Input\r\n")));
		}
	} else {
		/* Invalid pointer */
		status = DSP_EHANDLE;
		DEBUGMSG(DSPAPI_ZONE_ERROR, (TEXT("NODE: DSPStream_Issue: "
						"hStrm is Invalid \r\n")));
	}

	return status;
}

/*
 *  ======== DSPStream_Open ========
 *  Purpose:
 *      Retrieve a stream handle for sending/receiving data buffers
 *      to/from a task node on a DSP.
 */
DBAPI DSPStream_Open(DSP_HNODE hNode, UINT uDirection, UINT uIndex,
	       IN OPTIONAL struct DSP_STREAMATTRIN *pAttrIn,
	       OUT DSP_HSTREAM *phStream)
{
	DSP_STATUS status = DSP_SOK;
	Trapped_Args tempStruct;
	struct STRM_ATTR strmAttrs;
#ifndef LINUX			/* Events are handled in kernel */
	CHAR szEventName[STRM_MAXEVTNAMELEN];
	WCHAR wszEventName[STRM_MAXEVTNAMELEN];
	CHAR szTemp[STRM_MAXEVTNAMELEN];
#endif
	struct CMM_OBJECT *hCmm = NULL;	/* SM Mgr handle */
	struct CMM_INFO pInfo;/* CMM info; use for virtual space allocation */

	DEBUGMSG(DSPAPI_ZONE_FUNCTION, (TEXT("NODE: DSPStream_Open:\r\n")));

	if (!hNode) {
		status = DSP_EHANDLE;
		DEBUGMSG(DSPAPI_ZONE_ERROR, (TEXT("NODE: DSPStream_Open: "
					"Invalid handle in the Input\r\n")));
		return status;
	}
	if (uDirection != DSP_TONODE && uDirection != DSP_FROMNODE) {
		status = DSP_EDIRECTION;
		DEBUGMSG(DSPAPI_ZONE_ERROR, (TEXT("NODE: DSPStream_Open: "
					"Invalid direction in the Input\r\n")));
		return status;
	}
	if (!phStream) {
		status = DSP_EPOINTER;
		DEBUGMSG(DSPAPI_ZONE_ERROR,
			(TEXT("NODE: DSPStream_Open: "
				"Invalid pointer in the Input\r\n")));
		return status;
	}
	*phStream = NULL;
	strmAttrs.hUserEvent = NULL;
#ifndef LINUX			/* Events are handled in kernel */
			 /* Create a 'named' user event that is unique.*/
	strmAttrs.pStreamAttrIn = pAttrIn;
	szEventName[0] = 'E';
	szEventName[1] = 'V';
	szEventName[2] = '\0';
	/* append hNode handle string */
	strncat(szEventName, _ultoa((ULONG)hNode, szTemp, 16), 8);
	/* now append stream index and direction */
	strncat(szEventName, _ultoa((ULONG)uDirection, szTemp, 16), 2);
	strmAttrs.pstrEventName =
		strncat(szEventName, _ultoa((ULONG)uIndex, szTemp, 16), 3);
	(Void)CSL_AnsiToWchar(wszEventName, szEventName, STRM_MAXEVTNAMELEN);
	/* Create an auto reset event. */
	strmAttrs.hUserEvent = CreateEvent(NULL,false,false,wszEventName);
	if (!strmAttrs.hUserEvent) {
		status = DSP_EFAIL;
		DEBUGMSG(DSPAPI_ZONE_ERROR, (TEXT("NODE: DSPStream_Open: "
					"Failed to Create the Event \r\n")));
	}
#endif
	 /*  Default stream mode is PROCCOPY.
	 *  Check for currently supported mode(s).*/
	if (pAttrIn) {
		if (pAttrIn->lMode == STRMMODE_LDMA) {
			/* No System-DMA support */
			status = DSP_ENOTIMPL;
		} else
		    if ((pAttrIn->lMode != STRMMODE_PROCCOPY)
				&& (pAttrIn->lMode != STRMMODE_ZEROCOPY)
				&& (pAttrIn->lMode != STRMMODE_RDMA)) {
			status = DSP_ESTRMMODE;	/* illegal stream mode */
		}
		pAttrIn->uSegment = abs(pAttrIn->uSegment);
		/* make non-neg */
	}
	 /*  If opening the stream for STRMMODE_ZEROCOPY or
	 *  STRMMODE_RDMA(DSP-DMA) stream mode, then setup the
	 *  stream's  CMM translator for the specified SM segment.*/
	strmAttrs.pVirtBase = NULL;
	strmAttrs.ulVirtSize = 0;
	if (DSP_SUCCEEDED(status) && pAttrIn) {
		if ((pAttrIn->lMode == STRMMODE_ZEROCOPY) ||
				(pAttrIn->lMode == STRMMODE_RDMA)) {
			if (pAttrIn->uSegment == 0) {
				status = DSP_ENOTSHAREDMEM;	/* must be
								SM segment */
				goto loop_end;
			}
			/* >0 is SM segment. Get default SM Mgr */
			tempStruct.ARGS_CMM_GETHANDLE.hProcessor = NULL;
			tempStruct.ARGS_CMM_GETHANDLE.phCmmMgr = &hCmm;
			status = DSPTRAP_Trap(&tempStruct,
					CMD_CMM_GETHANDLE_OFFSET);
			if (status == DSP_SOK) {
				/* Get SM segment info from CMM */
				tempStruct.ARGS_CMM_GETINFO.hCmmMgr = hCmm;
				tempStruct.ARGS_CMM_GETINFO.pCmmInfo = &pInfo;
				status = DSPTRAP_Trap(&tempStruct,
						CMD_CMM_GETINFO_OFFSET);
				if (status != DSP_SOK)
					status = DSP_EFAIL;
			} else
				status = DSP_EFAIL;

			if (!DSP_SUCCEEDED(status ||
				!(pInfo.ulNumGPPSMSegs >= pAttrIn->uSegment))) {
				status = DSP_EBADSEGID; /* no SM segments */
				goto loop_end;
			}
			/* segInfo index starts at 0 */
			if ((pInfo.segInfo[pAttrIn->uSegment-1].dwSegBasePa
				== 0) || (pInfo.segInfo[pAttrIn->uSegment-1]\
					.ulTotalSegSize) < 0) {
				status = DSP_EFAIL;
				DEBUGMSG(DSPAPI_ZONE_ERROR,
						(TEXT("STRM:DSPStream_Open: "
						"Bad SM info...why?\r\n")));
				goto loop_end;
			}
			strmAttrs.pVirtBase = mmap(NULL,
				pInfo.segInfo[pAttrIn->uSegment-1]\
				.ulTotalSegSize, PROT_READ | PROT_WRITE,
				MAP_SHARED | MAP_LOCKED, hMediaFile, pInfo\
				.segInfo[pAttrIn->uSegment-1].dwSegBasePa);
			if (strmAttrs.pVirtBase == NULL) {
				status = DSP_EFAIL;
				DEBUGMSG(DSPAPI_ZONE_ERROR,
					(TEXT("STRM: DSPStream_Open: "
						"Virt alloc failed\r\n")));
				goto loop_end;
			}
			strmAttrs.ulVirtSize =
			pInfo.segInfo[pAttrIn->uSegment-1].ulTotalSegSize;
		}
	}
loop_end:
	if (DSP_SUCCEEDED(status)) {
		/* Set up the structure */
		strmAttrs.pStreamAttrIn = pAttrIn;
		/* Call DSP Trap */
		tempStruct.ARGS_STRM_OPEN.hNode = hNode;
		tempStruct.ARGS_STRM_OPEN.uDirection = uDirection;
		tempStruct.ARGS_STRM_OPEN.uIndex = uIndex;
		tempStruct.ARGS_STRM_OPEN.pAttrIn = &strmAttrs;
		tempStruct.ARGS_STRM_OPEN.phStream = phStream;
		status = DSPTRAP_Trap(&tempStruct, CMD_STRM_OPEN_OFFSET);
#ifndef LINUX			/* Events are handled in kernel */
		if (DSP_FAILED(status))
			CloseHandle(strmAttrs.hUserEvent);

#endif
	}
	return status;
}

/*
 *  ======== DSPStream_PrepareBuffer ========
 *  Purpose:
 *      Prepares a buffer.
 */
DBAPI DSPStream_PrepareBuffer(DSP_HSTREAM hStream, UINT uSize, BYTE *pBuffer)
{
	DSP_STATUS status = DSP_SOK;
#ifndef LINUX
	/*  Pages are never swapped out (i.e. always locked in Linux) */
	ULONG aPageTab[STRM_MAXLOCKPAGES];
	/* Find the maximum # of pages that could be locked. x86 &
			ARM=4Kb pages */
	UINT cPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(NULL, uSize);
#endif
	/* Do error checking here to API spec. We don't call down to WCD */
	if (!hStream)
		status = DSP_EHANDLE;

	if (DSP_SUCCEEDED(status)) {
		if (!pBuffer)
			status = DSP_EPOINTER;
	}

	if (DSP_SUCCEEDED(status)) {
		if (uSize <= 0)
			status = DSP_ESIZE;
	}
#ifndef LINUX
	/*  Pages are never swapped out (i.e. always locked in Linux) */
	if (DSP_SUCCEEDED(status)) {
		if (cPages > STRM_MAXLOCKPAGES)
			status = DSP_EFAIL;
		else {
			if (!LockPages((LPVOID)pBuffer, uSize, aPageTab,
					LOCKFLAG_WRITE))
				status = DSP_EFAIL;
		}
	}
#endif

	return status;
}

/*
 *  ======== DSPStream_Reclaim ========
 *  Purpose:
 *      Request a buffer back from a stream.
 */
DBAPI DSPStream_Reclaim(DSP_HSTREAM hStream, OUT BYTE **pBufPtr,
		OUT ULONG *pDataSize, OUT ULONG *pBufSize, OUT DWORD *pdwArg)
{
	DSP_STATUS status = DSP_SOK;
	Trapped_Args tempStruct;

	DEBUGMSG(DSPAPI_ZONE_FUNCTION, (TEXT("NODE: DSPStream_Reclaim:\r\n")));

	if (hStream) {
		/* Check the size of the buffer */
		if ((pBufPtr) && (pDataSize) && (pdwArg)) {
			/* Set up the structure */
			/* Call DSP Trap */
			tempStruct.ARGS_STRM_RECLAIM.hStream = hStream;
			tempStruct.ARGS_STRM_RECLAIM.pBufPtr = pBufPtr;
			tempStruct.ARGS_STRM_RECLAIM.pBytes = pDataSize;
			tempStruct.ARGS_STRM_RECLAIM.pBufSize = pBufSize;
			tempStruct.ARGS_STRM_RECLAIM.pdwArg = pdwArg;
			status = DSPTRAP_Trap(&tempStruct,
					CMD_STRM_RECLAIM_OFFSET);
		} else {
			/* Invalid parameter */
			status = DSP_EPOINTER;
			DEBUGMSG(DSPAPI_ZONE_ERROR,
				(TEXT("NODE: DSPStream_Reclaim: "
					"Invalid pointer in the Input\r\n")));
		}
	} else {
		/* Invalid pointer */
		status = DSP_EHANDLE;
		DEBUGMSG(DSPAPI_ZONE_ERROR, (TEXT("NODE: DSPStream_Reclaim: "
						"hStrm is Invalid \r\n")));
	}

	return status;
}

/*
 *  ======== DSPStream_RegisterNotify ========
 *  Purpose:
 *      Register to be notified of specific events for this stream.
 */
DBAPI
DSPStream_RegisterNotify(DSP_HSTREAM hStream, UINT uEventMask,
		 UINT uNotifyType, struct DSP_NOTIFICATION *hNotification)
{
	DSP_STATUS status = DSP_SOK;
	Trapped_Args tempStruct;

	DEBUGMSG(DSPAPI_ZONE_FUNCTION,
			(TEXT("NODE: DSPStream_RegisterNotify:\r\n")));

	if ((hStream) && (hNotification)) {
		if (IsValidStrmEvent(uEventMask)) {
			if (IsValidNotifyMask(uNotifyType)) {
				/* Set up the structure */
				/* Call DSP Trap */
				tempStruct.ARGS_STRM_REGISTERNOTIFY.hStream =
						hStream;
				tempStruct.ARGS_STRM_REGISTERNOTIFY.uEventMask =
						uEventMask;
				tempStruct.ARGS_STRM_REGISTERNOTIFY\
						.uNotifyType = uNotifyType;
				tempStruct.ARGS_STRM_REGISTERNOTIFY\
						.hNotification = hNotification;
				status = DSPTRAP_Trap(&tempStruct,
						CMD_STRM_REGISTERNOTIFY_OFFSET);
			} else {
				status = DSP_ENOTIMPL;
				DEBUGMSG(DSPAPI_ZONE_ERROR,
					(TEXT("NODE: DSPStream_RegisterNotify: "
						"Invalid Notify Mask \r\n")));
			}
		} else {
			status = DSP_EVALUE;
			DEBUGMSG(DSPAPI_ZONE_ERROR,
					(TEXT("NODE: DSPStream_RegisterNotify: "
						"Invalid Event Mask \r\n")));
		}
	} else {
		/* Invalid handle */
		status = DSP_EHANDLE;
		DEBUGMSG(DSPAPI_ZONE_ERROR,
			(TEXT("NODE: DSPStream_RegisterNotify: "
					"Invalid Handle \r\n")));
	}

	return status;
}

/*
 *  ======== DSPStream_Select ========
 *  Purpose:
 *      Select a ready stream.
 */
DBAPI DSPStream_Select(IN DSP_HSTREAM *aStreamTab,
		 UINT nStreams, OUT UINT *pMask, UINT uTimeout)
{
	DSP_STATUS status = DSP_SOK;
	Trapped_Args tempStruct;

	DEBUGMSG(DSPAPI_ZONE_FUNCTION, (TEXT("NODE: DSPStream_Select:\r\n")));

	if ((aStreamTab) && (pMask)) {
		if (nStreams) {
			/* Set up the structure */
			/* Call DSP Trap */
			tempStruct.ARGS_STRM_SELECT.aStreamTab = aStreamTab;
			tempStruct.ARGS_STRM_SELECT.nStreams = nStreams;
			tempStruct.ARGS_STRM_SELECT.pMask = pMask;
			tempStruct.ARGS_STRM_SELECT.uTimeout = uTimeout;
			status = DSPTRAP_Trap(&tempStruct,
					CMD_STRM_SELECT_OFFSET);
		} else
			/* nStreams == 0 */
			*pMask = 0;
	} else {
		/* Invalid pointer */
		status = DSP_EPOINTER;
		DEBUGMSG(DSPAPI_ZONE_ERROR,
		 (TEXT("NODE: DSPStream_Select: hStrm is Invalid \r\n")));
	}

	return status;
}

/*
 *  ======== DSPStream_UnprepareBuffer ========
 *  Purpose:
 *      Unprepares a buffer.
 */
DBAPI DSPStream_UnprepareBuffer(DSP_HSTREAM hStream, UINT uSize,
				BYTE *pBuffer)
{
	DSP_STATUS status = DSP_SOK;

	/* Do error checking here to API spec. We don't call down to WCD */
	if (!hStream)
		status = DSP_EHANDLE;

	if (DSP_SUCCEEDED(status)) {
		if (!pBuffer)
			status = DSP_EPOINTER;
	}

	if (DSP_SUCCEEDED(status)) {
		/*|| ((LPVOID)pBuffer == NULL) - already checked above */
		if ((uSize <= 0))
			status = DSP_EFAIL;
	}
#ifndef LINUX			/*  Pages are never swapped out
						(i.e. always locked in Linux) */
	if (DSP_SUCCEEDED(status)) {
		if (!UnlockPages((LPVOID) pBuffer, uSize))
			status = DSP_EFAIL;
	}
#endif

	return status;
}

/*
 *  ======== GetStrmInfo ========
 */
static DSP_STATUS GetStrmInfo(DSP_HSTREAM hStream, struct STRM_INFO *pStrmInfo,
							UINT uStreamInfoSize)
{
	DSP_STATUS status = DSP_SOK;
	Trapped_Args tempStruct;

	if (hStream) {
		/* Check the size of the buffer */
		if (pStrmInfo && pStrmInfo->pUser) {
			if (uStreamInfoSize >= sizeof(struct DSP_STREAMINFO)) {
				/* user info */
				/* Set up the structure */
				/* Call DSP Trap */
				tempStruct.ARGS_STRM_GETINFO.hStream = hStream;
				tempStruct.ARGS_STRM_GETINFO.pStreamInfo =
						pStrmInfo;
				/* user returned struct DSP_STREAMINFO
						info size */
				tempStruct.ARGS_STRM_GETINFO.uStreamInfoSize =
						uStreamInfoSize;
				status = DSPTRAP_Trap(&tempStruct,
						CMD_STRM_GETINFO_OFFSET);
			} else {
				status = DSP_ESIZE;
				DEBUGMSG(DSPAPI_ZONE_ERROR,
					 (TEXT("NODE: DSPStream_GetInfo: "
					 "uStreamInfo size is less than the "
					 "size of struct DSP_STREAMINFO\r\n")));
			}
		} else {
			/* Invalid parameter */
			status = DSP_EPOINTER;
			DEBUGMSG(DSPAPI_ZONE_ERROR,
				 (TEXT("NODE: DSPStream_GetInfo: "
					"Invalid pointer\r\n")));
		}
	} else {
		/* Invalid pointer */
		status = DSP_EHANDLE;
		DEBUGMSG(DSPAPI_ZONE_ERROR, (TEXT(
			"NODE: DSPStream_GetInfo: hStrm is Invalid \r\n")));
	}

	return status;
}