|
1 // Copyright (c) 2003-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". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // e32test\debug\d_context.cpp |
|
15 // Test LDD exercising hardware/software exception hooks |
|
16 // and get/set user context APIs. |
|
17 // |
|
18 // |
|
19 |
|
20 #include "platform.h" |
|
21 #include <kernel/kern_priv.h> |
|
22 #include "kern_test.h" |
|
23 #include "d_context.h" |
|
24 |
|
25 _LIT(KClientPanicCat, "D_CONTEXT"); |
|
26 |
|
27 extern TUint32 SpinInKernel(TBool); |
|
28 |
|
29 DThread* ThreadFromId(TUint aId) |
|
30 { |
|
31 // This is risky because the thread could die on us an the DThread* become invalid. |
|
32 // We are relying on this never happening in our test code. |
|
33 NKern::ThreadEnterCS(); |
|
34 DObjectCon& threads=*Kern::Containers()[EThread]; |
|
35 threads.Wait(); |
|
36 DThread* thread = Kern::ThreadFromId(aId); |
|
37 threads.Signal(); |
|
38 NKern::ThreadLeaveCS(); |
|
39 return thread; |
|
40 } |
|
41 |
|
42 void ModifyContext(TArmRegSet& aContext) |
|
43 { |
|
44 TArmReg* end = (TArmReg*)(&aContext+1); |
|
45 for (TArmReg* p = (TArmReg*)&aContext; p<end; ++p) |
|
46 *p = ~*p; |
|
47 } |
|
48 |
|
49 void DumpContext(TArmRegSet& aContext) |
|
50 { |
|
51 Kern::Printf(" r0 =%08x r1 =%08x r2 =%08x r3 =%08x",aContext.iR0,aContext.iR1,aContext.iR2,aContext.iR3); |
|
52 Kern::Printf(" r4 =%08x r5 =%08x r6 =%08x r7 =%08x",aContext.iR4,aContext.iR5,aContext.iR6,aContext.iR7); |
|
53 Kern::Printf(" r8 =%08x r9 =%08x r10=%08x r11=%08x",aContext.iR8,aContext.iR9,aContext.iR10,aContext.iR11); |
|
54 Kern::Printf(" r12=%08x r13=%08x r14=%08x r15=%08x",aContext.iR12,aContext.iR13,aContext.iR14,aContext.iR15); |
|
55 Kern::Printf(" cpsr=%08x dacr=%08x",aContext.iFlags, aContext.iDacr); |
|
56 } |
|
57 |
|
58 inline TBool IsRegisterAvailable(TInt aReg, TUint32 aMask) |
|
59 { |
|
60 return aMask & (1<<aReg); |
|
61 } |
|
62 |
|
63 TInt CheckSetContext(const TArmRegSet& aSetContext, const TArmRegSet& aContextAfterSet, TUint32 aAvailMask) |
|
64 { |
|
65 Kern::Printf("Checking all available registers have been modified (0%08x)", aAvailMask); |
|
66 |
|
67 const TArmReg* pSet = (const TArmReg*)&aSetContext; |
|
68 const TArmReg* pAfterSet = (const TArmReg*)&aContextAfterSet; |
|
69 |
|
70 for (int i=0; i<=EArmPc; ++i) |
|
71 { |
|
72 if (pAfterSet[i] == 0 && IsRegisterAvailable(i, aAvailMask) && pSet[i] != 0) |
|
73 { |
|
74 Kern::Printf("Register %d not set (expected %08x actual %08x)", i, pSet[i], pAfterSet[i]); |
|
75 return KErrCorrupt; |
|
76 } |
|
77 if (pAfterSet[i] != 0 && ! IsRegisterAvailable(i, aAvailMask)) |
|
78 { |
|
79 Kern::Printf("Register %d incorrectly set (expected %08x actual %08x)", i, 0, pAfterSet[i]); |
|
80 return KErrCorrupt; |
|
81 } |
|
82 } |
|
83 |
|
84 return KErrNone; |
|
85 } |
|
86 |
|
87 ////////////////////////////////////////////////////////////////////////////// |
|
88 |
|
89 class DEventHandler : public DKernelEventHandler |
|
90 { |
|
91 public: |
|
92 DEventHandler(); |
|
93 TInt Create(DLogicalDevice* aDevice); |
|
94 ~DEventHandler(); |
|
95 void Cancel(); |
|
96 private: |
|
97 static TUint EventHandler(TKernelEvent aEvent, TAny* a1, TAny* a2, TAny* aThis); |
|
98 TUint HandleEvent(TKernelEvent aType, TAny* a1, TAny* a2); |
|
99 TBool HandleException(TRequestStatus*& aStatusPtr, TClientRequest*& aRequestPtr); |
|
100 void HandleThreadDeath(); |
|
101 void Cleanup(); |
|
102 private: |
|
103 DThread* iClientThread; |
|
104 DMutex* iLock; // serialise calls to handler |
|
105 public: |
|
106 DLogicalDevice* iDevice; // open reference to LDD for avoiding lifetime issues |
|
107 // software exception fields |
|
108 TExcType iSwExcLastType; |
|
109 TInt* iSwExcCounterPtr; |
|
110 TRequestStatus* iSwExcStatusPtr; |
|
111 TClientRequest* iClientRequestSwExc; |
|
112 // hardware exception fields |
|
113 TRequestStatus* iHwExcStatusPtr; |
|
114 TClientRequest* iClientRequestHwExc; |
|
115 // fields used for both hardware and software exceptions |
|
116 TUint iExcThreadId; |
|
117 TAny* iExcContextPtr; |
|
118 TBool iExcKillThread; |
|
119 // thread death event fields |
|
120 TUint iDeathThreadId; |
|
121 TRequestStatus* iDeathStatusPtr; |
|
122 TAny* iDeathContextPtr; |
|
123 TClientRequest* iClientRequestDeath; |
|
124 }; |
|
125 |
|
126 |
|
127 DEventHandler::DEventHandler() |
|
128 : DKernelEventHandler(EventHandler, this) |
|
129 { |
|
130 } |
|
131 |
|
132 |
|
133 TInt DEventHandler::Create(DLogicalDevice* aDevice) |
|
134 { |
|
135 TInt r; |
|
136 r = aDevice->Open(); |
|
137 if (r != KErrNone) |
|
138 goto error; |
|
139 iDevice = aDevice; |
|
140 iClientThread = &Kern::CurrentThread(); |
|
141 r = Kern::CreateClientRequest(iClientRequestSwExc); |
|
142 if (r != KErrNone) |
|
143 goto error; |
|
144 r = Kern::CreateClientRequest(iClientRequestHwExc); |
|
145 if (r != KErrNone) |
|
146 goto error; |
|
147 r = Kern::CreateClientRequest(iClientRequestDeath); |
|
148 if (r != KErrNone) |
|
149 goto error; |
|
150 r = Kern::MutexCreate(iLock, _L("EventHandlerLock"), KMutexOrdDebug); |
|
151 if (r != KErrNone) |
|
152 goto error; |
|
153 return Add(); |
|
154 error: |
|
155 Cleanup(); |
|
156 return r; |
|
157 } |
|
158 |
|
159 void DEventHandler::Cleanup() |
|
160 { |
|
161 if (iLock) |
|
162 iLock->Close(NULL); |
|
163 if (iDevice) |
|
164 iDevice->Close(NULL); |
|
165 if (iClientRequestSwExc) |
|
166 { |
|
167 Kern::DestroyClientRequest(iClientRequestSwExc); |
|
168 iClientRequestSwExc = NULL; |
|
169 } |
|
170 if (iClientRequestHwExc) |
|
171 { |
|
172 Kern::DestroyClientRequest(iClientRequestHwExc); |
|
173 iClientRequestHwExc = NULL; |
|
174 } |
|
175 if (iClientRequestDeath) |
|
176 { |
|
177 Kern::DestroyClientRequest(iClientRequestDeath); |
|
178 iClientRequestDeath = NULL; |
|
179 } |
|
180 } |
|
181 |
|
182 DEventHandler::~DEventHandler() |
|
183 { |
|
184 Cleanup(); |
|
185 } |
|
186 |
|
187 |
|
188 void DEventHandler::Cancel() |
|
189 { |
|
190 Kern::MutexWait(*iLock); |
|
191 if (iHwExcStatusPtr) |
|
192 { |
|
193 Kern::QueueRequestComplete(iClientThread, iClientRequestHwExc, KErrCancel); |
|
194 iHwExcStatusPtr = NULL; |
|
195 } |
|
196 if (iSwExcStatusPtr) |
|
197 { |
|
198 Kern::QueueRequestComplete(iClientThread, iClientRequestSwExc, KErrCancel); |
|
199 iSwExcStatusPtr = NULL; |
|
200 } |
|
201 Kern::MutexSignal(*iLock); |
|
202 } |
|
203 |
|
204 |
|
205 TUint DEventHandler::EventHandler(TKernelEvent aEvent, TAny* a1, TAny* a2, TAny* aThis) |
|
206 { |
|
207 return ((DEventHandler*)aThis)->HandleEvent(aEvent, a1, a2); |
|
208 } |
|
209 |
|
210 |
|
211 // called in thread CS |
|
212 TUint DEventHandler::HandleEvent(TKernelEvent aType, TAny* a1, TAny* a2) |
|
213 { |
|
214 // Ensure handler always called in thread critical section |
|
215 NThread& nt = Kern::CurrentThread().iNThread; |
|
216 __ASSERT_ALWAYS(nt.iCsCount != 0, (NKern::Lock(),*(TInt*)0xfaece5=0)); |
|
217 |
|
218 TUint r = DKernelEventHandler::ERunNext; |
|
219 |
|
220 switch (aType) |
|
221 { |
|
222 case EEventSwExc: |
|
223 // HACK, EVIL UNSAFE MEMORY ACCESS FOLLOWS... |
|
224 TInt counter; |
|
225 // folowing will kill system if memory is bad (because we're in a critical section) |
|
226 umemget32(&counter, iSwExcCounterPtr, sizeof(TInt*)); |
|
227 ++counter; |
|
228 umemput32(iSwExcCounterPtr, &counter, sizeof(TInt)); |
|
229 |
|
230 Kern::MutexWait(*iLock); |
|
231 iSwExcLastType = (TExcType)(TInt)a1; |
|
232 if (iSwExcStatusPtr) |
|
233 HandleException(iSwExcStatusPtr, iClientRequestSwExc); |
|
234 Kern::MutexSignal(*iLock); |
|
235 break; |
|
236 case EEventHwExc: |
|
237 Kern::MutexWait(*iLock); |
|
238 if (iHwExcStatusPtr) |
|
239 if (HandleException(iHwExcStatusPtr, iClientRequestHwExc)) |
|
240 r |= (TUint)EExcHandled; |
|
241 Kern::MutexSignal(*iLock); |
|
242 break; |
|
243 case EEventKillThread: |
|
244 Kern::MutexWait(*iLock); |
|
245 HandleThreadDeath(); |
|
246 Kern::MutexSignal(*iLock); |
|
247 break; |
|
248 default: |
|
249 // NO-OP |
|
250 break; |
|
251 } |
|
252 |
|
253 return r; |
|
254 } |
|
255 |
|
256 |
|
257 // called in thread CS |
|
258 TBool DEventHandler::HandleException(TRequestStatus*& aStatusPtr, TClientRequest*& aRequestPtr) |
|
259 { |
|
260 DThread& t = Kern::CurrentThread(); |
|
261 TBool handled = EFalse; |
|
262 |
|
263 if (iExcThreadId == t.iId) |
|
264 { |
|
265 Kern::Printf("Trapped exception"); |
|
266 TArmRegSet context1; |
|
267 TUint32 availmask; |
|
268 NKern::ThreadGetUserContext(&t.iNThread, &context1, availmask); |
|
269 XTRAPD(r, XT_DEFAULT, umemput(iExcContextPtr, &context1, sizeof(context1))); |
|
270 |
|
271 if (r == KErrNone) |
|
272 { |
|
273 if (iExcKillThread) |
|
274 { |
|
275 // We must preserve PC for software exceptions because execution |
|
276 // goes back user-side and only then is the thread panicked. |
|
277 TArmReg r15usr = context1.iR15; |
|
278 ModifyContext(context1); |
|
279 context1.iR15 = r15usr; |
|
280 NKern::ThreadSetUserContext(&t.iNThread, &context1); |
|
281 |
|
282 TArmRegSet context2; |
|
283 memclr(&context2, sizeof context2); |
|
284 NKern::ThreadGetUserContext(&t.iNThread, &context2, availmask); |
|
285 r = CheckSetContext(context1, context2, availmask); |
|
286 } |
|
287 else |
|
288 { |
|
289 Kern::ThreadSuspend(t, 1); |
|
290 handled = ETrue; |
|
291 } |
|
292 } |
|
293 Kern::QueueRequestComplete(iClientThread, aRequestPtr, r); |
|
294 aStatusPtr = NULL; |
|
295 } |
|
296 return handled; |
|
297 } |
|
298 |
|
299 |
|
300 // called in thread CS |
|
301 void DEventHandler::HandleThreadDeath() |
|
302 { |
|
303 DThread& t = Kern::CurrentThread(); |
|
304 if (iDeathStatusPtr && iDeathThreadId == t.iId) |
|
305 { |
|
306 Kern::Printf("Trapping thread death: %O", &t); |
|
307 TArmRegSet context; |
|
308 TUint32 unused; |
|
309 NKern::ThreadGetUserContext(&t.iNThread, &context, unused); |
|
310 XTRAPD(r, XT_DEFAULT, umemput(iDeathContextPtr, &context, sizeof(context))); |
|
311 Kern::QueueRequestComplete(iClientThread, iClientRequestDeath, r); |
|
312 iDeathStatusPtr = NULL; |
|
313 } |
|
314 } |
|
315 |
|
316 ////////////////////////////////////////////////////////////////////////////// |
|
317 |
|
318 class DTestChannel : public DLogicalChannelBase |
|
319 { |
|
320 public: |
|
321 virtual ~DTestChannel(); |
|
322 protected: |
|
323 // from DLogicalChannelBase |
|
324 virtual TInt DoCreate(TInt aUnit, const TDesC8* anInfo, const TVersion& aVer); |
|
325 virtual TInt Request(TInt aFunction, TAny* a1, TAny* a2); |
|
326 private: |
|
327 TInt SetAndGetBackContext(TUint aId, TAny* aContext); |
|
328 TInt SetAndGetFullContext(TUint aId, TAny* aContext); |
|
329 void GetContext(TUint aId, TAny* aContext); |
|
330 void GetKernelContext(TUint aId, TAny* aContext); |
|
331 void AddUserCallback(TUint aId, TUserCallbackState aCallback); |
|
332 private: |
|
333 DEventHandler* iHandler; |
|
334 }; |
|
335 |
|
336 |
|
337 // called in thread critical section |
|
338 TInt DTestChannel::DoCreate(TInt /*aUnit*/, const TDesC8* /*aInfo*/, const TVersion& /*aVer*/) |
|
339 { |
|
340 return KErrNone; |
|
341 } |
|
342 |
|
343 // called in thread critical section |
|
344 DTestChannel::~DTestChannel() |
|
345 { |
|
346 if (iHandler) |
|
347 { |
|
348 iHandler->Cancel(); |
|
349 iHandler->Close(); |
|
350 } |
|
351 } |
|
352 |
|
353 TInt DTestChannel::Request(TInt aFunction, TAny* a1, TAny* a2) |
|
354 { |
|
355 RContextLdd::TTrapInfo info; |
|
356 DThread* pT; |
|
357 TInt r = KErrNone; |
|
358 switch (aFunction) |
|
359 { |
|
360 case RContextLdd::EHook: |
|
361 NKern::ThreadEnterCS(); |
|
362 iHandler = new DEventHandler; |
|
363 if (!iHandler) |
|
364 r = KErrNoMemory; |
|
365 else |
|
366 { |
|
367 r = iHandler->Create(iDevice); |
|
368 iHandler->iSwExcCounterPtr = (TInt*)a1; |
|
369 } |
|
370 NKern::ThreadLeaveCS(); |
|
371 break; |
|
372 case RContextLdd::EGetLastExc: |
|
373 r = iHandler->iSwExcLastType; |
|
374 break; |
|
375 case RContextLdd::ETrapNextSwExc: |
|
376 case RContextLdd::ETrapNextHwExc: |
|
377 umemget(&info, a1, sizeof(info)); |
|
378 if (aFunction == RContextLdd::ETrapNextHwExc) |
|
379 { |
|
380 __ASSERT_ALWAYS(iHandler->iSwExcStatusPtr==NULL, Kern::PanicCurrentThread(KClientPanicCat, __LINE__)); |
|
381 iHandler->iHwExcStatusPtr = info.iStatusPtr; |
|
382 r = iHandler->iClientRequestHwExc->SetStatus(iHandler->iHwExcStatusPtr); |
|
383 __ASSERT_ALWAYS(r==KErrNone, Kern::PanicCurrentThread(KClientPanicCat, __LINE__)); |
|
384 } |
|
385 else |
|
386 { |
|
387 __ASSERT_ALWAYS(iHandler->iHwExcStatusPtr==NULL, Kern::PanicCurrentThread(KClientPanicCat, __LINE__)); |
|
388 iHandler->iSwExcStatusPtr = info.iStatusPtr; |
|
389 r = iHandler->iClientRequestSwExc->SetStatus(iHandler->iSwExcStatusPtr); |
|
390 __ASSERT_ALWAYS(r==KErrNone, Kern::PanicCurrentThread(KClientPanicCat, __LINE__)); |
|
391 } |
|
392 iHandler->iExcThreadId = info.iThreadId; |
|
393 iHandler->iExcContextPtr = info.iContextPtr; |
|
394 iHandler->iExcKillThread = info.iKillThread; |
|
395 break; |
|
396 case RContextLdd::ETrapNextDeath: |
|
397 umemget(&info, a1, sizeof(info)); |
|
398 iHandler->iDeathThreadId = info.iThreadId; |
|
399 iHandler->iDeathContextPtr = info.iContextPtr; |
|
400 iHandler->iDeathStatusPtr = info.iStatusPtr; |
|
401 r = iHandler->iClientRequestDeath->SetStatus(iHandler->iDeathStatusPtr); |
|
402 __ASSERT_ALWAYS(r==KErrNone, Kern::PanicCurrentThread(KClientPanicCat, __LINE__)); |
|
403 break; |
|
404 case RContextLdd::EGetContext: |
|
405 GetContext((TUint)a1, a2); |
|
406 break; |
|
407 case RContextLdd::ESetGetContext: |
|
408 r = SetAndGetBackContext((TUint)a1, a2); |
|
409 break; |
|
410 case RContextLdd::ESetGetFullContext: |
|
411 r = SetAndGetFullContext((TUint)a1, a2); |
|
412 break; |
|
413 case RContextLdd::EGetKernelContext: |
|
414 GetKernelContext((TUint)a1, a2); |
|
415 break; |
|
416 case RContextLdd::ESpinInKernel: |
|
417 r = SpinInKernel((TBool)a1); |
|
418 break; |
|
419 case RContextLdd::EAddUserCallback: |
|
420 AddUserCallback((TUint)a1, (TUserCallbackState)(TUint)a2); |
|
421 break; |
|
422 case RContextLdd::EResumeTrappedThread: |
|
423 pT = ThreadFromId((TUint)a1); |
|
424 Kern::ThreadResume(*pT); |
|
425 break; |
|
426 default: |
|
427 Kern::PanicCurrentThread(KClientPanicCat, __LINE__); |
|
428 break; |
|
429 } |
|
430 return r; |
|
431 } |
|
432 |
|
433 |
|
434 TInt DTestChannel::SetAndGetBackContext(TUint aId, TAny* aContext) |
|
435 { |
|
436 DThread* pT = ThreadFromId(aId); |
|
437 __ASSERT_ALWAYS(pT!=NULL, Kern::PanicCurrentThread(KClientPanicCat, __LINE__)); |
|
438 |
|
439 // The following code assumes the inspected thread does not run between the |
|
440 // set context and get context call. |
|
441 |
|
442 TArmRegSet context1; |
|
443 umemget(&context1, aContext, sizeof context1); |
|
444 NKern::ThreadSetUserContext(&pT->iNThread, &context1); |
|
445 |
|
446 TArmRegSet context2; |
|
447 memclr(&context2, sizeof context2); |
|
448 TUint32 availmask; |
|
449 NKern::ThreadGetUserContext(&pT->iNThread, &context2, availmask); |
|
450 umemput(aContext, &context2, sizeof context2); |
|
451 |
|
452 return CheckSetContext(context1, context2, availmask); |
|
453 } |
|
454 |
|
455 #ifdef __SMP__ |
|
456 class NKTest |
|
457 { |
|
458 public: |
|
459 static TBool IsDead(NThreadBase* aT) |
|
460 { return aT->iWaitState.ThreadIsDead(); } |
|
461 }; |
|
462 #endif |
|
463 |
|
464 TInt DTestChannel::SetAndGetFullContext(TUint aId, TAny* aContext) |
|
465 { |
|
466 DThread* pT = ThreadFromId(aId); |
|
467 __ASSERT_ALWAYS(pT != NULL, Kern::PanicCurrentThread(KClientPanicCat, __LINE__)); |
|
468 TBool dead; |
|
469 #ifdef __SMP__ |
|
470 dead = NKTest::IsDead(&pT->iNThread); |
|
471 #else |
|
472 dead = pT->iNThread.iSpare1 == NThreadBase::EDead; |
|
473 #endif |
|
474 __ASSERT_ALWAYS(dead, Kern::PanicCurrentThread(KClientPanicCat, __LINE__)); |
|
475 |
|
476 TArmFullContext contextData; |
|
477 umemget(&contextData, aContext, sizeof contextData); |
|
478 NKern::ThreadSetUserContext(&pT->iNThread, &contextData.iUserContext); |
|
479 |
|
480 NKern::ThreadGetUserContext(&pT->iNThread, &contextData.iUserContext, contextData.iUserAvail); |
|
481 NKern::ThreadGetSystemContext(&pT->iNThread, &contextData.iSystemContext, contextData.iSystemAvail); |
|
482 |
|
483 umemput(aContext, &contextData, sizeof contextData); |
|
484 |
|
485 return KErrNone; |
|
486 } |
|
487 |
|
488 void DTestChannel::GetContext(TUint aId, TAny* aContext) |
|
489 { |
|
490 DThread* pT = ThreadFromId(aId); |
|
491 __ASSERT_ALWAYS(pT!=NULL, Kern::PanicCurrentThread(KClientPanicCat, __LINE__)); |
|
492 |
|
493 TArmRegSet context; |
|
494 memclr(&context, sizeof context); |
|
495 TUint32 unused; |
|
496 NKern::ThreadGetUserContext(&pT->iNThread, &context, unused); |
|
497 umemput(aContext, &context, sizeof context); |
|
498 } |
|
499 |
|
500 void DTestChannel::GetKernelContext(TUint aId, TAny* aContext) |
|
501 { |
|
502 DThread* pT = ThreadFromId(aId); |
|
503 __ASSERT_ALWAYS(pT!=NULL, Kern::PanicCurrentThread(KClientPanicCat, __LINE__)); |
|
504 |
|
505 TArmRegSet context; |
|
506 memclr(&context, sizeof context); |
|
507 TUint32 unused; |
|
508 NKern::ThreadGetSystemContext(&pT->iNThread, &context, unused); |
|
509 umemput(aContext, &context, sizeof context); |
|
510 } |
|
511 |
|
512 void DTestChannel::AddUserCallback(TUint aId, TUserCallbackState aCallback) |
|
513 { |
|
514 DThread* pT = ThreadFromId(aId); |
|
515 __ASSERT_ALWAYS(pT!=NULL, Kern::PanicCurrentThread(KClientPanicCat, __LINE__)); |
|
516 |
|
517 switch (aCallback) |
|
518 { |
|
519 case ESpinningCallback: |
|
520 KernTest::Test(KernTest::EUserModeCallbackSpin, pT); |
|
521 break; |
|
522 case ESleepingCallback: |
|
523 KernTest::Test(KernTest::EUserModeCallbackSleep, pT); |
|
524 break; |
|
525 default: |
|
526 Kern::PanicCurrentThread(KClientPanicCat, __LINE__); |
|
527 } |
|
528 } |
|
529 |
|
530 ////////////////////////////////////////////////////////////////////////////// |
|
531 |
|
532 class DTestFactory : public DLogicalDevice |
|
533 { |
|
534 public: |
|
535 DTestFactory(); |
|
536 // from DLogicalDevice |
|
537 virtual TInt Install(); |
|
538 virtual void GetCaps(TDes8& aDes) const; |
|
539 virtual TInt Create(DLogicalChannelBase*& aChannel); |
|
540 }; |
|
541 |
|
542 DTestFactory::DTestFactory() |
|
543 { |
|
544 iVersion = RContextLdd::Version(); |
|
545 // iParseMask = 0; // no unit, no info, no PDD |
|
546 // iUnitsMask = 0; // only one thing |
|
547 } |
|
548 |
|
549 TInt DTestFactory::Create(DLogicalChannelBase*& aChannel) |
|
550 { |
|
551 aChannel=new DTestChannel; |
|
552 return aChannel ? KErrNone : KErrNoMemory; |
|
553 } |
|
554 |
|
555 TInt DTestFactory::Install() |
|
556 { |
|
557 return SetName(&KTestLddName); |
|
558 } |
|
559 |
|
560 void DTestFactory::GetCaps(TDes8& /*aDes*/) const |
|
561 { |
|
562 } |
|
563 |
|
564 ////////////////////////////////////////////////////////////////////////////// |
|
565 |
|
566 DECLARE_STANDARD_LDD() |
|
567 { |
|
568 return new DTestFactory; |
|
569 } |