Exemplo n.º 1
0
/*
 *  ======== 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;
	struct WMD_DEV_CONTEXT *dev_ctxt;
	struct DEV_OBJECT *dev_obj;
	u32 dwState;
	bool fIsEOS;
	struct CHNL_MGR *pChnlMgr = pChnl->pChnlMgr;
	u8 *pHostSysBuf = NULL;
	bool fSchedDPC = false;
	u16 wMbVal = 0;

	fIsEOS = (cBytes == 0);
	/* 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);
		}
	}

	dev_obj = DEV_GetFirst();
	DEV_GetWMDContext(dev_obj, &dev_ctxt);
	if (!dev_ctxt)
		status = DSP_EHANDLE;

	if (DSP_FAILED(status))
		goto func_end;

	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;
			goto func_end;
		}
		if (CHNL_IsOutput(pChnl->uMode)) {
			status = copy_from_user(pHostSysBuf, pHostBuf,
						cBufSize);
			if (status) {
				kfree(pHostSysBuf);
				pHostSysBuf = NULL;
				status = DSP_EPOINTER;
				goto func_end;
			}
		}
	}
func_cont:
	/* 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);
	omap_mbox_disable_irq(dev_ctxt->mbox, IRQ_RX);
	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;

		/*
		 * 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 list_head *)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;


	}
	omap_mbox_enable_irq(dev_ctxt->mbox, IRQ_RX);
	SYNC_LeaveCS(pChnlMgr->hCSObj);
	if (wMbVal != 0)
		IO_IntrDSP2(pChnlMgr->hIOMgr, wMbVal);

	/* Schedule a DPC, to do the actual data transfer: */
	if (fSchedDPC)
		IO_Schedule(pChnlMgr->hIOMgr);

func_end:
	return status;
}
Exemplo n.º 2
0
/*
 *  ======== 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;
}