static void MEM_Check(void) { struct memInfo *pMem; struct LST_ELEM *last = &mMan.lst.head; struct LST_ELEM *curr = mMan.lst.head.next; if (!LST_IsEmpty(&mMan.lst)) { GT_0trace(MEM_debugMask, GT_7CLASS, "*** MEMORY LEAK ***\n"); GT_0trace(MEM_debugMask, GT_7CLASS, "Addr Size Caller\n"); while (curr != last) { pMem = (struct memInfo *)curr; curr = curr->next; if ((u32)pMem > PAGE_OFFSET && MEM_IsValidHandle(pMem, memInfoSign)) { GT_3trace(MEM_debugMask, GT_7CLASS, "%lx %d\t [<%p>]\n", (u32) pMem + sizeof(struct memInfo), pMem->size, pMem->caller); MLST_RemoveElem(&mMan.lst, (struct LST_ELEM *) pMem); kfree(pMem); } else { GT_1trace(MEM_debugMask, GT_7CLASS, "Invalid allocation or " "Buffer underflow at %x\n", (u32)pMem + sizeof(struct memInfo)); break; } } } DBC_Ensure(LST_IsEmpty(&mMan.lst)); }
/* * ======== 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; short int uMode; struct CHNL_IRP *pChirp; struct CHNL_MGR *pChnlMgr = NULL; /* Check args: */ if (MEM_IsValidHandle(pChnl, CHNL_SIGNATURE) && pChnl->pChnlMgr) { 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 list_head *)pChirp); pChnl->cIOCs++; pChnl->cIOReqs--; DBC_Assert(pChnl->cIOReqs >= 0); } } func_cont: SYNC_LeaveCS(pChnlMgr->hCSObj); func_end: return status; }
/* * ======== WMD_MSG_DeleteQueue ======== * Delete a MSG queue allocated in WMD_MSG_CreateQueue. */ void WMD_MSG_DeleteQueue(struct MSG_QUEUE *hMsgQueue) { struct MSG_MGR *hMsgMgr = hMsgQueue->hMsgMgr; u32 refCount; DBC_Require(MEM_IsValidHandle(hMsgQueue, MSGQ_SIGNATURE)); hMsgQueue->fDone = true; /* Unblock all threads blocked in MSG_Get() or MSG_Put(). */ refCount = hMsgQueue->refCount; while (refCount) { /* Unblock thread */ SYNC_SetEvent(hMsgQueue->hSyncDone); /* Wait for acknowledgement */ SYNC_WaitOnEvent(hMsgQueue->hSyncDoneAck, SYNC_INFINITE); refCount = hMsgQueue->refCount; } /* Remove message queue from hMsgMgr->queueList */ (void)SYNC_EnterCS(hMsgMgr->hSyncCS); LST_RemoveElem(hMsgMgr->queueList, (struct LST_ELEM *)hMsgQueue); /* Free the message queue object */ DeleteMsgQueue(hMsgQueue, hMsgQueue->uMaxMsgs); if (!hMsgMgr->msgFreeList) goto func_cont; if (LST_IsEmpty(hMsgMgr->msgFreeList)) SYNC_ResetEvent(hMsgMgr->hSyncEvent); func_cont: (void)SYNC_LeaveCS(hMsgMgr->hSyncCS); }
/* * ======== NTFY_Delete ======== * Purpose: * Free resources allocated in NTFY_Create. */ void NTFY_Delete(struct NTFY_OBJECT *hNtfy) { struct NOTIFICATION *pNotify; DBC_Require(MEM_IsValidHandle(hNtfy, NTFY_SIGNATURE)); /* Remove any elements remaining in list */ if (hNtfy->notifyList) { (void) SYNC_EnterCS(hNtfy->hSync); while ((pNotify = (struct NOTIFICATION *)LST_GetHead(hNtfy-> notifyList))) { DeleteNotify(pNotify); } DBC_Assert(LST_IsEmpty(hNtfy->notifyList)); kfree(hNtfy->notifyList); (void) SYNC_LeaveCS(hNtfy->hSync); } if (hNtfy->hSync) (void)SYNC_DeleteCS(hNtfy->hSync); MEM_FreeObject(hNtfy); }
/* * ======== DeleteMsgMgr ======== */ static void DeleteMsgMgr(struct MSG_MGR *hMsgMgr) { DBC_Require(MEM_IsValidHandle(hMsgMgr, MSGMGR_SIGNATURE)); if (hMsgMgr->queueList) { if (LST_IsEmpty(hMsgMgr->queueList)) { LST_Delete(hMsgMgr->queueList); hMsgMgr->queueList = NULL; } } if (hMsgMgr->msgFreeList) { FreeMsgList(hMsgMgr->msgFreeList); hMsgMgr->msgFreeList = NULL; } if (hMsgMgr->msgUsedList) { FreeMsgList(hMsgMgr->msgUsedList); hMsgMgr->msgUsedList = NULL; } if (hMsgMgr->hSyncEvent) SYNC_CloseEvent(hMsgMgr->hSyncEvent); if (hMsgMgr->hSyncCS) SYNC_DeleteCS(hMsgMgr->hSyncCS); MEM_FreeObject(hMsgMgr); }
/* * ======== 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; short int 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; }
/* * ======== FreeChirpList ======== * Purpose: * Free the queue of Chirps. */ static void FreeChirpList(struct LST_LIST *pChirpList) { DBC_Require(pChirpList != NULL); while (!LST_IsEmpty(pChirpList)) kfree(LST_GetHead(pChirpList)); kfree(pChirpList); }
/* * ======== LST_First ======== * Purpose: * Returns a pointer to the first element of the list, or NULL if the * list is empty. */ struct LST_ELEM *LST_First(struct LST_LIST *pList) { struct LST_ELEM *pElem = NULL; GT_1trace(LST_debugMask, GT_ENTER, "LST_First: pList 0x%x\n", pList); if (pList && !LST_IsEmpty(pList)) pElem = pList->head.next; return pElem; }
/* * ======== DeleteMsgQueue ======== */ static void DeleteMsgQueue(struct MSG_QUEUE *hMsgQueue, u32 uNumToDSP) { struct MSG_MGR *hMsgMgr; struct MSG_FRAME *pMsg; u32 i; if (!MEM_IsValidHandle(hMsgQueue, MSGQ_SIGNATURE) || !hMsgQueue->hMsgMgr || !hMsgQueue->hMsgMgr->msgFreeList) goto func_end; hMsgMgr = hMsgQueue->hMsgMgr; /* Pull off uNumToDSP message frames from Msg manager and free */ for (i = 0; i < uNumToDSP; i++) { if (!LST_IsEmpty(hMsgMgr->msgFreeList)) { pMsg = (struct MSG_FRAME *)LST_GetHead(hMsgMgr-> msgFreeList); MEM_Free(pMsg); } else { /* Cannot free all of the message frames */ break; } } if (hMsgQueue->msgFreeList) { FreeMsgList(hMsgQueue->msgFreeList); hMsgQueue->msgFreeList = NULL; } if (hMsgQueue->msgUsedList) { FreeMsgList(hMsgQueue->msgUsedList); hMsgQueue->msgUsedList = NULL; } if (hMsgQueue->hNtfy) NTFY_Delete(hMsgQueue->hNtfy); if (hMsgQueue->hSyncEvent) SYNC_CloseEvent(hMsgQueue->hSyncEvent); if (hMsgQueue->hSyncDone) SYNC_CloseEvent(hMsgQueue->hSyncDone); if (hMsgQueue->hSyncDoneAck) SYNC_CloseEvent(hMsgQueue->hSyncDoneAck); MEM_FreeObject(hMsgQueue); func_end: return; }
/* * ======== LST_PutTail ======== * Purpose: * Adds the specified element to the tail of the list */ void LST_PutTail(struct LST_LIST *pList, struct LST_ELEM *pElem) { GT_2trace(LST_debugMask, GT_ENTER, "LST_PutTail: pList 0x%x, pElem 0x%x\n", pList, pElem); if (!pList || !pElem) return; pElem->prev = pList->head.prev; pElem->next = &pList->head; pList->head.prev = pElem; pElem->prev->next = pElem; DBC_Ensure(!LST_IsEmpty(pList)); }
/* * ======== FreeMsgList ======== */ static void FreeMsgList(struct LST_LIST *msgList) { struct MSG_FRAME *pMsg; if (!msgList) goto func_end; while ((pMsg = (struct MSG_FRAME *)LST_GetHead(msgList)) != NULL) MEM_Free(pMsg); DBC_Assert(LST_IsEmpty(msgList)); LST_Delete(msgList); func_end: return; }
/* * ======== LST_Next ======== * Purpose: * Returns a pointer to the next element of the list, or NULL if the * next element is the head of the list or the list is empty. */ struct LST_ELEM *LST_Next(struct LST_LIST *pList, struct LST_ELEM *pCurElem) { struct LST_ELEM *pNextElem = NULL; GT_2trace(LST_debugMask, GT_ENTER, "LST_Next: pList 0x%x, pCurElem 0x%x\n", pList, pCurElem); if (!pList || !pCurElem) return NULL; if (!LST_IsEmpty(pList)) { if (pCurElem->next != &pList->head) pNextElem = pCurElem->next; } return pNextElem; }
/* * ======== LST_GetHead ======== * Purpose: * "Pops" the head off the list and returns a pointer to it. */ struct LST_ELEM *LST_GetHead(struct LST_LIST *pList) { struct LST_ELEM *pElem; GT_1trace(LST_debugMask, GT_ENTER, "LST_GetHead: pList 0x%x\n", pList); if (!pList || LST_IsEmpty(pList)) return NULL; /* pElem is always valid because the list cannot be empty * at this point */ pElem = pList->head.next; pList->head.next = pElem->next; pElem->next->prev = &pList->head; return pElem->self; }
/* * ======== LST_RemoveElem ======== * Purpose: * Removes (unlinks) the given element from the list, if the list is not * empty. Does not free the list element. */ void LST_RemoveElem(struct LST_LIST *pList, struct LST_ELEM *pCurElem) { GT_2trace(LST_debugMask, GT_ENTER, "LST_RemoveElem: pList 0x%x, pCurElem " "0x%x\n", pList, pCurElem); if (!pList || !pCurElem) return; if (!LST_IsEmpty(pList)) { pCurElem->prev->next = pCurElem->next; pCurElem->next->prev = pCurElem->prev; /* set elem fields to NULL to prevent illegal references */ pCurElem->next = NULL; pCurElem->prev = NULL; } }
/* * ======== RMM_delete ======== */ void RMM_delete(struct RMM_TargetObj *target) { struct RMM_OvlySect *pSect; struct RMM_Header *hptr; struct RMM_Header *next; u32 i; DBC_Require(MEM_IsValidHandle(target, RMM_TARGSIGNATURE)); GT_1trace(RMM_debugMask, GT_ENTER, "RMM_delete(0x%lx)\n", target); if (target->segTab != NULL) MEM_Free(target->segTab); if (target->ovlyList) { while ((pSect = (struct RMM_OvlySect *)LST_GetHead (target->ovlyList))) { MEM_Free(pSect); } DBC_Assert(LST_IsEmpty(target->ovlyList)); LST_Delete(target->ovlyList); } if (target->freeList != NULL) { /* Free elements on freelist */ for (i = 0; i < target->numSegs; i++) { hptr = next = target->freeList[i]; while (next) { hptr = next; next = hptr->next; MEM_Free(hptr); } } MEM_Free(target->freeList); } MEM_FreeObject(target); }
/* * ======== WMD_MSG_Get ======== * Get a message from a MSG queue. */ DSP_STATUS WMD_MSG_Get(struct MSG_QUEUE *hMsgQueue, struct DSP_MSG *pMsg, u32 uTimeout) { struct MSG_FRAME *pMsgFrame; struct MSG_MGR *hMsgMgr; bool fGotMsg = false; struct SYNC_OBJECT *hSyncs[2]; u32 uIndex; DSP_STATUS status = DSP_SOK; DBC_Require(MEM_IsValidHandle(hMsgQueue, MSGQ_SIGNATURE)); DBC_Require(pMsg != NULL); hMsgMgr = hMsgQueue->hMsgMgr; if (!hMsgQueue->msgUsedList) { status = DSP_EHANDLE; goto func_end; } /* Enter critical section */ (void)SYNC_EnterCS(hMsgMgr->hSyncCS); /* If a message is already there, get it */ if (!LST_IsEmpty(hMsgQueue->msgUsedList)) { pMsgFrame = (struct MSG_FRAME *)LST_GetHead(hMsgQueue-> msgUsedList); if (pMsgFrame != NULL) { *pMsg = pMsgFrame->msgData.msg; LST_PutTail(hMsgQueue->msgFreeList, (struct LST_ELEM *)pMsgFrame); if (LST_IsEmpty(hMsgQueue->msgUsedList)) SYNC_ResetEvent(hMsgQueue->hSyncEvent); else { NTFY_Notify(hMsgQueue->hNtfy, DSP_NODEMESSAGEREADY); SYNC_SetEvent(hMsgQueue->hSyncEvent); } fGotMsg = true; } } else { if (hMsgQueue->fDone) status = DSP_EFAIL; else hMsgQueue->refCount++; } /* Exit critical section */ (void)SYNC_LeaveCS(hMsgMgr->hSyncCS); if (DSP_SUCCEEDED(status) && !fGotMsg) { /* Wait til message is available, timeout, or done. We don't * have to schedule the DPC, since the DSP will send messages * when they are available. */ hSyncs[0] = hMsgQueue->hSyncEvent; hSyncs[1] = hMsgQueue->hSyncDone; status = SYNC_WaitOnMultipleEvents(hSyncs, 2, uTimeout, &uIndex); /* Enter critical section */ (void)SYNC_EnterCS(hMsgMgr->hSyncCS); if (hMsgQueue->fDone) { hMsgQueue->refCount--; /* Exit critical section */ (void)SYNC_LeaveCS(hMsgMgr->hSyncCS); /* Signal that we're not going to access hMsgQueue * anymore, so it can be deleted. */ (void)SYNC_SetEvent(hMsgQueue->hSyncDoneAck); status = DSP_EFAIL; } else { if (DSP_SUCCEEDED(status)) { DBC_Assert(!LST_IsEmpty(hMsgQueue-> msgUsedList)); /* Get msg from used list */ pMsgFrame = (struct MSG_FRAME *) LST_GetHead(hMsgQueue->msgUsedList); /* Copy message into pMsg and put frame on the * free list */ if (pMsgFrame != NULL) { *pMsg = pMsgFrame->msgData.msg; LST_PutTail(hMsgQueue->msgFreeList, (struct LST_ELEM *)pMsgFrame); } } hMsgQueue->refCount--; /* Reset the event if there are still queued messages */ if (!LST_IsEmpty(hMsgQueue->msgUsedList)) { NTFY_Notify(hMsgQueue->hNtfy, DSP_NODEMESSAGEREADY); SYNC_SetEvent(hMsgQueue->hSyncEvent); } /* Exit critical section */ (void)SYNC_LeaveCS(hMsgMgr->hSyncCS); } } func_end: 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; struct WMD_DEV_CONTEXT *dev_ctxt; struct DEV_OBJECT *dev_obj; /* 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; } dev_obj = DEV_GetFirst(); DEV_GetWMDContext(dev_obj, &dev_ctxt); if (!dev_ctxt) status = DSP_EHANDLE; 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); omap_mbox_disable_irq(dev_ctxt->mbox, IRQ_RX); 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 list_head *)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); } omap_mbox_enable_irq(dev_ctxt->mbox, IRQ_RX); 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; goto func_cont; } if (!CHNL_IsInput(pChnl->uMode)) goto func_cont1; /*pHostUserBuf */ status = copy_to_user(ioc.pBuf, pHostSysBuf, ioc.cBytes); if (status) { if (current->flags & PF_EXITING) status = 0; } if (status) status = DSP_EPOINTER; func_cont1: kfree(pHostSysBuf); } func_cont: /* Update User's IOC block: */ *pIOC = ioc; func_end: return status; }
/* * ======== WMD_MSG_CreateQueue ======== * Create a MSG_QUEUE for sending/receiving messages to/from a node * on the DSP. */ DSP_STATUS WMD_MSG_CreateQueue(struct MSG_MGR *hMsgMgr, OUT struct MSG_QUEUE **phMsgQueue, u32 dwId, u32 uMaxMsgs, HANDLE hArg) { u32 i; u32 uNumAllocated = 0; struct MSG_QUEUE *pMsgQ; DSP_STATUS status = DSP_SOK; DBC_Require(MEM_IsValidHandle(hMsgMgr, MSGMGR_SIGNATURE)); DBC_Require(phMsgQueue != NULL); *phMsgQueue = NULL; /* Allocate MSG_QUEUE object */ MEM_AllocObject(pMsgQ, struct MSG_QUEUE, MSGQ_SIGNATURE); if (!pMsgQ) { status = DSP_EMEMORY; goto func_end; } LST_InitElem((struct LST_ELEM *) pMsgQ); pMsgQ->uMaxMsgs = uMaxMsgs; pMsgQ->hMsgMgr = hMsgMgr; pMsgQ->hArg = hArg; /* Node handle */ pMsgQ->dwId = dwId; /* Node env (not valid yet) */ /* Queues of Message frames for messages from the DSP */ pMsgQ->msgFreeList = LST_Create(); pMsgQ->msgUsedList = LST_Create(); if (pMsgQ->msgFreeList == NULL || pMsgQ->msgUsedList == NULL) status = DSP_EMEMORY; /* Create event that will be signalled when a message from * the DSP is available. */ if (DSP_SUCCEEDED(status)) status = SYNC_OpenEvent(&pMsgQ->hSyncEvent, NULL); /* Create a notification list for message ready notification. */ if (DSP_SUCCEEDED(status)) status = NTFY_Create(&pMsgQ->hNtfy); /* Create events that will be used to synchronize cleanup * when the object is deleted. hSyncDone will be set to * unblock threads in MSG_Put() or MSG_Get(). hSyncDoneAck * will be set by the unblocked thread to signal that it * is unblocked and will no longer reference the object. */ if (DSP_SUCCEEDED(status)) status = SYNC_OpenEvent(&pMsgQ->hSyncDone, NULL); if (DSP_SUCCEEDED(status)) status = SYNC_OpenEvent(&pMsgQ->hSyncDoneAck, NULL); if (DSP_SUCCEEDED(status)) { if (!hMsgMgr->msgFreeList) { status = DSP_EHANDLE; goto func_end; } /* Enter critical section */ (void)SYNC_EnterCS(hMsgMgr->hSyncCS); /* Initialize message frames and put in appropriate queues */ for (i = 0; i < uMaxMsgs && DSP_SUCCEEDED(status); i++) { status = AddNewMsg(hMsgMgr->msgFreeList); if (DSP_SUCCEEDED(status)) { uNumAllocated++; status = AddNewMsg(pMsgQ->msgFreeList); } } if (DSP_FAILED(status)) { /* Stay inside CS to prevent others from taking any * of the newly allocated message frames. */ DeleteMsgQueue(pMsgQ, uNumAllocated); } else { LST_PutTail(hMsgMgr->queueList, (struct LST_ELEM *)pMsgQ); *phMsgQueue = pMsgQ; /* Signal that free frames are now available */ if (!LST_IsEmpty(hMsgMgr->msgFreeList)) SYNC_SetEvent(hMsgMgr->hSyncEvent); } /* Exit critical section */ (void)SYNC_LeaveCS(hMsgMgr->hSyncCS); } else { DeleteMsgQueue(pMsgQ, 0); } func_end: 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_MSG_Put ======== * Put a message onto a MSG queue. */ DSP_STATUS WMD_MSG_Put(struct MSG_QUEUE *hMsgQueue, IN CONST struct DSP_MSG *pMsg, u32 uTimeout) { struct MSG_FRAME *pMsgFrame; struct MSG_MGR *hMsgMgr; bool fPutMsg = false; struct SYNC_OBJECT *hSyncs[2]; u32 uIndex; DSP_STATUS status = DSP_SOK; DBC_Require(MEM_IsValidHandle(hMsgQueue, MSGQ_SIGNATURE)); DBC_Require(pMsg != NULL); hMsgMgr = hMsgQueue->hMsgMgr; if (!hMsgMgr->msgFreeList) { status = DSP_EHANDLE; goto func_end; } (void) SYNC_EnterCS(hMsgMgr->hSyncCS); /* If a message frame is available, use it */ if (!LST_IsEmpty(hMsgMgr->msgFreeList)) { pMsgFrame = (struct MSG_FRAME *)LST_GetHead(hMsgMgr-> msgFreeList); if (pMsgFrame != NULL) { pMsgFrame->msgData.msg = *pMsg; pMsgFrame->msgData.dwId = hMsgQueue->dwId; LST_PutTail(hMsgMgr->msgUsedList, (struct LST_ELEM *) pMsgFrame); hMsgMgr->uMsgsPending++; fPutMsg = true; } if (LST_IsEmpty(hMsgMgr->msgFreeList)) SYNC_ResetEvent(hMsgMgr->hSyncEvent); /* Release critical section before scheduling DPC */ (void)SYNC_LeaveCS(hMsgMgr->hSyncCS); /* Schedule a DPC, to do the actual data transfer: */ IO_Schedule(hMsgMgr->hIOMgr); } else { if (hMsgQueue->fDone) status = DSP_EFAIL; else hMsgQueue->refCount++; (void)SYNC_LeaveCS(hMsgMgr->hSyncCS); } if (DSP_SUCCEEDED(status) && !fPutMsg) { /* Wait til a free message frame is available, timeout, * or done */ hSyncs[0] = hMsgMgr->hSyncEvent; hSyncs[1] = hMsgQueue->hSyncDone; status = SYNC_WaitOnMultipleEvents(hSyncs, 2, uTimeout, &uIndex); /* Enter critical section */ (void)SYNC_EnterCS(hMsgMgr->hSyncCS); if (hMsgQueue->fDone) { hMsgQueue->refCount--; /* Exit critical section */ (void)SYNC_LeaveCS(hMsgMgr->hSyncCS); /* Signal that we're not going to access hMsgQueue * anymore, so it can be deleted. */ (void)SYNC_SetEvent(hMsgQueue->hSyncDoneAck); status = DSP_EFAIL; } else { if (DSP_SUCCEEDED(status)) { if (LST_IsEmpty(hMsgMgr->msgFreeList)) { status = DSP_EPOINTER; goto func_cont; } /* Get msg from free list */ pMsgFrame = (struct MSG_FRAME *) LST_GetHead(hMsgMgr->msgFreeList); /* Copy message into pMsg and put frame on the * used list */ if (pMsgFrame != NULL) { pMsgFrame->msgData.msg = *pMsg; pMsgFrame->msgData.dwId = hMsgQueue->dwId; LST_PutTail(hMsgMgr->msgUsedList, (struct LST_ELEM *) pMsgFrame); hMsgMgr->uMsgsPending++; /* Schedule a DPC, to do the actual * data transfer: */ IO_Schedule(hMsgMgr->hIOMgr); } } hMsgQueue->refCount--; /* Reset event if there are still frames available */ if (!LST_IsEmpty(hMsgMgr->msgFreeList)) SYNC_SetEvent(hMsgMgr->hSyncEvent); func_cont: /* Exit critical section */ (void) SYNC_LeaveCS(hMsgMgr->hSyncCS); } } func_end: return status; }