Update contrib.
1 // Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies).
2 // All rights reserved.
3 // This component and the accompanying materials are made available
4 // under the terms of the License "Eclipse Public License v1.0"
5 // which accompanies this distribution, and is available
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
18 const TUint8 KMutexOrder = 0xf0;
20 class DBMLDevice : public DLogicalDevice
24 virtual TInt Install();
25 virtual void GetCaps(TDes8& aDes) const;
26 virtual TInt Create(DLogicalChannelBase*& aChannel);
29 class DBMLChannel : public DLogicalChannelBase, public MBMIsr, public MBMInterruptLatencyIsr
35 virtual TInt DoCreate(TInt aUnit, const TDesC8* anInfo, const TVersion& aVer);
36 virtual TInt Request(TInt aFunction, TAny* a1, TAny* a2);
38 DBMPChannel* PChannel() { return (DBMPChannel*) iPdd; }
41 static const TInt KBMDfcQThreadPriority;
42 static const TInt KBMKernelThreadPriority;
44 static void Dfc(TAny*);
46 virtual void Isr(TBMTicks now);
48 TInt (DBMLChannel::*iRequestInterrupt)(); // Measurement specific RBMChannel::RequestInterrupt() implmentation
49 TInt RequestInterrupt(); // Default iRequestInterrupt() implementation
51 TBMTicks (DBMLChannel::*iResult)(); // Measurement specific RBMChannel::Result() implmentation
52 TBMTicks Result(); // Default iResult() implementation
54 TInt Start(RBMChannel::TMode);
56 TInt StartInterruptLatency();
57 virtual void InterruptLatencyIsr(TBMTicks latency);
59 TInt StartKernelPreemptionLatency();
60 static TInt KernelPreemptionLatencyThreadEntry(TAny* ptr);
61 void KernelPreemptionLatencyThread();
63 TInt StartUserPreemptionLatency();
64 TBMTicks UserPreemptionLatencyResult(); // iResult() implementation
66 TInt StartNTimerJitter();
67 TInt RequestNTimerJitterInterrupt(); // iRequestInterrupt() implementation
68 static void NTimerJitterCallBack(TAny*);
70 TInt StartTimerStampOverhead();
71 TInt RequestTimerStampOverhead(); // iRequestInterrupt() implementation
73 TInt SetAbsPrioirty(TInt aThreadHandle, TInt aNewPrio, TInt* aOldPrio);
75 DMutex* iLock; // Shall be acquired by anyone who access the object's writable state.
77 TBool iStarted; // ETrue when a particular sequence of measurements has been started
78 TBool iPendingInterruptRequest; // ETrue when an interrupt has been requested
80 TDynamicDfcQue* iDfcQ;
83 DThread* iKernelThread; // the kernel thread created by some benchmarks
84 DThread* iUserThread; // the user-side thread
85 DThread* iInterruptThread; // the thread signaled by DFC; if non-NULL either iKernelThread or iUserThread
87 NTimer iNTimer; // the timer used in "NTimer jitter" benchmark
88 TBMTicks iOneNTimerTick; // number of high-resolution timer ticks in one NKern tick.
89 TInt iNTimerShotCount; // used in "NTimer jitter" to distinguish between the first and the second shots
92 TBMTicks iTimerPeriod; // period of high-resolution timer in ticks
94 NFastSemaphore* iKernelThreadExitSemaphore;
98 NKern::ThreadEnterCS();
99 Kern::MutexWait(*iLock);
103 Kern::MutexSignal(*iLock);
104 NKern::ThreadLeaveCS();
107 TBMTicks Delta(TBMTicks aT0, TBMTicks aT1)
109 return (aT0 <= aT1) ? (aT1 - aT0) : iTimerPeriod - (aT0 - aT1);
113 _LIT(KBMLChannelLit, "BMLChannel");
115 const TInt DBMLChannel::KBMDfcQThreadPriority = KBMLDDHighPriority;
116 const TInt DBMLChannel::KBMKernelThreadPriority = KBMLDDMidPriority;
120 DECLARE_STANDARD_LDD()
122 // Create a new device
126 return new DBMLDevice;
129 DBMLDevice::DBMLDevice()
135 iVersion = TVersion(1,0,1);
136 iParseMask = KDeviceAllowPhysicalDevice;
139 TInt DBMLDevice::Install()
141 // Install the device driver.
144 TInt r = SetName(&KBMLdName);
148 void DBMLDevice::GetCaps(TDes8&) const
150 // Return the Comm capabilities.
155 TInt DBMLDevice::Create(DLogicalChannelBase*& aChannel)
157 // Create a channel on the device.
161 aChannel = new DBMLChannel;
162 return aChannel ? KErrNone : KErrNoMemory;
165 DBMLChannel::DBMLChannel() :
170 // iStarted = EFalse;
171 // iPendingInterruptRequest = EFalse;
172 // iKernelThread = NULL;
175 TInt DBMLChannel::DoCreate(TInt /*aUnit*/, const TDesC8* /* aInfo*/ , const TVersion& aVer)
177 // Create the channel from the passed info.
181 if (!Kern::QueryVersionSupported(TVersion(1,0,1),aVer))
182 return KErrNotSupported;
183 TInt r = Kern::MutexCreate(iLock, KBMLChannelLit, KMutexOrder);
188 iTimerPeriod = PChannel()->TimerPeriod();
189 // Calculate the number of high-resolution timer ticks in one NKern tick
190 // deviding the number of high-resolution timer ticks in one second by the
191 // number of NKern ticks in one second.
193 iOneNTimerTick = PChannel()->TimerNsToTicks(BMSecondsToNs(1))/NKern::TimerTicks(1000);
197 DBMLChannel::~DBMLChannel()
198 // Called on a channel close. Note that if the PDD channel create failed
199 // then the DoCreate() call will not have been made so don't assume anything
200 // about non-ctor initialisation of members.
205 if (iPendingInterruptRequest)
207 PChannel()->CancelInterrupt();
218 NFastSemaphore exitSemaphore;
219 exitSemaphore.iOwningThread = NKern::CurrentThread();
220 iKernelThreadExitSemaphore = &exitSemaphore;
221 NKern::ThreadRequestSignal(&iKernelThread->iNThread);
222 NKern::FSWait(&exitSemaphore);
226 void DBMLChannel::Dfc(TAny* ptr)
228 DBMLChannel* lCh = (DBMLChannel*) ptr;
229 BM_ASSERT(lCh->iPendingInterruptRequest);
230 BM_ASSERT(lCh->iInterruptThread);
231 NKern::ThreadRequestSignal(&lCh->iInterruptThread->iNThread);
232 lCh->iPendingInterruptRequest = EFalse;
236 // Default DBMLChannel::iRequestInterrupt implementation
238 TInt DBMLChannel::RequestInterrupt()
244 if (iPendingInterruptRequest)
248 iPendingInterruptRequest = ETrue;
249 PChannel()->RequestInterrupt();
254 // Default DBMLChannel::iResult implementation
256 TBMTicks DBMLChannel::Result()
261 void DBMLChannel::Isr(TBMTicks aNow)
264 // Store the ISR entry time and queue a DFC.
271 // "INTERRUPT LATENCY"
275 // A user thread requests an interrupt (RBMChannel::RequestInterrupt()) and waits at User::WaitForAnyRequest()
276 // (RBMChannel::Result()).
277 // When the interrupt occurs DBMLChannel::InterruptLatencyIsr() stores the interrupt latency
278 // provided by LDD, in DBMLChannel::iTime and queues a DFC (DBMLChannel::iDfc, DBMLChannel::Dfc())
279 // which in its turn signals the user thread.
282 TInt DBMLChannel::StartInterruptLatency()
288 TInt r = PChannel()->BindInterrupt((MBMInterruptLatencyIsr*) this);
293 // Use the default iRequestInterrupt() implmentation
294 iRequestInterrupt = &DBMLChannel::RequestInterrupt;
295 // Use the default iResult() implmentation
296 iResult = &DBMLChannel::Result;
297 iInterruptThread = &Kern::CurrentThread();
302 void DBMLChannel::InterruptLatencyIsr(TBMTicks aLatency)
309 // "KERNEL THREAD PREEMPTION LATENCY"
313 // A new kernel thread is created at the beginning of a sequence of measurements
314 // (DBMLChannel::StartKernelPreemptionLatency()). The kernel thread waits at Kern::WaitForAnyRequest()
315 // (DBMLChannel::KernelPreemptionLatencyThread()).
316 // The user thread requests an interrupt (RBMChannel::RequestInterrupt()) and waits at User::WaitForAnyRequest()
317 // (RBMChannel::Result()).
318 // When the interrupt occurs DBMLChannel::Isr() stores the ISR entry time, provided by LDD
319 // in DBMLChannel::iTime and queues a DFC (DBMLChannel::iDfc, DBMLChannel::Dfc()) which, in its turn,
320 // signals the kernel thread.
321 // The kernel thread, when awaken, calculates the latency as the difference between the ISR entry time
322 // and the current time and finally signals the user thread.
325 TInt DBMLChannel::StartKernelPreemptionLatency()
331 TInt r = PChannel()->BindInterrupt((MBMIsr*) this);
337 SThreadCreateInfo info;
338 info.iType = EThreadSupervisor;
339 info.iFunction = DBMLChannel::KernelPreemptionLatencyThreadEntry;
341 info.iSupervisorStack = NULL;
342 info.iSupervisorStackSize = 0;
343 info.iInitialThreadPriority = KBMKernelThreadPriority;
344 info.iName.Set(KBMLChannelLit);
345 info.iTotalSize = sizeof(info);
346 r = Kern::ThreadCreate(info);
351 iKernelThread = (DThread*) info.iHandle;
354 iUserThread = &Kern::CurrentThread();
355 // Use the default iRequestInterrupt() implmentation
356 iRequestInterrupt = &DBMLChannel::RequestInterrupt;
357 // Use the default iResult() implmentation
358 iResult = &DBMLChannel::Result;
359 iInterruptThread = iKernelThread;
362 Kern::ThreadResume(*iKernelThread);
367 TInt DBMLChannel::KernelPreemptionLatencyThreadEntry(TAny* ptr)
369 DBMLChannel* lCh = (DBMLChannel*) ptr;
370 lCh->KernelPreemptionLatencyThread();
375 void DBMLChannel::KernelPreemptionLatencyThread()
379 NKern::WaitForAnyRequest();
381 if(iKernelThreadExitSemaphore)
384 TBMTicks now = PChannel()->TimerStamp();
385 iTime = Delta(iTime, now);
386 BM_ASSERT(iUserThread);
387 NKern::ThreadRequestSignal(&iUserThread->iNThread);
390 NKern::FSSignal(iKernelThreadExitSemaphore);
396 // "USER THREAD PREEMPTION LATENCY"
400 // A user thread requests an interrupt (RBMChannel::RequestInterrupt()) and waits at User::WaitForAnyRequest()
401 // (RBMChannel::Result()).
402 // When the interrupt occurs DBMLChannel::Isr() stores the ISR entry time provided by LDD,
403 // in DBMLChannel::iTime and queues a DFC (DBMLChannel::iDfc, DBMLChannel::Dfc()) which in its turn
404 // signals the user thread.
405 // The user thread, when awaken, immediately re-enters in the LDD, and calculates the latency as
406 // the difference between the ISR entry time and the current time.
409 TInt DBMLChannel::StartUserPreemptionLatency()
415 TInt r = PChannel()->BindInterrupt((MBMIsr*) this);
420 // Default iRequestInterrupt() implmentation
421 iRequestInterrupt = &DBMLChannel::RequestInterrupt;
422 iResult = &DBMLChannel::UserPreemptionLatencyResult;
423 iInterruptThread = &Kern::CurrentThread();
428 TBMTicks DBMLChannel::UserPreemptionLatencyResult()
430 TBMTicks now = PChannel()->TimerStamp();
431 return Delta(iTime, now);
435 // "NTimer PERIOD JITTER"
439 // One measuremnt is done by two consecutive NTimer callbacks.
440 // The first callback stores the current time and the second one calculate the actual period as
441 // the difference between its own current time and the time stored by the first callback.
442 // The difference between this actual period and the theoretical period is considered as the jitter.
445 TInt DBMLChannel::StartNTimerJitter()
451 new (&iNTimer) NTimer(&NTimerJitterCallBack, this);
452 iRequestInterrupt = &DBMLChannel::RequestNTimerJitterInterrupt;
453 // Use the default iResult() implmentation
454 iResult = &DBMLChannel::Result;
455 iInterruptThread = &Kern::CurrentThread();
460 TInt DBMLChannel::RequestNTimerJitterInterrupt()
466 if (iPendingInterruptRequest)
470 iPendingInterruptRequest = ETrue;
471 iNTimerShotCount = 0;
477 void DBMLChannel::NTimerJitterCallBack(TAny* ptr)
479 DBMLChannel* lCh = (DBMLChannel*) ptr;
480 TBMTicks now = lCh->PChannel()->TimerStamp();
481 if (lCh->iNTimerShotCount++ == 0)
484 // This is the first callback: store the time and request another one.
487 lCh->iNTimer.Again(1);
492 // This is the second callback: measure the jitter and schedule a DFC
493 // which in its turn will signal the user thread.
495 lCh->iTime = lCh->Delta(lCh->iTime, now);
504 // To measure the overhead of the high-precision timer read operation we get
505 // two consecutive timestamps through DBMPChannel::TimerStamp() interface.
506 // The difference beween this two values is considered as the measured overhead.
509 TInt DBMLChannel::StartTimerStampOverhead()
515 iRequestInterrupt = &DBMLChannel::RequestTimerStampOverhead;
516 // Use the default iResult() implmentation
517 iResult = &DBMLChannel::Result;
518 iInterruptThread = &Kern::CurrentThread();
523 TInt DBMLChannel::RequestTimerStampOverhead()
525 TBMTicks t1 = PChannel()->TimerStamp();
526 TBMTicks t2 = PChannel()->TimerStamp();
527 iTime = Delta(t1, t2);
528 NKern::ThreadRequestSignal(&iInterruptThread->iNThread);
532 // END OF "GETTING TIMER OVERHEAD"
536 // The implmentation of RBMDriver::SetAbsPrioirty() call.
538 TInt DBMLChannel::SetAbsPrioirty(TInt aThreadHandle, TInt aNewPrio, TInt* aOldPrio)
542 // Under the system lock find the DThread object and increment its ref-count (i.e Open())
544 DThread* thr = (DThread*) Kern::ObjectFromHandle(&Kern::CurrentThread(), aThreadHandle, EThread);
555 // Now it's safe to release the system lock and to work with the object.
557 NKern::ThreadEnterCS();
558 NKern::UnlockSystem();
561 NKern::ThreadLeaveCS();
564 *aOldPrio = thr->iDefaultPriority;
565 Kern::SetThreadPriority(aNewPrio, thr);
567 // Work is done - close the object.
570 NKern::ThreadLeaveCS();
574 _LIT(KBmDfcQName, "BmDfcQ");
577 // Starts a new sequence of measurements.
579 // Only one sequence can be started for any particular DBMLChannel object during its life.
580 // If more than one sequence is required a new DBMLChannel object must be created.
582 TInt DBMLChannel::Start(RBMChannel::TMode aMode)
587 r = Kern::DynamicDfcQCreate(iDfcQ, KBMDfcQThreadPriority, KBmDfcQName);
592 iDfc.SetFunction(Dfc);
597 case RBMChannel::EInterruptLatency:
598 r = StartInterruptLatency();
600 case RBMChannel::EKernelPreemptionLatency:
601 r = StartKernelPreemptionLatency();
603 case RBMChannel::EUserPreemptionLatency:
604 r = StartUserPreemptionLatency();
606 case RBMChannel::ENTimerJitter:
607 r = StartNTimerJitter();
609 case RBMChannel::ETimerStampOverhead:
610 r = StartTimerStampOverhead();
613 r = KErrNotSupported;
623 TInt DBMLChannel::Request(TInt aFunction, TAny* a1, TAny* a2)
628 case RBMChannel::EStart:
630 RBMChannel::TMode mode = (RBMChannel::TMode) (TInt) a1;
636 case RBMChannel::ERequestInterrupt:
639 r = (this->*iRequestInterrupt)();
643 case RBMChannel::EResult:
646 // We don't acquire the lock because:
647 // (1) iResult() typically reads iTime which was written BEFORE to signal the current thread
648 // and therefore BEFORE the current thread comes here.
649 // (2) we really want if possible (i.e. correct!) to avoid the lock acquisition because it can
650 // increase the measurement overhead in the case when we are in a measured path (e.g. user
651 // preemption latency benchmark).
653 TBMTicks ticks = (this->*iResult)();
654 umemput(a1, &ticks, sizeof(ticks));
658 // All below requests do not access writable DBMChannel state and therefore do not require the lock
660 case RBMChannel::ETimerStamp:
662 TBMTicks ticks = PChannel()->TimerStamp();
663 umemput(a1, &ticks, sizeof(ticks));
666 case RBMChannel::ETimerPeriod:
668 TBMTicks ticks = iTimerPeriod;
669 umemput(a1, &ticks, sizeof(ticks));
672 case RBMChannel::ETimerTicksToNs:
675 umemget(&ticks, a1, sizeof(ticks));
676 TBMNs ns = PChannel()->TimerTicksToNs(ticks);
677 umemput(a2, &ns, sizeof(ns));
680 case RBMChannel::ETimerNsToTicks:
683 umemget(&ns, a1, sizeof(ns));
684 TBMTicks ticks = PChannel()->TimerNsToTicks(ns);
685 umemput(a2, &ticks, sizeof(ticks));
688 case RBMChannel::ESetAbsPriority:
692 umemget(&newPrio, a2, sizeof(newPrio));
693 r = SetAbsPrioirty((TInt) a1, newPrio, &oldPrio);
694 umemput(a2, &oldPrio, sizeof(oldPrio));
698 r = KErrNotSupported;