/* * chnl_sm.c * * DSP-BIOS Bridge driver support functions for TI OMAP processors. * * Copyright (C) 2005-2006 Texas Instruments, Inc. * * This package is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ /* * ======== chnl_sm.c ======== * Description: * Implements upper edge functions for WMD channel module. * * Public Functions: * WMD_CHNL_AddIOReq * WMD_CHNL_CancelIO * WMD_CHNL_Close * WMD_CHNL_Create * WMD_CHNL_Destroy * WMD_CHNL_FlushIO * WMD_CHNL_GetInfo * WMD_CHNL_GetIOC * WMD_CHNL_GetMgrInfo * WMD_CHNL_Idle * WMD_CHNL_Open * * Notes: * The lower edge functions must be implemented by the WMD writer, and * are declared in chnl_sm.h. * * Care is taken in this code to prevent simulataneous access to channel * queues from * 1. Threads. * 2. IO_DPC(), scheduled from the IO_ISR() as an event. * * This is done primarily by: * - Semaphores. * - state flags in the channel object; and * - ensuring the IO_Dispatch() routine, which is called from both * CHNL_AddIOReq() and the DPC(if implemented), is not re-entered. * * Channel Invariant: * There is an important invariant condition which must be maintained per * channel outside of WMD_CHNL_GetIOC() and IO_Dispatch(), violation of * which may cause timeouts and/or failure offunction SYNC_WaitOnEvent. * This invariant condition is: * * LST_Empty(pChnl->pIOCompletions) ==> pChnl->hSyncEvent is reset * and * !LST_Empty(pChnl->pIOCompletions) ==> pChnl->hSyncEvent is set. * *! Revision History: *! ================ *! 10-Feb-2004 sb: Consolidated the MAILBOX_IRQ macro at the top of the file. *! 05-Jan-2004 vp: Updated for 2.6 kernel on 24xx platform. *! 23-Apr-2003 sb: Fixed mailbox deadlock *! 24-Feb-2003 vp: Code Review Updates. *! 18-Oct-2002 vp: Ported to Linux platform *! 29-Aug-2002 rr Changed the SYNC error code return to DSP error code return * in WMD_CHNL_GetIOC. *! 22-Jan-2002 ag Zero-copy support added. *! CMM_CallocBuf() used for SM allocations. *! 04-Feb-2001 ag DSP-DMA support added. *! 22-Nov-2000 kc: Updated usage of PERF_RegisterStat. *! 06-Nov-2000 jeh Move ISR_Install, DPC_Create from CHNL_Create to IO_Create. *! 13-Oct-2000 jeh Added dwArg parameter to WMD_CHNL_AddIOReq(), added *! WMD_CHNL_Idle and WMD_CHNL_RegisterNotify for DSPStream. *! Remove #ifdef DEBUG from around channel cIOCs field. *! 21-Sep-2000 rr: PreOMAP chnl class library acts like a IO class library. *! 25-Sep-2000 ag: MEM_[Unmap]LinearAddress added for #ifdef CHNL_PREOMAP. *! 07-Sep-2000 rr: Added new channel class for PreOMAP. *! 11-Jul-2000 jeh Allow NULL user event in WMD_CHNL_Open(). *! 06-Jul-2000 rr: Changed prefix PROC to PRCS for process module calls. *! 20-Jan-2000 ag: Incorporated code review comments. *! 05-Jan-2000 ag: Text format cleanup. *! 07-Dec-1999 ag: Now setting ChnlMgr fSharedIRQ flag before ISR_Install(). *! 01-Dec-1999 ag: WMD_CHNL_Open() now accepts named sync event. *! 14-Nov-1999 ag: DPC_Schedule() uncommented. *! 28-Oct-1999 ag: CHNL Attrs userEvent not supported. *! SM addrs taken from COFF(IO) or host resource(SM). *! 25-May-1999 jg: CHNL_IOCLASS boards now get their shared memory buffer *! address and length from symbols defined in the currently *! loaded COFF file. See _chn_sm.h. *! 18-Jun-1997 gp: Moved waiting back to ring 0 to improve performance. *! 22-Jan-1998 gp: Update User's pIOC struct in GetIOC at lower IRQL (NT). *! 16-Jan-1998 gp: Commented out PERF stuff, since it is not all there in NT. *! 13-Jan-1998 gp: Protect IOCTLs from IO_DPC by raising IRQL to DIRQL (NT). *! 22-Oct-1997 gp: Call SYNC_OpenEvent in CHNL_Open, for NT support. *! 18-Jun-1997 gp: Moved waiting back to ring 0 to improve performance. *! 16-Jun-1997 gp: Added call into lower edge CHNL function to allow override *! of the SHM window length reported by Windows CM. *! 05-Jun-1997 gp: Removed unnecessary critical sections. *! 18-Mar-1997 gp: Ensured CHNL_FlushIO on input leaves channel in READY state. *! 06-Jan-1997 gp: ifdefed to support the IO variant of SHM channel class lib. *! 21-Jan-1997 gp: CHNL_Close: set pChnl = NULL for DBC_Ensure(). *! 14-Jan-1997 gp: Updated based on code review feedback. *! 03-Jan-1997 gp: Added CHNL_E_WAITTIMEOUT error return code to CHNL_FlushIO() *! 23-Oct-1996 gp: Tag channel with ring 0 process handle. *! 13-Sep-1996 gp: Added performance statistics for channel. *! 09-Sep-1996 gp: Added WMD_CHNL_GetMgrInfo(). *! 04-Sep-1996 gp: Removed shared memory control struct offset: made zero. *! 01-Aug-1996 gp: Implemented basic channel manager and channel create/delete. *! 17-Jul-1996 gp: Started pseudo coding. *! 11-Jul-1996 gp: Stubbed out. */ /* ----------------------------------- OS */ #include <dspbridge/host_os.h> /* ----------------------------------- DSP/BIOS Bridge */ #include <dspbridge/std.h> #include <dspbridge/dbdefs.h> #include <dspbridge/errbase.h> /* ----------------------------------- Trace & Debug */ #include <dspbridge/dbc.h> #include <dspbridge/dbg.h> /* ----------------------------------- OS Adaptation Layer */ #include <dspbridge/mem.h> #include <dspbridge/cfg.h> #include <dspbridge/csl.h> #include <dspbridge/sync.h> /* ----------------------------------- Mini-Driver */ #include <dspbridge/wmd.h> #include <dspbridge/wmdchnl.h> /* ----------------------------------- Platform Manager */ #include <dspbridge/dev.h> /* ----------------------------------- Others */ #include <dspbridge/io_sm.h> /* ----------------------------------- Define for This */ #define USERMODE_ADDR PAGE_OFFSET #define MAILBOX_IRQ INT_MAIL_MPU_IRQ /* ----------------------------------- Function Prototypes */ static struct LST_LIST *CreateChirpList(u32 uChirps); static void FreeChirpList(struct LST_LIST *pList); static struct CHNL_IRP *MakeNewChirp(void); static DSP_STATUS SearchFreeChannel(struct CHNL_MGR *pChnlMgr, OUT u32 *pdwChnl); /* * ======== WMD_CHNL_AddIOReq ======== * Enqueue an I/O request for data transfer on a channel to the DSP. * The direction (mode) is specified in the channel object. Note the DSP * address is specified for channels opened in direct I/O mode. */ DSP_STATUS WMD_CHNL_AddIOReq(struct CHNL_OBJECT *hChnl, void *pHostBuf, u32 cBytes, u32 cBufSize, OPTIONAL u32 dwDspAddr, u32 dwArg) { DSP_STATUS status = DSP_SOK; struct CHNL_OBJECT *pChnl = (struct CHNL_OBJECT *)hChnl; struct CHNL_IRP *pChirp = NULL; u32 dwState; bool fIsEOS; struct CHNL_MGR *pChnlMgr = pChnl->pChnlMgr; u8 *pHostSysBuf = NULL; bool fSchedDPC = false; u16 wMbVal = 0; DBG_Trace(DBG_ENTER, "> WMD_CHNL_AddIOReq pChnl %p CHNL_IsOutput %x uChnlType " "%x Id %d\n", pChnl, CHNL_IsOutput(pChnl->uMode), pChnl->uChnlType, pChnl->uId); fIsEOS = (cBytes == 0) ? true : false; if (pChnl->uChnlType == CHNL_PCPY && pChnl->uId > 1 && pHostBuf) { if (!(pHostBuf < (void *)USERMODE_ADDR)) { pHostSysBuf = pHostBuf; goto func_cont; } /* if addr in user mode, then copy to kernel space */ pHostSysBuf = MEM_Alloc(cBufSize, MEM_NONPAGED); if (pHostSysBuf == NULL) { status = DSP_EMEMORY; DBG_Trace(DBG_LEVEL7, "No memory to allocate kernel buffer\n"); goto func_cont; } if (CHNL_IsOutput(pChnl->uMode)) { status = copy_from_user(pHostSysBuf, pHostBuf, cBufSize); if (status) { DBG_Trace(DBG_LEVEL7, "Error copying user buffer to " "kernel, %d bytes remaining.\n", status); MEM_Free(pHostSysBuf); pHostSysBuf = NULL; status = DSP_EPOINTER; } } } func_cont: /* Validate args: */ if (pHostBuf == NULL) { status = DSP_EPOINTER; } else if (!MEM_IsValidHandle(pChnl, CHNL_SIGNATURE)) { status = DSP_EHANDLE; } else if (fIsEOS && CHNL_IsInput(pChnl->uMode)) { status = CHNL_E_NOEOS; } else { /* Check the channel state: only queue chirp if channel state * allows */ dwState = pChnl->dwState; if (dwState != CHNL_STATEREADY) { if (dwState & CHNL_STATECANCEL) { status = CHNL_E_CANCELLED; } else if ((dwState & CHNL_STATEEOS) && CHNL_IsOutput(pChnl->uMode)) { status = CHNL_E_EOS; } else { /* No other possible states left: */ DBC_Assert(0); } } } /* Mailbox IRQ is disabled to avoid race condition with DMA/ZCPY * channels. DPCCS is held to avoid race conditions with PCPY channels. * If DPC is scheduled in process context (IO_Schedule) and any * non-mailbox interrupt occurs, that DPC will run and break CS. Hence * we disable ALL DPCs. We will try to disable ONLY IO DPC later. */ SYNC_EnterCS(pChnlMgr->hCSObj); disable_irq(MAILBOX_IRQ); if (pChnl->uChnlType == CHNL_PCPY) { /* This is a processor-copy channel. */ if (DSP_SUCCEEDED(status) && CHNL_IsOutput(pChnl->uMode)) { /* Check buffer size on output channels for fit. */ if (cBytes > IO_BufSize(pChnl->pChnlMgr->hIOMgr)) status = CHNL_E_BUFSIZE; } } if (DSP_SUCCEEDED(status)) { /* Get a free chirp: */ pChirp = (struct CHNL_IRP *)LST_GetHead(pChnl->pFreeList); if (pChirp == NULL) status = CHNL_E_NOIORPS; } if (DSP_SUCCEEDED(status)) { /* Enqueue the chirp on the chnl's IORequest queue: */ pChirp->pHostUserBuf = pChirp->pHostSysBuf = pHostBuf; if (pChnl->uChnlType == CHNL_PCPY && pChnl->uId > 1) pChirp->pHostSysBuf = pHostSysBuf; if (DSP_SUCCEEDED(status)) { /* Note: for dma chans dwDspAddr contains dsp address * of SM buffer.*/ DBC_Assert(pChnlMgr->uWordSize != 0); /* DSP address */ pChirp->uDspAddr = dwDspAddr / pChnlMgr->uWordSize; pChirp->cBytes = cBytes; pChirp->cBufSize = cBufSize; /* Only valid for output channel */ pChirp->dwArg = dwArg; pChirp->status = (fIsEOS ? CHNL_IOCSTATEOS : CHNL_IOCSTATCOMPLETE); LST_PutTail(pChnl->pIORequests, (struct LST_ELEM *) pChirp); pChnl->cIOReqs++; DBC_Assert(pChnl->cIOReqs <= pChnl->cChirps); /* If end of stream, update the channel state to prevent * more IOR's: */ if (fIsEOS) pChnl->dwState |= CHNL_STATEEOS; { /* Legacy DSM Processor-Copy */ DBC_Assert(pChnl->uChnlType == CHNL_PCPY); /* Request IO from the DSP */ IO_RequestChnl(pChnlMgr->hIOMgr, pChnl, (CHNL_IsInput(pChnl->uMode) ? IO_INPUT : IO_OUTPUT), &wMbVal); fSchedDPC = true; } } } enable_irq(MAILBOX_IRQ); SYNC_LeaveCS(pChnlMgr->hCSObj); if (wMbVal != 0) IO_IntrDSP2(pChnlMgr->hIOMgr, wMbVal); if (fSchedDPC == true) { /* Schedule a DPC, to do the actual data transfer: */ IO_Schedule(pChnlMgr->hIOMgr); } DBG_Trace(DBG_ENTER, "< WMD_CHNL_AddIOReq pChnl %p\n", pChnl); return status; } /* * ======== WMD_CHNL_CancelIO ======== * Return all I/O requests to the client which have not yet been * transferred. The channel's I/O completion object is * signalled, and all the I/O requests are queued as IOC's, with the * status field set to CHNL_IOCSTATCANCEL. * This call is typically used in abort situations, and is a prelude to * CHNL_Close(); */ DSP_STATUS WMD_CHNL_CancelIO(struct CHNL_OBJECT *hChnl) { DSP_STATUS status = DSP_SOK; struct CHNL_OBJECT *pChnl = (struct CHNL_OBJECT *)hChnl; u32 iChnl = -1; CHNL_MODE uMode; struct CHNL_IRP *pChirp; struct CHNL_MGR *pChnlMgr = NULL; /* Check args: */ if (MEM_IsValidHandle(pChnl, CHNL_SIGNATURE)) { iChnl = pChnl->uId; uMode = pChnl->uMode; pChnlMgr = pChnl->pChnlMgr; } else { status = DSP_EHANDLE; } if (DSP_FAILED(status)) goto func_end; /* Mark this channel as cancelled, to prevent further IORequests or * IORequests or dispatching. */ SYNC_EnterCS(pChnlMgr->hCSObj); pChnl->dwState |= CHNL_STATECANCEL; if (LST_IsEmpty(pChnl->pIORequests)) goto func_cont; if (pChnl->uChnlType == CHNL_PCPY) { /* Indicate we have no more buffers available for transfer: */ if (CHNL_IsInput(pChnl->uMode)) { IO_CancelChnl(pChnlMgr->hIOMgr, iChnl); } else { /* Record that we no longer have output buffers * available: */ pChnlMgr->dwOutputMask &= ~(1 << iChnl); } } /* Move all IOR's to IOC queue: */ while (!LST_IsEmpty(pChnl->pIORequests)) { pChirp = (struct CHNL_IRP *)LST_GetHead(pChnl->pIORequests); if (pChirp) { pChirp->cBytes = 0; pChirp->status |= CHNL_IOCSTATCANCEL; LST_PutTail(pChnl->pIOCompletions, (struct LST_ELEM *)pChirp); pChnl->cIOCs++; pChnl->cIOReqs--; DBC_Assert(pChnl->cIOReqs >= 0); } } func_cont: SYNC_LeaveCS(pChnlMgr->hCSObj); func_end: return status; } /* * ======== WMD_CHNL_Close ======== * Purpose: * Ensures all pending I/O on this channel is cancelled, discards all * queued I/O completion notifications, then frees the resources allocated * for this channel, and makes the corresponding logical channel id * available for subsequent use. */ DSP_STATUS WMD_CHNL_Close(struct CHNL_OBJECT *hChnl) { DSP_STATUS status; struct CHNL_OBJECT *pChnl = (struct CHNL_OBJECT *)hChnl; /* Check args: */ if (!MEM_IsValidHandle(pChnl, CHNL_SIGNATURE)) { status = DSP_EHANDLE; goto func_cont; } { /* Cancel IO: this ensures no further IO requests or * notifications.*/ status = WMD_CHNL_CancelIO(hChnl); } func_cont: if (DSP_SUCCEEDED(status)) { /* Assert I/O on this channel is now cancelled: Protects * from IO_DPC. */ DBC_Assert((pChnl->dwState & CHNL_STATECANCEL)); /* Invalidate channel object: Protects from * CHNL_GetIOCompletion(). */ pChnl->dwSignature = 0x0000; /* Free the slot in the channel manager: */ pChnl->pChnlMgr->apChannel[pChnl->uId] = NULL; pChnl->pChnlMgr->cOpenChannels -= 1; if (pChnl->hNtfy) { NTFY_Delete(pChnl->hNtfy); pChnl->hNtfy = NULL; } /* Reset channel event: (NOTE: hUserEvent freed in user * context.). */ if (pChnl->hSyncEvent) { SYNC_ResetEvent(pChnl->hSyncEvent); SYNC_CloseEvent(pChnl->hSyncEvent); pChnl->hSyncEvent = NULL; } /* Free I/O request and I/O completion queues: */ if (pChnl->pIOCompletions) { FreeChirpList(pChnl->pIOCompletions); pChnl->pIOCompletions = NULL; pChnl->cIOCs = 0; } if (pChnl->pIORequests) { FreeChirpList(pChnl->pIORequests); pChnl->pIORequests = NULL; pChnl->cIOReqs = 0; } if (pChnl->pFreeList) { FreeChirpList(pChnl->pFreeList); pChnl->pFreeList = NULL; } /* Release channel object. */ MEM_FreeObject(pChnl); pChnl = NULL; } DBC_Ensure(DSP_FAILED(status) || !MEM_IsValidHandle(pChnl, CHNL_SIGNATURE)); return status; } /* * ======== WMD_CHNL_Create ======== * Create a channel manager object, responsible for opening new channels * and closing old ones for a given board. */ DSP_STATUS WMD_CHNL_Create(OUT struct CHNL_MGR **phChnlMgr, struct DEV_OBJECT *hDevObject, IN CONST struct CHNL_MGRATTRS *pMgrAttrs) { DSP_STATUS status = DSP_SOK; struct CHNL_MGR *pChnlMgr = NULL; s32 cChannels; #ifdef DEBUG struct CHNL_MGR *hChnlMgr; #endif /* Check DBC requirements: */ DBC_Require(phChnlMgr != NULL); DBC_Require(pMgrAttrs != NULL); DBC_Require(pMgrAttrs->cChannels > 0); DBC_Require(pMgrAttrs->cChannels <= CHNL_MAXCHANNELS); DBC_Require(pMgrAttrs->uWordSize != 0); #ifdef DEBUG /* This for the purposes of DBC_Require: */ status = DEV_GetChnlMgr(hDevObject, &hChnlMgr); DBC_Require(status != DSP_EHANDLE); DBC_Require(hChnlMgr == NULL); #endif if (DSP_SUCCEEDED(status)) { /* Allocate channel manager object: */ MEM_AllocObject(pChnlMgr, struct CHNL_MGR, CHNL_MGRSIGNATURE); if (pChnlMgr) { /* The cChannels attr must equal the # of supported * chnls for each transport(# chnls for PCPY = DDMA = * ZCPY): i.e. pMgrAttrs->cChannels = CHNL_MAXCHANNELS = * DDMA_MAXDDMACHNLS = DDMA_MAXZCPYCHNLS. */ DBC_Assert(pMgrAttrs->cChannels == CHNL_MAXCHANNELS); cChannels = (CHNL_MAXCHANNELS + (CHNL_MAXCHANNELS * CHNL_PCPY)); /* Create array of channels: */ pChnlMgr->apChannel = MEM_Calloc( sizeof(struct CHNL_OBJECT *) * cChannels, MEM_NONPAGED); if (pChnlMgr->apChannel) { /* Initialize CHNL_MGR object: */ /* Shared memory driver. */ pChnlMgr->dwType = CHNL_TYPESM; pChnlMgr->uWordSize = pMgrAttrs->uWordSize; /* total # chnls supported */ pChnlMgr->cChannels = cChannels; pChnlMgr->cOpenChannels = 0; pChnlMgr->dwOutputMask = 0; pChnlMgr->dwLastOutput = 0; pChnlMgr->hDevObject = hDevObject; if (DSP_SUCCEEDED(status)) { status = SYNC_InitializeDPCCS (&pChnlMgr->hCSObj); } } else { status = DSP_EMEMORY; } } else { status = DSP_EMEMORY; } } if (DSP_FAILED(status)) { WMD_CHNL_Destroy(pChnlMgr); *phChnlMgr = NULL; } else { /* Return channel manager object to caller... */ *phChnlMgr = pChnlMgr; } return status; } /* * ======== WMD_CHNL_Destroy ======== * Purpose: * Close all open channels, and destroy the channel manager. */ DSP_STATUS WMD_CHNL_Destroy(struct CHNL_MGR *hChnlMgr) { DSP_STATUS status = DSP_SOK; struct CHNL_MGR *pChnlMgr = hChnlMgr; u32 iChnl; if (MEM_IsValidHandle(hChnlMgr, CHNL_MGRSIGNATURE)) { /* Close all open channels: */ for (iChnl = 0; iChnl < pChnlMgr->cChannels; iChnl++) { if (DSP_SUCCEEDED (WMD_CHNL_Close(pChnlMgr->apChannel[iChnl]))) { DBC_Assert(pChnlMgr->apChannel[iChnl] == NULL); } } /* release critical section */ if (pChnlMgr->hCSObj) SYNC_DeleteCS(pChnlMgr->hCSObj); /* Free channel manager object: */ if (pChnlMgr->apChannel) MEM_Free(pChnlMgr->apChannel); /* Set hChnlMgr to NULL in device object. */ DEV_SetChnlMgr(pChnlMgr->hDevObject, NULL); /* Free this Chnl Mgr object: */ MEM_FreeObject(hChnlMgr); } else { status = DSP_EHANDLE; } return status; } /* * ======== WMD_CHNL_FlushIO ======== * purpose: * Flushes all the outstanding data requests on a channel. */ DSP_STATUS WMD_CHNL_FlushIO(struct CHNL_OBJECT *hChnl, u32 dwTimeOut) { DSP_STATUS status = DSP_SOK; struct CHNL_OBJECT *pChnl = (struct CHNL_OBJECT *)hChnl; CHNL_MODE uMode = -1; struct CHNL_MGR *pChnlMgr; struct CHNL_IOC chnlIOC; /* Check args: */ if (MEM_IsValidHandle(pChnl, CHNL_SIGNATURE)) { if ((dwTimeOut == CHNL_IOCNOWAIT) && CHNL_IsOutput(pChnl->uMode)) { status = DSP_EINVALIDARG; } else { uMode = pChnl->uMode; pChnlMgr = pChnl->pChnlMgr; } } else { status = DSP_EHANDLE; } if (DSP_SUCCEEDED(status)) { /* Note: Currently, if another thread continues to add IO * requests to this channel, this function will continue to * flush all such queued IO requests. */ if (CHNL_IsOutput(uMode) && (pChnl->uChnlType == CHNL_PCPY)) { /* Wait for IO completions, up to the specified * timeout: */ while (!LST_IsEmpty(pChnl->pIORequests) && DSP_SUCCEEDED(status)) { status = WMD_CHNL_GetIOC(hChnl, dwTimeOut, &chnlIOC); if (DSP_FAILED(status)) continue; if (chnlIOC.status & CHNL_IOCSTATTIMEOUT) status = CHNL_E_WAITTIMEOUT; } } else { status = WMD_CHNL_CancelIO(hChnl); /* Now, leave the channel in the ready state: */ pChnl->dwState &= ~CHNL_STATECANCEL; } } DBC_Ensure(DSP_FAILED(status) || LST_IsEmpty(pChnl->pIORequests)); return status; } /* * ======== WMD_CHNL_GetInfo ======== * Purpose: * Retrieve information related to a channel. */ DSP_STATUS WMD_CHNL_GetInfo(struct CHNL_OBJECT *hChnl, OUT struct CHNL_INFO *pInfo) { DSP_STATUS status = DSP_SOK; struct CHNL_OBJECT *pChnl = (struct CHNL_OBJECT *)hChnl; if (pInfo != NULL) { if (MEM_IsValidHandle(pChnl, CHNL_SIGNATURE)) { /* Return the requested information: */ pInfo->hChnlMgr = pChnl->pChnlMgr; pInfo->hEvent = pChnl->hUserEvent; pInfo->dwID = pChnl->uId; pInfo->dwMode = pChnl->uMode; pInfo->cPosition = pChnl->cBytesMoved; pInfo->hProcess = pChnl->hProcess; pInfo->hSyncEvent = pChnl->hSyncEvent; pInfo->cIOCs = pChnl->cIOCs; pInfo->cIOReqs = pChnl->cIOReqs; pInfo->dwState = pChnl->dwState; } else { status = DSP_EHANDLE; } } else { status = DSP_EPOINTER; } return status; } /* * ======== WMD_CHNL_GetIOC ======== * Optionally wait for I/O completion on a channel. Dequeue an I/O * completion record, which contains information about the completed * I/O request. * Note: Ensures Channel Invariant (see notes above). */ DSP_STATUS WMD_CHNL_GetIOC(struct CHNL_OBJECT *hChnl, u32 dwTimeOut, OUT struct CHNL_IOC *pIOC) { DSP_STATUS status = DSP_SOK; struct CHNL_OBJECT *pChnl = (struct CHNL_OBJECT *)hChnl; struct CHNL_IRP *pChirp; DSP_STATUS statSync; bool fDequeueIOC = true; struct CHNL_IOC ioc = { NULL, 0, 0, 0, 0 }; u8 *pHostSysBuf = NULL; DBG_Trace(DBG_ENTER, "> WMD_CHNL_GetIOC pChnl %p CHNL_IsOutput %x " "uChnlType %x\n", pChnl, CHNL_IsOutput(pChnl->uMode), pChnl->uChnlType); /* Check args: */ if (pIOC == NULL) { status = DSP_EPOINTER; } else if (!MEM_IsValidHandle(pChnl, CHNL_SIGNATURE)) { status = DSP_EHANDLE; } else if (dwTimeOut == CHNL_IOCNOWAIT) { if (LST_IsEmpty(pChnl->pIOCompletions)) status = CHNL_E_NOIOC; } if (DSP_FAILED(status)) goto func_end; ioc.status = CHNL_IOCSTATCOMPLETE; if (dwTimeOut != CHNL_IOCNOWAIT && LST_IsEmpty(pChnl->pIOCompletions)) { if (dwTimeOut == CHNL_IOCINFINITE) dwTimeOut = SYNC_INFINITE; statSync = SYNC_WaitOnEvent(pChnl->hSyncEvent, dwTimeOut); if (statSync == DSP_ETIMEOUT) { /* No response from DSP */ ioc.status |= CHNL_IOCSTATTIMEOUT; fDequeueIOC = false; } else if (statSync == DSP_EFAIL) { /* This can occur when the user mode thread is * aborted (^C), or when _VWIN32_WaitSingleObject() * fails due to unkown causes. */ /* Even though Wait failed, there may be something in * the Q: */ if (LST_IsEmpty(pChnl->pIOCompletions)) { ioc.status |= CHNL_IOCSTATCANCEL; fDequeueIOC = false; } } } /* See comment in AddIOReq */ SYNC_EnterCS(pChnl->pChnlMgr->hCSObj); disable_irq(MAILBOX_IRQ); if (fDequeueIOC) { /* Dequeue IOC and set pIOC; */ DBC_Assert(!LST_IsEmpty(pChnl->pIOCompletions)); pChirp = (struct CHNL_IRP *)LST_GetHead(pChnl->pIOCompletions); /* Update pIOC from channel state and chirp: */ if (pChirp) { pChnl->cIOCs--; /* If this is a zero-copy channel, then set IOC's pBuf * to the DSP's address. This DSP address will get * translated to user's virtual addr later. */ { pHostSysBuf = pChirp->pHostSysBuf; ioc.pBuf = pChirp->pHostUserBuf; } ioc.cBytes = pChirp->cBytes; ioc.cBufSize = pChirp->cBufSize; ioc.dwArg = pChirp->dwArg; ioc.status |= pChirp->status; /* Place the used chirp on the free list: */ LST_PutTail(pChnl->pFreeList, (struct LST_ELEM *) pChirp); } else { ioc.pBuf = NULL; ioc.cBytes = 0; } } else { ioc.pBuf = NULL; ioc.cBytes = 0; ioc.dwArg = 0; ioc.cBufSize = 0; } /* Ensure invariant: If any IOC's are queued for this channel... */ if (!LST_IsEmpty(pChnl->pIOCompletions)) { /* Since DSPStream_Reclaim() does not take a timeout * parameter, we pass the stream's timeout value to * WMD_CHNL_GetIOC. We cannot determine whether or not * we have waited in User mode. Since the stream's timeout * value may be non-zero, we still have to set the event. * Therefore, this optimization is taken out. * * if (dwTimeOut == CHNL_IOCNOWAIT) { * ... ensure event is set.. * SYNC_SetEvent(pChnl->hSyncEvent); * } */ SYNC_SetEvent(pChnl->hSyncEvent); } else { /* else, if list is empty, ensure event is reset. */ SYNC_ResetEvent(pChnl->hSyncEvent); } enable_irq(MAILBOX_IRQ); SYNC_LeaveCS(pChnl->pChnlMgr->hCSObj); if (fDequeueIOC && (pChnl->uChnlType == CHNL_PCPY && pChnl->uId > 1)) { if (!(ioc.pBuf < (void *) USERMODE_ADDR)) goto func_cont; /* If the addr is in user mode, then copy it */ if (!pHostSysBuf || !ioc.pBuf) { status = DSP_EPOINTER; DBG_Trace(DBG_LEVEL7, "System buffer NULL in IO completion.\n"); goto func_cont; } if (!CHNL_IsInput(pChnl->uMode)) goto func_cont1; /*pHostUserBuf */ status = copy_to_user(ioc.pBuf, pHostSysBuf, ioc.cBytes); #ifndef RES_CLEANUP_DISABLE if (status) { if (current->flags & PF_EXITING) { DBG_Trace(DBG_LEVEL7, "\n2current->flags == PF_EXITING, " " current->flags;0x%x\n", current->flags); status = 0; } else { DBG_Trace(DBG_LEVEL7, "\n2current->flags != PF_EXITING, " " current->flags;0x%x\n", current->flags); } } #endif if (status) { DBG_Trace(DBG_LEVEL7, "Error copying kernel buffer to user, %d" " bytes remaining. in_interupt %d\n", status, in_interrupt()); status = DSP_EPOINTER; } func_cont1: MEM_Free(pHostSysBuf); } func_cont: /* Update User's IOC block: */ *pIOC = ioc; func_end: DBG_Trace(DBG_ENTER, "< WMD_CHNL_GetIOC pChnl %p\n", pChnl); return status; } /* * ======== WMD_CHNL_GetMgrInfo ======== * Retrieve information related to the channel manager. */ DSP_STATUS WMD_CHNL_GetMgrInfo(struct CHNL_MGR *hChnlMgr, u32 uChnlID, OUT struct CHNL_MGRINFO *pMgrInfo) { DSP_STATUS status = DSP_SOK; struct CHNL_MGR *pChnlMgr = (struct CHNL_MGR *)hChnlMgr; if (pMgrInfo != NULL) { if (uChnlID <= CHNL_MAXCHANNELS) { if (MEM_IsValidHandle(hChnlMgr, CHNL_MGRSIGNATURE)) { /* Return the requested information: */ pMgrInfo->hChnl = pChnlMgr->apChannel[uChnlID]; pMgrInfo->cOpenChannels = pChnlMgr-> cOpenChannels; pMgrInfo->dwType = pChnlMgr->dwType; /* total # of chnls */ pMgrInfo->cChannels = pChnlMgr->cChannels; } else { status = DSP_EHANDLE; } } else { status = CHNL_E_BADCHANID; } } else { status = DSP_EPOINTER; } return status; } /* * ======== WMD_CHNL_Idle ======== * Idles a particular channel. */ DSP_STATUS WMD_CHNL_Idle(struct CHNL_OBJECT *hChnl, u32 dwTimeOut, bool fFlush) { CHNL_MODE uMode; struct CHNL_MGR *pChnlMgr; DSP_STATUS status = DSP_SOK; DBC_Require(MEM_IsValidHandle(hChnl, CHNL_SIGNATURE)); uMode = hChnl->uMode; pChnlMgr = hChnl->pChnlMgr; if (CHNL_IsOutput(uMode) && !fFlush) { /* Wait for IO completions, up to the specified timeout: */ status = WMD_CHNL_FlushIO(hChnl, dwTimeOut); } else { status = WMD_CHNL_CancelIO(hChnl); /* Reset the byte count and put channel back in ready state. */ hChnl->cBytesMoved = 0; hChnl->dwState &= ~CHNL_STATECANCEL; } return status; } /* * ======== WMD_CHNL_Open ======== * Open a new half-duplex channel to the DSP board. */ DSP_STATUS WMD_CHNL_Open(OUT struct CHNL_OBJECT **phChnl, struct CHNL_MGR *hChnlMgr, CHNL_MODE uMode, u32 uChnlId, CONST IN struct CHNL_ATTRS *pAttrs) { DSP_STATUS status = DSP_SOK; struct CHNL_MGR *pChnlMgr = hChnlMgr; struct CHNL_OBJECT *pChnl = NULL; struct SYNC_ATTRS *pSyncAttrs = NULL; struct SYNC_OBJECT *hSyncEvent = NULL; /* Ensure DBC requirements: */ DBC_Require(phChnl != NULL); DBC_Require(pAttrs != NULL); *phChnl = NULL; /* Validate Args: */ if (pAttrs->uIOReqs == 0) { status = DSP_EINVALIDARG; } else { if (!MEM_IsValidHandle(hChnlMgr, CHNL_MGRSIGNATURE)) { status = DSP_EHANDLE; } else { if (uChnlId != CHNL_PICKFREE) { if (uChnlId >= pChnlMgr->cChannels) { status = CHNL_E_BADCHANID; } else if (pChnlMgr->apChannel[uChnlId] != NULL) { status = CHNL_E_CHANBUSY; } } else { /* Check for free channel */ status = SearchFreeChannel(pChnlMgr, &uChnlId); } } } if (DSP_FAILED(status)) goto func_end; DBC_Assert(uChnlId < pChnlMgr->cChannels); /* Create channel object: */ MEM_AllocObject(pChnl, struct CHNL_OBJECT, 0x0000); if (!pChnl) { status = DSP_EMEMORY; goto func_cont; } /* Protect queues from IO_DPC: */ pChnl->dwState = CHNL_STATECANCEL; /* Allocate initial IOR and IOC queues: */ pChnl->pFreeList = CreateChirpList(pAttrs->uIOReqs); pChnl->pIORequests = CreateChirpList(0); pChnl->pIOCompletions = CreateChirpList(0); pChnl->cChirps = pAttrs->uIOReqs; pChnl->cIOCs = 0; pChnl->cIOReqs = 0; status = SYNC_OpenEvent(&hSyncEvent, pSyncAttrs); if (DSP_SUCCEEDED(status)) { status = NTFY_Create(&pChnl->hNtfy); if (DSP_FAILED(status)) { /* The only failure that could have occurred */ status = DSP_EMEMORY; } } if (DSP_SUCCEEDED(status)) { if (pChnl->pIOCompletions && pChnl->pIORequests && pChnl->pFreeList) { /* Initialize CHNL object fields: */ pChnl->pChnlMgr = pChnlMgr; pChnl->uId = uChnlId; pChnl->uMode = uMode; pChnl->hUserEvent = hSyncEvent; /* for Linux */ pChnl->hSyncEvent = hSyncEvent; /* get the process handle */ pChnl->hProcess = current->pid; pChnl->pCBArg = 0; pChnl->cBytesMoved = 0; /* Default to proc-copy */ pChnl->uChnlType = CHNL_PCPY; } else { status = DSP_EMEMORY; } } else { status = DSP_EINVALIDARG; } if (DSP_FAILED(status)) { /* Free memory */ if (pChnl->pIOCompletions) { FreeChirpList(pChnl->pIOCompletions); pChnl->pIOCompletions = NULL; pChnl->cIOCs = 0; } if (pChnl->pIORequests) { FreeChirpList(pChnl->pIORequests); pChnl->pIORequests = NULL; } if (pChnl->pFreeList) { FreeChirpList(pChnl->pFreeList); pChnl->pFreeList = NULL; } if (hSyncEvent) { SYNC_CloseEvent(hSyncEvent); hSyncEvent = NULL; } if (pChnl->hNtfy) { NTFY_Delete(pChnl->hNtfy); pChnl->hNtfy = NULL; } MEM_FreeObject(pChnl); } func_cont: if (DSP_SUCCEEDED(status)) { /* Insert channel object in channel manager: */ pChnlMgr->apChannel[pChnl->uId] = pChnl; SYNC_EnterCS(pChnlMgr->hCSObj); pChnlMgr->cOpenChannels++; SYNC_LeaveCS(pChnlMgr->hCSObj); /* Return result... */ pChnl->dwSignature = CHNL_SIGNATURE; pChnl->dwState = CHNL_STATEREADY; *phChnl = pChnl; } func_end: DBC_Ensure((DSP_SUCCEEDED(status) && MEM_IsValidHandle(pChnl, CHNL_SIGNATURE)) || (*phChnl == NULL)); return status; } /* * ======== WMD_CHNL_RegisterNotify ======== * Registers for events on a particular channel. */ DSP_STATUS WMD_CHNL_RegisterNotify(struct CHNL_OBJECT *hChnl, u32 uEventMask, u32 uNotifyType, struct DSP_NOTIFICATION *hNotification) { DSP_STATUS status = DSP_SOK; DBC_Assert(!(uEventMask & ~(DSP_STREAMDONE | DSP_STREAMIOCOMPLETION))); status = NTFY_Register(hChnl->hNtfy, hNotification, uEventMask, uNotifyType); return status; } /* * ======== CreateChirpList ======== * Purpose: * Initialize a queue of channel I/O Request/Completion packets. * Parameters: * uChirps: Number of Chirps to allocate. * Returns: * Pointer to queue of IRPs, or NULL. * Requires: * Ensures: */ static struct LST_LIST *CreateChirpList(u32 uChirps) { struct LST_LIST *pChirpList; struct CHNL_IRP *pChirp; u32 i; pChirpList = LST_Create(); if (pChirpList) { /* Make N chirps and place on queue. */ for (i = 0; (i < uChirps) && ((pChirp = MakeNewChirp()) != NULL); i++) { LST_PutTail(pChirpList, (struct LST_ELEM *)pChirp); } /* If we couldn't allocate all chirps, free those allocated: */ if (i != uChirps) { FreeChirpList(pChirpList); pChirpList = NULL; } } return pChirpList; } /* * ======== FreeChirpList ======== * Purpose: * Free the queue of Chirps. */ static void FreeChirpList(struct LST_LIST *pChirpList) { DBC_Require(pChirpList != NULL); while (!LST_IsEmpty(pChirpList)) MEM_Free(LST_GetHead(pChirpList)); LST_Delete(pChirpList); } /* * ======== MakeNewChirp ======== * Allocate the memory for a new channel IRP. */ static struct CHNL_IRP *MakeNewChirp(void) { struct CHNL_IRP *pChirp; pChirp = (struct CHNL_IRP *)MEM_Calloc( sizeof(struct CHNL_IRP), MEM_NONPAGED); if (pChirp != NULL) { /* LST_InitElem only resets the list's member values. */ LST_InitElem(&pChirp->link); } return pChirp; } /* * ======== SearchFreeChannel ======== * Search for a free channel slot in the array of channel pointers. */ static DSP_STATUS SearchFreeChannel(struct CHNL_MGR *pChnlMgr, OUT u32 *pdwChnl) { DSP_STATUS status = CHNL_E_OUTOFSTREAMS; u32 i; DBC_Require(MEM_IsValidHandle(pChnlMgr, CHNL_MGRSIGNATURE)); for (i = 0; i < pChnlMgr->cChannels; i++) { if (pChnlMgr->apChannel[i] == NULL) { status = DSP_SOK; *pdwChnl = i; break; } } return status; }