kernel/eka/drivers/dma/dmapil.cpp
changeset 45 329ab0095843
parent 43 96e5fb8b040d
child 80 597aaf25e343
child 90 947f0dc9f7a8
--- a/kernel/eka/drivers/dma/dmapil.cpp	Thu Jan 07 13:38:45 2010 +0200
+++ b/kernel/eka/drivers/dma/dmapil.cpp	Mon Jan 18 21:31:10 2010 +0200
@@ -475,6 +475,15 @@
 
 	// append request to queue and link new descriptor list to existing one.
 	iChannel.Wait();
+
+	TUint32 req_count = iChannel.iQueuedRequests++;
+	if (req_count == 0)
+		{
+		iChannel.Signal();
+		iChannel.QueuedRequestCountChanged();
+		iChannel.Wait();
+		}
+
 	if (!(iChannel.iIsrDfc & (TUint32)TDmaChannel::KCancelFlagMask))
 		{
 		iQueued = ETrue;
@@ -482,8 +491,18 @@
 		*iChannel.iNullPtr = iFirstHdr;
 		iChannel.iNullPtr = &(iLastHdr->iNext);
 		iChannel.DoQueue(*this);
+		iChannel.Signal();
 		}
-	iChannel.Signal();
+	else
+		{
+		// Someone is cancelling all requests...
+		req_count = --iChannel.iQueuedRequests;
+		iChannel.Signal();
+		if (req_count == 0)
+			{
+			iChannel.QueuedRequestCountChanged();
+			}
+		}
 
 	__DMA_INVARIANT();
 	}
@@ -586,14 +605,19 @@
 
 
 TDmaChannel::TDmaChannel()
-	: iNullPtr(&iCurHdr),
-	  iDfc(Dfc, NULL, 0)
+	: iController(NULL),
+	  iPslId(0),
+	  iCurHdr(NULL),
+	  iNullPtr(&iCurHdr),
+	  iDfc(Dfc, NULL, 0),
+	  iMaxDesCount(0),
+	  iAvailDesCount(0),
+	  iIsrDfc(0),
+	  iReqQ(),
+	  iReqCount(0),
+	  iQueuedRequests(0),
+	  iCancelInfo(NULL)
 	{
-	// iController = NULL;
-	// iPslId = 0;
-	// iCurHdr = NULL;
-	// iMaxDesCount = iAvailDesCount = 0;
-	// iReqCount = 0;
 	__DMA_INVARIANT();
 	}
 
@@ -640,6 +664,8 @@
 	__DMA_ASSERTD(IsQueueEmpty());
 	__DMA_ASSERTD(iReqCount == 0);
 
+	__DMA_ASSERTD(iQueuedRequests == 0);
+
 	// descriptor leak? bug in request code
 	__DMA_ASSERTD(iAvailDesCount == iMaxDesCount);
 
@@ -665,8 +691,10 @@
 	TBool wait = FALSE;
 	TDmaCancelInfo c;
 	TDmaCancelInfo* waiters = 0;
+
 	NKern::ThreadEnterCS();
 	Wait();
+	const TUint32 req_count_before = iQueuedRequests;
 	NThreadBase* dfcnt = iDfc.Thread();
 	__e32_atomic_store_ord32(&iIsrDfc, (TUint32)KCancelFlagMask);
 	// ISRs after this point will not post a DFC, however a DFC may already be queued or running or both
@@ -683,6 +711,7 @@
 		SDblQueLink* pL;
 		while ((pL = iReqQ.GetFirst()) != NULL)
 			{
+			iQueuedRequests--;
 			DDmaRequest* pR = _LOFF(pL, DDmaRequest, iLink);
 			pR->OnDeque();
 			}
@@ -709,12 +738,21 @@
 		wait = TRUE;
 		iDfc.Enque();
 		}
+	const TUint32 req_count_after = iQueuedRequests;
 	Signal();
 	if (waiters)
 		waiters->Signal();
 	if (wait)
 		NKern::FSWait(&c.iSem);
  	NKern::ThreadLeaveCS();
+
+	// Only call PSL if there were requests queued when we entered AND there
+	// are now no requests left on the queue.
+	if ((req_count_before != 0) && (req_count_after == 0))
+		{
+		QueuedRequestCountChanged();
+		}
+
 	__DMA_INVARIANT();
 	}
 
@@ -740,6 +778,8 @@
 	const TBool error = w & (TUint32)KErrorFlagMask;
 	TBool stop = w & (TUint32)KCancelFlagMask;
 	__DMA_ASSERTD(count>0 || stop);
+	const TUint32 req_count_before = iQueuedRequests;
+	TUint32 req_count_after = 0;
 
 	while(count && !stop)
 		{
@@ -767,6 +807,7 @@
 				{
 				pCompletedReq = pCurReq;
 				pCurReq->iLink.Deque();
+				iQueuedRequests--;
 				if (iReqQ.IsEmpty())
 					iNullPtr = &iCurHdr;
 				pCompletedReq->OnDeque();
@@ -814,6 +855,7 @@
 		// reset the ISR count - new requests can now be processed
 		__e32_atomic_store_rel32(&iIsrDfc, 0);
 
+		req_count_after = iQueuedRequests;
 		Signal();
 
 		// release threads doing CancelAll()
@@ -832,6 +874,7 @@
 		SDblQueLink* pL;
 		while ((pL = q.GetFirst()) != NULL)
 			{
+			iQueuedRequests--;
 			DDmaRequest* pR = _LOFF(pL, DDmaRequest, iLink);
 			__KTRACE_OPT(KDMA, Kern::Printf("Removing request from queue and notifying client"));
 			pR->OnDeque();
@@ -844,10 +887,22 @@
 				Wait();
 				}
 			}
+		req_count_after = iQueuedRequests;
 		Signal();
 		}
 	else
+		{
+		req_count_after = iQueuedRequests;
 		Signal();
+		}
+
+	// Only call PSL if there were requests queued when we entered AND there
+	// are now no requests left on the queue (after also having executed all
+	// client callbacks).
+	if ((req_count_before != 0) && (req_count_after == 0))
+		{
+		QueuedRequestCountChanged();
+		}
 
 	__DMA_INVARIANT();
 	}
@@ -870,6 +925,21 @@
 	{
 	}
 
+
+/** PSL may override */
+void TDmaChannel::QueuedRequestCountChanged()
+	{
+#ifdef _DEBUG
+	Wait();
+	__KTRACE_OPT(KDMA,
+				 Kern::Printf("TDmaChannel::QueuedRequestCountChanged() %d",
+							  iQueuedRequests));
+	__DMA_ASSERTA(iQueuedRequests >= 0);
+	Signal();
+#endif
+	}
+
+
 #ifdef _DEBUG
 
 void TDmaChannel::Invariant()