kernel/eka/drivers/dma/dmapil.cpp
branchRCL_3
changeset 80 597aaf25e343
parent 36 538db54a451d
child 81 e7d2d738d3c2
--- a/kernel/eka/drivers/dma/dmapil.cpp	Sat Feb 20 00:10:51 2010 +0200
+++ b/kernel/eka/drivers/dma/dmapil.cpp	Fri Mar 12 15:50:11 2010 +0200
@@ -861,30 +861,55 @@
 		// release threads doing CancelAll()
 		waiters->Signal();
 		}
-	else if (!error && !iDfc.Queued() && !iReqQ.IsEmpty() && iController->IsIdle(*this))
+	else if (!error && iController->IsIdle(*this) && !iReqQ.IsEmpty() && !iDfc.Queued())
 		{
-		__KTRACE_OPT(KDMA, Kern::Printf("Missed interrupt(s) - draining request queue"));
-		ResetStateMachine();
-
-		// Move orphaned requests to temporary queue so channel queue can
-		// accept new requests.
-		SDblQue q;
-		q.MoveFrom(&iReqQ);
+		// Wait for a bit. If during that time the condition goes away then it
+		// was a 'spurious missed interrupt', in which case we just do nothing.
+		TBool spurious = EFalse;
+		const TUint32 nano_secs_per_loop = 1000 * 1000;			// 1ms
+		for (TInt i = 5; i > 0; i--)
+			{
+			if (!iController->IsIdle(*this))
+				{
+				__KTRACE_OPT(KDMA, Kern::Printf("DMAC no longer idle (i = %d)", i));
+				spurious = ETrue;
+				break;
+				}
+			else if (iDfc.Queued())
+				{
+				__KTRACE_OPT(KDMA, Kern::Printf("DFC now queued (i = %d)", i));
+				spurious = ETrue;
+				break;
+				}
+			Kern::NanoWait(nano_secs_per_loop);
+			}
+		if (!spurious)
+			{
+			__KTRACE_OPT(KDMA,
+						 Kern::Printf("Missed interrupt(s) - draining request queue on ch %d",
+									  PslId()));
+			ResetStateMachine();
 
-		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();
-			DDmaRequest::TCallback cb = pR->iCb;
-			TAny* arg = pR->iCbArg;
-			if (cb)
+			// Move orphaned requests to temporary queue so channel queue can
+			// accept new requests.
+			SDblQue q;
+			q.MoveFrom(&iReqQ);
+
+			SDblQueLink* pL;
+			while ((pL = q.GetFirst()) != NULL)
 				{
-				Signal();
-				(*cb)(DDmaRequest::EOk, arg);
-				Wait();
+				iQueuedRequests--;
+				DDmaRequest* pR = _LOFF(pL, DDmaRequest, iLink);
+				__KTRACE_OPT(KDMA, Kern::Printf("Removing request from queue and notifying client"));
+				pR->OnDeque();
+				DDmaRequest::TCallback cb = pR->iCb;
+				TAny* arg = pR->iCbArg;
+				if (cb)
+					{
+					Signal();
+					(*cb)(DDmaRequest::EOk, arg);
+					Wait();
+					}
 				}
 			}
 		req_count_after = iQueuedRequests;