1 // Copyright (c) 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 // This is a test for the PCI driver, so far implemented only on the |
|
15 // Naviengine platform. It aims to test: |
|
16 // -That known values of data in config and memory space, on a given |
|
17 // device can be read as expected. |
|
18 // -That data can be written and modified in config and memory space |
|
19 // -PCI memory buffers mapped or allocated by the PCI driver work as |
|
20 // expected. These are |
|
21 // -DChunk created by PCI driver and accessible from PCI |
|
22 // -DPlatHwChunk created by PCI driver and accessible from PCI |
|
23 // -DChunk created externally, then mapped in to PCI memory space |
|
24 // There are tests to: |
|
25 // - Create and close each buffer. Heap checking ensures proper |
|
26 // cleanup |
|
27 // - Create and close multiple buffers from multiple threads. |
|
28 // This is an SMP focused test to check that the implementation |
|
29 // of the chunk manager and allocator in the driver are thread |
|
30 // safe. The tests should pass without triggering any assertions in |
|
31 // the driver's invariance checks. |
|
32 // - Write to buffers from software, and read back via the |
|
33 // system to PCI window, and vice-versa -- a loop-back test. |
|
34 // This checks that PCI buffers are indeed accessible to PCI devices. |
|
35 // |
|
36 // The tests require several pieces of PSL specific information: |
|
37 // - A TPciDevice containing the vendor and device IDs of a PCI device |
|
38 // to use for testing. |
|
39 // - TAddrSpaceTests which identify regions of a device's config and |
|
40 // memory space with known values, or which are known to be writable. |
|
41 // |
|
42 // The test driver grants access to the PCI API with the following |
|
43 // constructs: |
|
44 // - TUserConfigSpace and TUserMemorySpace, derived from TUserPciSpace, |
|
45 // which are user side equivalents of kernel-side objects allowing |
|
46 // accesses of different sizes to a PCI device's config space or |
|
47 // memory space. |
|
48 // - RPciChunk which is derived from and RChunk and corresponds to |
|
49 // a kernel-side DChunk, which in turn corresponds to a PCI chunk or |
|
50 // buffer. The test driver uses these for all PCI chunk types (a |
|
51 // "wrapper" DChunk is used to map the memory of a PCI DPlatHwChunk |
|
52 // to user side). |
|
53 // |
|
54 // Known Issues: |
|
55 // The test driver d_pci is intended to be platform independent but |
|
56 // for now still contains some PSL specific information .eg the test |
|
57 // info structure (which should really be passed up from the PSL) and |
|
58 // the address and size of the system to pci window. For now the |
|
59 // test driver code will remain in the Naviengine baseport directory. |
|
60 // If the PCI driver is ever ported to a new platform this can be |
|
61 // rectified. |
|
62 // |
|
63 // |
|
64 // |
|
65 #include <e32std.h> |
|
66 #define __E32TEST_EXTENSION__ |
|
67 #include <e32test.h> |
|
68 #include "t_pci.h" |
|
69 #include <assp/naviengine/pci.h> |
|
70 |
|
71 #define __E32TEST_EXTENSION__ |
|
72 #include <e32test.h> |
|
73 #include <e32svr.h> |
|
74 #include <e32des8.h> |
|
75 #include <e32des8_private.h> |
|
76 #include <e32cmn.h> |
|
77 #include <e32cmn_private.h> |
|
78 #include <e32std.h> |
|
79 #include <e32std_private.h> |
|
80 |
|
81 |
|
82 _LIT(KPciPanicCat, "test_thread.h"); |
|
83 |
|
84 static const TInt KPciHeapSize=0x2000; |
|
85 |
|
86 enum TPciPanicCode |
|
87 { |
|
88 EThreadCreateFailed |
|
89 }; |
|
90 |
|
91 /** |
|
92 A utility class for running functions in other threads/processes |
|
93 */ |
|
94 class TTestRemote |
|
95 { |
|
96 public: |
|
97 virtual TInt WaitForExitL() = 0; |
|
98 virtual ~TTestRemote() |
|
99 {} |
|
100 |
|
101 virtual void Rendezvous(TRequestStatus& aStatus) = 0; |
|
102 |
|
103 protected: |
|
104 TTestRemote() |
|
105 {} |
|
106 |
|
107 static TInt RunFunctor(TAny* aFunctor) |
|
108 { |
|
109 TFunctor& functor = *(TFunctor*)aFunctor; |
|
110 functor(); |
|
111 return KErrNone; |
|
112 } |
|
113 |
|
114 TRequestStatus iLogonStatus; |
|
115 static TInt iCount; |
|
116 }; |
|
117 TInt TTestRemote::iCount=0; |
|
118 |
|
119 class TTestThread : public TTestRemote |
|
120 { |
|
121 public: |
|
122 TTestThread(const TDesC& aName, TThreadFunction aFn, TAny* aData, TBool aAutoResume=ETrue) |
|
123 { |
|
124 Init(aName, aFn, aData, aAutoResume); |
|
125 } |
|
126 |
|
127 /** |
|
128 Run aFunctor in another thread |
|
129 */ |
|
130 TTestThread(const TDesC& aName, TFunctor& aFunctor, TBool aAutoResume=ETrue) |
|
131 { |
|
132 Init(aName, RunFunctor, &aFunctor, aAutoResume); |
|
133 } |
|
134 |
|
135 ~TTestThread() |
|
136 { |
|
137 //RTest::CloseHandleAndWaitForDestruction(iThread); |
|
138 iThread.Close(); |
|
139 } |
|
140 |
|
141 void Resume() |
|
142 { |
|
143 iThread.Resume(); |
|
144 } |
|
145 |
|
146 /** |
|
147 If thread exited normally, return its return code |
|
148 Otherwise, leave with exit reason |
|
149 */ |
|
150 virtual TInt WaitForExitL() |
|
151 { |
|
152 User::WaitForRequest(iLogonStatus); |
|
153 const TInt exitType = iThread.ExitType(); |
|
154 const TInt exitReason = iThread.ExitReason(); |
|
155 |
|
156 __ASSERT_ALWAYS(exitType != EExitPending, User::Panic(_L("TTestThread"),0)); |
|
157 |
|
158 if(exitType != EExitKill) |
|
159 User::Leave(exitReason); |
|
160 |
|
161 return exitReason; |
|
162 } |
|
163 |
|
164 virtual void Rendezvous(TRequestStatus& aStatus) |
|
165 { |
|
166 iThread.Rendezvous(aStatus); |
|
167 } |
|
168 |
|
169 private: |
|
170 void Init(const TDesC& aName, TThreadFunction aFn, TAny* aData, TBool aAutoResume) |
|
171 { |
|
172 TKName name(aName); |
|
173 name.AppendFormat(_L("-%d"), iCount++); |
|
174 TInt r=iThread.Create(name, aFn, KDefaultStackSize, KPciHeapSize, KPciHeapSize, aData); |
|
175 if(r!=KErrNone) |
|
176 { |
|
177 RDebug::Printf("RThread::Create failed, code=%d", r); |
|
178 User::Panic(KPciPanicCat, EThreadCreateFailed); |
|
179 } |
|
180 |
|
181 iThread.Logon(iLogonStatus); |
|
182 __ASSERT_ALWAYS(iLogonStatus == KRequestPending, User::Panic(_L("TTestThread"),0)); |
|
183 |
|
184 if(aAutoResume) |
|
185 iThread.Resume(); |
|
186 } |
|
187 |
|
188 RThread iThread; |
|
189 }; |
|
190 |
|
191 class CTest : public CBase, public TFunctor |
|
192 { |
|
193 public: |
|
194 ~CTest() |
|
195 { |
|
196 iName.Close(); |
|
197 } |
|
198 |
|
199 virtual void operator()() |
|
200 { |
|
201 RTest test(iName); |
|
202 test.Start(iName); |
|
203 for(TInt i=0; i<iIterations; i++) |
|
204 { |
|
205 test.Next(iName); |
|
206 RunTest(); |
|
207 } |
|
208 test.End(); |
|
209 } |
|
210 |
|
211 virtual void RunTest() = 0; |
|
212 |
|
213 virtual CTest* Clone() const = 0; |
|
214 |
|
215 const TDesC& Name() const |
|
216 { |
|
217 return iName; |
|
218 } |
|
219 |
|
220 protected: |
|
221 CTest(const TDesC& aName, TInt aIterations) |
|
222 :iIterations(aIterations) |
|
223 { |
|
224 iName.CreateL(aName); |
|
225 } |
|
226 |
|
227 |
|
228 |
|
229 CTest(const CTest& aOther) |
|
230 :iIterations(aOther.iIterations) |
|
231 { |
|
232 iName.CreateL(aOther.iName); |
|
233 } |
|
234 |
|
235 //It would be useful to have an RTest member, but this can't be |
|
236 //initialised untill the new thread is running as it will refer to |
|
237 //the creating thread |
|
238 RBuf iName; |
|
239 const TInt iIterations; |
|
240 }; |
|
241 |
|
242 /** |
|
243 Make aNumberOfThreads copies of aTest and run |
|
244 each in its own thread |
|
245 |
|
246 @param test Reference to test object |
|
247 @param aTest Referance |
|
248 */ |
|
249 void MultipleTestRun(RTest& test, const CTest& aTest, TInt aNumberOfThreads) |
|
250 { |
|
251 RPointerArray<CTest> testArray; |
|
252 RPointerArray<TTestThread> threadArray; |
|
253 |
|
254 for(TInt i=0; i<aNumberOfThreads; i++) |
|
255 { |
|
256 test.Printf(_L("Create test thread")); |
|
257 CTest* newTest = aTest.Clone(); |
|
258 test_NotNull(newTest); |
|
259 |
|
260 TTestThread* thread = new TTestThread(aTest.Name(), *newTest); |
|
261 test_NotNull(thread); |
|
262 |
|
263 threadArray.AppendL(thread); |
|
264 testArray.AppendL(newTest); |
|
265 } |
|
266 |
|
267 const TInt count = threadArray.Count(); |
|
268 for(TInt j=0; j<count; j++) |
|
269 { |
|
270 TTestThread* thread = threadArray[j]; |
|
271 |
|
272 TInt r = KErrNone; |
|
273 TRAPD(leaveCode, r = thread->WaitForExitL()); |
|
274 if(leaveCode != KErrNone) |
|
275 { |
|
276 test.Printf(_L("Thread %d: Panic code:%d\n"), j, leaveCode); |
|
277 test_KErrNone(leaveCode); |
|
278 } |
|
279 |
|
280 if(r!=KErrNone) |
|
281 { |
|
282 test.Printf(_L("Thread Number %d\n"), j); |
|
283 test_KErrNone(r); |
|
284 } |
|
285 } |
|
286 |
|
287 threadArray.ResetAndDestroy(); |
|
288 threadArray.Close(); |
|
289 |
|
290 testArray.ResetAndDestroy(); |
|
291 testArray.Close(); |
|
292 } |
|
293 |
|
294 class RPci; |
|
295 /** |
|
296 Extends RChunk to hold the PCI address |
|
297 associated with a chunk. |
|
298 */ |
|
299 class RPciChunk: public RChunk |
|
300 { |
|
301 public: |
|
302 TUint PciBase() |
|
303 { |
|
304 return iPciBaseAddr; |
|
305 } |
|
306 |
|
307 /** |
|
308 Return the PCI accessible size |
|
309 */ |
|
310 TInt Size() const |
|
311 { |
|
312 return iPciSize; |
|
313 } |
|
314 |
|
315 private: |
|
316 friend class RPci; |
|
317 TUint iPciBaseAddr; |
|
318 TInt iPciSize; //size of the region mapped into PCI |
|
319 }; |
|
320 |
|
321 typedef TInt (RPci::*ChunkOpenFn)(RPciChunk&, TInt, TRequestStatus*); |
|
322 |
|
323 class RPci : public RBusLogicalChannel |
|
324 { |
|
325 public: |
|
326 TInt Open(); |
|
327 TInt GetTestInfo(TPciTestInfo& aTestInfo); |
|
328 |
|
329 TInt Open(const TPciDevice&); |
|
330 |
|
331 TUint AccessConfigSpace(const TUserConfigSpace& aCs); |
|
332 TUint AccessMemorySpace(const TUserMemorySpace& aMs); |
|
333 TInt OpenPciDChunk(RPciChunk& aPciChunk,TInt aPciChunkSize, TRequestStatus* aStatus=0); |
|
334 TInt OpenPciPlatHwChunk(RPciChunk& aPciHwChunk,TInt aPciChunkSize, TRequestStatus* aStatus=0); |
|
335 TInt OpenPciMappedChunk(RPciChunk& aPciMappedChunk,TInt aPciChunkSize, TRequestStatus* aStatus=0); |
|
336 TInt OpenPciWindowChunk(RChunk& aPciWindowChunk); |
|
337 TInt RunUnitTests(); |
|
338 private: |
|
339 TInt DoOpenPciChunk(RPciChunk& aPciChunk, TInt aPciChunkSize, TPciTestCmd aCmd, TRequestStatus* aStatus); |
|
340 }; |
|
341 |
|
342 inline TInt RPci::Open() |
|
343 { |
|
344 return DoCreate(KPciLddFactory, TVersion(), KNullUnit, NULL, NULL); |
|
345 } |
|
346 |
|
347 inline TInt RPci::Open(const TPciDevice& aDevice) |
|
348 { |
|
349 TPckgC<TPciDevice> devicePkg(aDevice); |
|
350 return DoCreate(KPciLddFactory, TVersion(), KNullUnit, NULL, &devicePkg); |
|
351 } |
|
352 |
|
353 inline TInt RPci::GetTestInfo(TPciTestInfo& aTestInfo) |
|
354 { |
|
355 TPckg<TPciTestInfo> info(aTestInfo); |
|
356 return DoControl(EGetTestInfo, &info); |
|
357 } |
|
358 |
|
359 inline TInt RPci::RunUnitTests() |
|
360 { |
|
361 return DoControl(ERunUnitTests); |
|
362 } |
|
363 |
|
364 TUint RPci::AccessConfigSpace(const TUserConfigSpace& aCs) |
|
365 { |
|
366 TPckgC<TUserConfigSpace> pkg(aCs); |
|
367 return DoControl(EAccessConfigSpace, &pkg); |
|
368 } |
|
369 |
|
370 TUint RPci::AccessMemorySpace(const TUserMemorySpace& aMs) |
|
371 { |
|
372 TPckgC<TUserMemorySpace> pkg(aMs); |
|
373 return DoControl(EAccessMemorySpace, &pkg); |
|
374 } |
|
375 |
|
376 TInt RPci::OpenPciDChunk(RPciChunk& aPciChunk,TInt aPciChunkSize, TRequestStatus* aStatus) |
|
377 { |
|
378 return DoOpenPciChunk(aPciChunk, aPciChunkSize, EOpenPciDChunk, aStatus); |
|
379 } |
|
380 |
|
381 TInt RPci::OpenPciPlatHwChunk(RPciChunk& aPciHwChunk,TInt aPciChunkSize, TRequestStatus* aStatus) |
|
382 { |
|
383 return DoOpenPciChunk(aPciHwChunk, aPciChunkSize, EOpenPciPlatHwChunk, aStatus); |
|
384 } |
|
385 |
|
386 TInt RPci::OpenPciMappedChunk(RPciChunk& aPciMappedChunk,TInt aPciChunkSize, TRequestStatus* aStatus) |
|
387 { |
|
388 return DoOpenPciChunk(aPciMappedChunk, aPciChunkSize, EOpenPciMappedChunk, aStatus); |
|
389 } |
|
390 |
|
391 TInt RPci::OpenPciWindowChunk(RChunk& aPciWindowChunk) |
|
392 { |
|
393 TUint chunkHandle = DoControl(EOpenPciWindowChunk); |
|
394 return aPciWindowChunk.SetReturnedHandle(chunkHandle); |
|
395 } |
|
396 |
|
397 TInt RPci::DoOpenPciChunk(RPciChunk& aPciChunk, TInt aPciChunkSize, TPciTestCmd aCmd, TRequestStatus* aStatus) |
|
398 { |
|
399 const TInt constPciChunkSize = aPciChunkSize; |
|
400 TPciChunkCreateInfo info(constPciChunkSize, aPciChunk.iPciBaseAddr, aStatus); |
|
401 TPckgC<TPciChunkCreateInfo> pkg(info); |
|
402 |
|
403 TUint chunkHandle = DoControl(aCmd, &pkg); |
|
404 |
|
405 const TInt r = aPciChunk.SetReturnedHandle(chunkHandle); |
|
406 if(r == KErrNone) |
|
407 { |
|
408 aPciChunk.iPciSize = constPciChunkSize; |
|
409 } |
|
410 return r; |
|
411 } |
|
412 |
|
413 TUserPciSpace::TUserPciSpace(RPci& aPci) |
|
414 :iPci(&aPci) |
|
415 {} |
|
416 |
|
417 TUserConfigSpace::TUserConfigSpace(RPci& aPci) |
|
418 :TUserPciSpace(aPci) |
|
419 {} |
|
420 |
|
421 TUint TUserConfigSpace::Call() |
|
422 { |
|
423 return iPci->AccessConfigSpace(*this); |
|
424 } |
|
425 |
|
426 TUserPciSpace* TUserConfigSpace::Clone() const |
|
427 { |
|
428 return new TUserConfigSpace(*this); |
|
429 } |
|
430 |
|
431 TUserMemorySpace::TUserMemorySpace(RPci& aPci, TInt aBarIndex) |
|
432 :TUserPciSpace(aPci), iBarIndex(aBarIndex) |
|
433 {} |
|
434 |
|
435 TUint TUserMemorySpace::Call() |
|
436 { |
|
437 return iPci->AccessMemorySpace(*this); |
|
438 } |
|
439 |
|
440 TUserPciSpace* TUserMemorySpace::Clone() const |
|
441 { |
|
442 return new TUserMemorySpace(*this); |
|
443 } |
|
444 |
|
445 /** |
|
446 Test address allocator |
|
447 */ |
|
448 TInt TestRunPciUnitTest(RPci& pci) |
|
449 { |
|
450 return pci.RunUnitTests(); |
|
451 } |
|
452 |
|
453 |
|
454 /** |
|
455 Read from a defined address in memory or config space, compare against expected values. |
|
456 8,16, and 32 bit accesses performed. |
|
457 |
|
458 @param aSpace Object gving access to either the config or memory space of a PCI device |
|
459 @param aInfo Contains the address and expected value of a dword |
|
460 */ |
|
461 void TestReadAddressSpace(TUserPciSpace& aSpace, const TPciTestInfo::TAddrSpaceTest& aInfo, RTest& test, TBool aVerbose=EFalse) |
|
462 { |
|
463 const TUint os = aInfo.iOffset; |
|
464 //Iterate over different widths, and possible |
|
465 //subfields of 32 bit word |
|
466 for(TInt bitWidth=32; bitWidth>=8; bitWidth>>=1) |
|
467 { |
|
468 const TInt numberOfFields = (32/bitWidth); |
|
469 for(TInt i=0; i< numberOfFields; i++) |
|
470 { |
|
471 const TInt extraByteOffset = i * (bitWidth >> 3); |
|
472 const TInt byteOffset = os + extraByteOffset; |
|
473 if(aVerbose) |
|
474 test.Printf(_L("Access bitWidth=%d byte offset=%d\n"), bitWidth, byteOffset); |
|
475 |
|
476 const TUint expected = aInfo.Expected(bitWidth, byteOffset); |
|
477 const TUint read = aSpace.Read(bitWidth, byteOffset); |
|
478 if(aVerbose) |
|
479 test.Printf(_L("expect 0x%08x, read 0x%08x\n"), expected, read); |
|
480 test_Equal(expected, read); |
|
481 } |
|
482 } |
|
483 } |
|
484 |
|
485 /** |
|
486 Verify writes and modifications to a defined address in memory or config space. 8,16, and 32 bit |
|
487 accesses performed. |
|
488 |
|
489 @param aSpace Object gving access to either the config or memory space of a PCI device |
|
490 @param aInfo Contains the address of a (at least partially) writable dword |
|
491 */ |
|
492 void TestWriteAddressSpace(TUserPciSpace& aSpace, TPciTestInfo::TAddrSpaceTest& aInfo, RTest& test, TBool aVerbose=EFalse) |
|
493 { |
|
494 const TUint original = aSpace.Read(32, aInfo.iOffset); |
|
495 const TUint os = aInfo.iOffset; |
|
496 TUint mask = ~aInfo.iReadOnlyMask; |
|
497 |
|
498 //The pattern will be truncated when used with bit widths |
|
499 //less than 32. |
|
500 const TUint initPattern = 0xFFFFFFFF; |
|
501 |
|
502 for(TInt bitWidth=32; bitWidth>=8; bitWidth>>=1) |
|
503 { |
|
504 const TUint pattern = initPattern >> (32-bitWidth); |
|
505 const TInt numberOfFields = (32/bitWidth); |
|
506 for(TInt i=0; i< numberOfFields; i++) |
|
507 { |
|
508 const TInt extraByteOffset = i * (bitWidth >> 3); |
|
509 const TInt byteOffset = os + extraByteOffset; |
|
510 if(aVerbose) |
|
511 test.Printf(_L("Access bitWidth=%d byte offset=%d\n"), bitWidth, byteOffset); |
|
512 //the full dword we expect |
|
513 //currently assume that the unwritable bits will be 0 |
|
514 const TUint writeExpect = (pattern << (bitWidth * i) ) & mask; |
|
515 const TUint clearExpect = 0; |
|
516 |
|
517 //do write followed by clear |
|
518 const TUint expect[] = {writeExpect, clearExpect}; |
|
519 const TUint write[] = {pattern, 0}; |
|
520 for(TInt n = 0; n < 2; n++) |
|
521 { |
|
522 aSpace.Write(bitWidth, byteOffset, write[n]); |
|
523 TUint result = aSpace.Read(32, os); |
|
524 |
|
525 if(aVerbose) |
|
526 test.Printf(_L("wrote 0x%08x, expect 0x%08x, read 0x%08x\n"), |
|
527 write[n], expect[n], result); |
|
528 test_Equal(expect[n], result); |
|
529 } |
|
530 |
|
531 //test Modify calls. Set then clear pattern |
|
532 TUint set[] = {pattern, 0}; |
|
533 TUint clear[] = {0, pattern}; |
|
534 |
|
535 for(TInt m = 0; m < 2; m++) |
|
536 { |
|
537 aSpace.Modify(bitWidth, byteOffset, clear[m], set[m]); |
|
538 TUint result = aSpace.Read(32, os); |
|
539 |
|
540 if(aVerbose) |
|
541 test.Printf(_L("clear 0x%08x, set 0x%08x, expect 0x%08x, read 0x%08x\n"), clear[m], set[m], expect[m], result); |
|
542 test_Equal(expect[m], result); |
|
543 } |
|
544 } |
|
545 } |
|
546 |
|
547 //restore orginal value or we will not be able to access device |
|
548 aSpace.Write(32, os, original); |
|
549 } |
|
550 |
|
551 |
|
552 /** |
|
553 Verify that a PCI DChunk can be opened and closed from user side |
|
554 |
|
555 @param pci The RPci object to use |
|
556 @param test The RTest object to use |
|
557 @param aPciChunkSize The size of the DChunk which would be created |
|
558 */ |
|
559 void TestOpenAndCloseDChunk(RPci& pci,RTest& test,TInt aPciChunkSize) |
|
560 { |
|
561 RPciChunk testPciDChunk; |
|
562 |
|
563 // Create and open Chunk |
|
564 TRequestStatus status; |
|
565 TInt r = pci.OpenPciDChunk(testPciDChunk,aPciChunkSize, &status); |
|
566 test_KErrNone(r); |
|
567 |
|
568 test(testPciDChunk.IsWritable()); |
|
569 test(testPciDChunk.IsReadable()); |
|
570 |
|
571 test.Printf(_L("PCI Chunk base = 0x%08x\n"), testPciDChunk.Base()); |
|
572 test.Printf(_L("PCI Chunk size = %d\n"), testPciDChunk.Size()); |
|
573 test.Printf(_L("PCI Address = 0x%08x\n"), testPciDChunk.PciBase()); |
|
574 |
|
575 //Close Chunk |
|
576 test.Next(_L("Close PCI Chunk handle")); |
|
577 |
|
578 RTest::CloseHandleAndWaitForDestruction(testPciDChunk); |
|
579 User::WaitForRequest(status); |
|
580 } |
|
581 |
|
582 /** |
|
583 Verify that a PCI PlatHwChunk can be opened and closed from user side |
|
584 |
|
585 |
|
586 @param pci The RPci object to use |
|
587 @param test The RTest object to use |
|
588 @param aPciChunkSize The size of the PlatHwChunk which would be created |
|
589 */ |
|
590 void TestOpenAndClosePciPlatHwChunk(RPci& pci,RTest& test,TInt aPciChunkSize) |
|
591 { |
|
592 RPciChunk testPciPlatHwChunk; |
|
593 |
|
594 // Create and open Chunk |
|
595 TRequestStatus status; |
|
596 TInt r = pci.OpenPciPlatHwChunk(testPciPlatHwChunk,aPciChunkSize, &status); |
|
597 test_KErrNone(r); |
|
598 |
|
599 test(testPciPlatHwChunk.IsWritable()); |
|
600 test(testPciPlatHwChunk.IsReadable()); |
|
601 |
|
602 test.Printf(_L("PCI Chunk base = 0x%08x\n"), testPciPlatHwChunk.Base()); |
|
603 test.Printf(_L("PCI Chunk size = %d\n"), testPciPlatHwChunk.Size()); |
|
604 test.Printf(_L("PCI Address = 0x%08x\n"), testPciPlatHwChunk.PciBase()); |
|
605 |
|
606 //Close Chunk |
|
607 testPciPlatHwChunk.Close(); |
|
608 User::WaitForRequest(status); |
|
609 test.Next(_L("Closed PCI PlatHwChunk handle")); |
|
610 } |
|
611 |
|
612 /** |
|
613 Verify that pci-mapped DChunk can be opended and closed form user side |
|
614 |
|
615 @param pci The RPci object to use |
|
616 @param test The RTest object to use |
|
617 @param aPciChunkSize The size of the pci-mapped DChunk which would be created |
|
618 */ |
|
619 void TestPciMapppedChunk(RPci& pci,RTest& test,TInt aPciChunkSize) |
|
620 { |
|
621 RPciChunk testPciMappedChunk; |
|
622 |
|
623 // Create and open Chunk |
|
624 TRequestStatus status; |
|
625 TInt r = pci.OpenPciMappedChunk(testPciMappedChunk,aPciChunkSize, &status); |
|
626 test_KErrNone(r); |
|
627 |
|
628 test(testPciMappedChunk.IsWritable()); |
|
629 test(testPciMappedChunk.IsReadable()); |
|
630 |
|
631 test.Printf(_L("PCI Chunk base = 0x%08x\n"), testPciMappedChunk.Base()); |
|
632 test.Printf(_L("PCI Chunk size = %d\n"), testPciMappedChunk.Size()); |
|
633 test.Printf(_L("PCI Address = 0x%08x\n"), testPciMappedChunk.PciBase()); |
|
634 |
|
635 //Close Chunk |
|
636 testPciMappedChunk.Close(); |
|
637 User::WaitForRequest(status); |
|
638 test.Next(_L("Closed PCI Mapped Chunk handle")); |
|
639 } |
|
640 |
|
641 /** |
|
642 Verify that an RChunk can be open to grant access to the internal PCI window from the user side |
|
643 |
|
644 @param pci The RPci object to use |
|
645 @param test The RTest object to use |
|
646 */ |
|
647 void TestPciWindowChunk(RPci& pci,RTest& test) |
|
648 { |
|
649 RChunk testPciWindowChunk; |
|
650 |
|
651 // Create and open DChunk |
|
652 TInt r = pci.OpenPciWindowChunk(testPciWindowChunk); |
|
653 test_KErrNone(r); |
|
654 |
|
655 test(testPciWindowChunk.IsWritable()); |
|
656 test(testPciWindowChunk.IsReadable()); |
|
657 |
|
658 test.Printf(_L("PCI Window Chunk base = 0x%08x\n"), testPciWindowChunk.Base()); |
|
659 test.Printf(_L("PCI Window Chunk size = %d\n"), testPciWindowChunk.Size()); |
|
660 |
|
661 //Close Chunk |
|
662 testPciWindowChunk.Close(); |
|
663 test.Next(_L("Closed PCI Window Chunk handle")); |
|
664 } |
|
665 |
|
666 |
|
667 class CPciTest : public CTest |
|
668 { |
|
669 protected: |
|
670 CPciTest(const TDesC& aName, TInt aIterations, RPci& aDevice) |
|
671 : CTest(aName, aIterations), iDevice(aDevice) |
|
672 {} |
|
673 |
|
674 RPci iDevice; |
|
675 }; |
|
676 |
|
677 /** |
|
678 Each instance of test will open a chunk, using the function specified in |
|
679 the template argument, FUNC. |
|
680 |
|
681 The total number of chunks that can be opened by all instances is limited |
|
682 by iMaxCount. |
|
683 |
|
684 All intances of the test will hold their chunk open until iMaxCount has |
|
685 been reached. |
|
686 */ |
|
687 template<ChunkOpenFn FUNC> |
|
688 class CPciOpenChunkTest : public CPciTest |
|
689 { |
|
690 public: |
|
691 CPciOpenChunkTest(const TDesC& aName, TInt aIterations, RPci& aDevice, |
|
692 RSemaphore aSemOpen, RSemaphore aSemClose, RFastLock aLock, TInt aMaxCount) |
|
693 :CPciTest(aName, aIterations, aDevice), |
|
694 iSemOpen(aSemOpen), iSemClose(aSemClose), iLock(aLock), iMaxCount(aMaxCount) |
|
695 { |
|
696 } |
|
697 |
|
698 virtual void RunTest() |
|
699 { |
|
700 RTest test(iName); |
|
701 RPciChunk chunk; |
|
702 |
|
703 iSemOpen.Wait(); |
|
704 TRequestStatus status; |
|
705 const TInt chunkSize = 0x400; |
|
706 //open chunk by calling FUNC |
|
707 TInt r = ((iDevice).*(FUNC))(chunk, chunkSize, &status); |
|
708 test_KErrNone(r); |
|
709 |
|
710 iLock.Wait(); |
|
711 iOpenCount++; |
|
712 test.Printf(_L("Opened chunk %d\n"), iOpenCount); |
|
713 if(iOpenCount == iMaxCount) |
|
714 { |
|
715 test.Printf(_L("Opened=%d, max=%d: Allow chunks to close\n"), iOpenCount, iMaxCount); |
|
716 //release all waiting threads |
|
717 //plus 1 preincrement so this |
|
718 //thread also passes |
|
719 iSemClose.Signal(iOpenCount); |
|
720 iOpenCount = 0; |
|
721 } |
|
722 iLock.Signal(); |
|
723 |
|
724 |
|
725 iSemClose.Wait(); |
|
726 chunk.Close(); |
|
727 User::WaitForRequest(status); |
|
728 |
|
729 // permit another chunk to be opened |
|
730 iSemOpen.Signal(); |
|
731 test.Close(); |
|
732 } |
|
733 |
|
734 virtual CTest* Clone() const |
|
735 { |
|
736 //make shallow copy |
|
737 return new CPciOpenChunkTest(*this); |
|
738 } |
|
739 |
|
740 |
|
741 private: |
|
742 RSemaphore& iSemOpen; ///!< Represents the number of available PCI mappings |
|
743 RSemaphore& iSemClose; ///!< Represents the number of threads waiting to close their chunk |
|
744 RFastLock& iLock; |
|
745 static TInt iOpenCount; |
|
746 const TInt iMaxCount; |
|
747 }; |
|
748 |
|
749 template<ChunkOpenFn FUNC> |
|
750 TInt CPciOpenChunkTest<FUNC>::iOpenCount = 0; |
|
751 |
|
752 |
|
753 /** |
|
754 Test which will perform various reads from a PCI address |
|
755 space (config or memory) and confirm that values are read |
|
756 as expected |
|
757 */ |
|
758 class CPciAddressSpaceRead : public CPciTest |
|
759 { |
|
760 public: |
|
761 CPciAddressSpaceRead(const TDesC& aName, TInt aIterations, RPci& aDevice, |
|
762 const TUserPciSpace& aSpace, const TPciTestInfo::TAddrSpaceTest& aInfo) |
|
763 :CPciTest(aName, aIterations, aDevice), |
|
764 iAddressSpace(aSpace.Clone()), iSpaceTestInfo(aInfo) |
|
765 { |
|
766 } |
|
767 |
|
768 CPciAddressSpaceRead(const CPciAddressSpaceRead& aOther) |
|
769 :CPciTest(aOther)/* TODO-REVIEW have object-sliced aOther - is this ok?*/, |
|
770 iAddressSpace(aOther.iAddressSpace->Clone()), iSpaceTestInfo(aOther.iSpaceTestInfo) |
|
771 { |
|
772 } |
|
773 |
|
774 virtual ~CPciAddressSpaceRead() |
|
775 { |
|
776 delete iAddressSpace; |
|
777 } |
|
778 |
|
779 virtual void RunTest() |
|
780 { |
|
781 __UHEAP_MARK; |
|
782 RTest test(iName); |
|
783 TestReadAddressSpace(*iAddressSpace, iSpaceTestInfo, test); |
|
784 test.Close(); |
|
785 __UHEAP_MARKEND; |
|
786 } |
|
787 |
|
788 virtual CTest* Clone() const |
|
789 { |
|
790 //make shallow copy |
|
791 return new CPciAddressSpaceRead(*this); |
|
792 } |
|
793 |
|
794 private: |
|
795 TUserPciSpace* iAddressSpace; |
|
796 const TPciTestInfo::TAddrSpaceTest& iSpaceTestInfo; |
|
797 }; |
|
798 |
|
799 /** |
|
800 For aBuffer, test writing to it then reading back from aWindow |
|
801 then write via window and read back from chunk |
|
802 |
|
803 @param test The RTest object to use |
|
804 @param aBuffer RChunk corresponding to a PCI accessible buffer |
|
805 @param aWindow RChunk coressponding an appropriate System-to-PCI memory window |
|
806 It is presumed to start at PCI address 0 |
|
807 */ |
|
808 void DoLoopBackTest(RTest& test, RPciChunk aBuffer, RChunk aWindow) |
|
809 { |
|
810 test.Start(_L("Test accessing memory via PCI")); |
|
811 |
|
812 TUint8* const bufferBase = aBuffer.Base(); |
|
813 const TUint bufferSize = aBuffer.Size(); |
|
814 const TUint bufferPciBase = aBuffer.PciBase(); |
|
815 |
|
816 TUint8* const windowBase = aWindow.Base(); |
|
817 const TUint windowSize = aWindow.Size(); |
|
818 |
|
819 #define PRINT(N) RDebug::Printf("%s = 0x%08x (%d)", #N, (N), (N)) |
|
820 PRINT(bufferBase); |
|
821 PRINT(bufferSize); |
|
822 PRINT(bufferPciBase); |
|
823 |
|
824 PRINT(windowBase); |
|
825 PRINT(windowSize); |
|
826 |
|
827 #undef PRINT |
|
828 |
|
829 //need to check that the end of the buffer |
|
830 //is within the windowed region |
|
831 test(bufferPciBase + bufferSize <= windowSize); |
|
832 TUint8* const bufferBaseWithinWindow = windowBase + bufferPciBase; |
|
833 |
|
834 test.Next(_L("write chunk")); |
|
835 for(TUint i = 0; i < bufferSize; ++i) |
|
836 { |
|
837 //each byte will hold its own offset modulo 256 |
|
838 bufferBase[i] = (TUint8)i; |
|
839 } |
|
840 |
|
841 test.Next(_L("read back via window")); |
|
842 for(TUint j=0; j < bufferSize; ++j) |
|
843 { |
|
844 const TUint8 result = bufferBaseWithinWindow[j]; |
|
845 test_Equal(j%256, result); |
|
846 } |
|
847 |
|
848 //clear chunk |
|
849 memclr(bufferBase, bufferSize); |
|
850 test.Next(_L("write via window")); |
|
851 for(TUint k=0; k < bufferSize; ++k) |
|
852 { |
|
853 //each byte will hold its own offset modulo 256 |
|
854 bufferBaseWithinWindow[k] = (TUint8)k; |
|
855 } |
|
856 |
|
857 test.Next(_L("read back from chunk")); |
|
858 for(TUint l=0; l < bufferSize; ++l) |
|
859 { |
|
860 const TUint8 result = bufferBase[l]; |
|
861 test_Equal(l%256, result); |
|
862 } |
|
863 |
|
864 test.End(); |
|
865 } |
|
866 |
|
867 /** |
|
868 Take care of opening a chunk, running the test and closing |
|
869 */ |
|
870 template<ChunkOpenFn OPEN_FUNC> |
|
871 inline void LoopBackTest(RPci& aPci, RTest& test, RChunk& aWindow) |
|
872 { |
|
873 RPciChunk pciChunk; |
|
874 const TInt chunkSize = 0x400; //1k |
|
875 |
|
876 //call the specified chunk opening function |
|
877 TRequestStatus status; |
|
878 TInt r = ((aPci).*(OPEN_FUNC))(pciChunk, chunkSize, &status); |
|
879 test_KErrNone(r); |
|
880 DoLoopBackTest(test, pciChunk, aWindow); |
|
881 pciChunk.Close(); |
|
882 User::WaitForRequest(status); |
|
883 } |
|
884 |
|
885 /** |
|
886 Run the loopback test for the 3 types of buffer supported by the PCI driver. |
|
887 DChunk |
|
888 DPlatChunk |
|
889 Mapped In external memory |
|
890 */ |
|
891 void TestLoopBack(RPci& aPci, RTest& test) |
|
892 { |
|
893 test.Next(_L("Open PCI window")); |
|
894 RChunk window; |
|
895 |
|
896 TInt r = aPci.OpenPciWindowChunk(window); |
|
897 test_KErrNone(r); |
|
898 |
|
899 test.Next(_L("DChunk")); |
|
900 LoopBackTest<&RPci::OpenPciDChunk>(aPci, test, window); |
|
901 |
|
902 test.Next(_L("DPlatHwChunk")); |
|
903 LoopBackTest<&RPci::OpenPciPlatHwChunk>(aPci, test, window); |
|
904 |
|
905 test.Next(_L("DChunk (mapped in)")); |
|
906 LoopBackTest<&RPci::OpenPciMappedChunk>(aPci, test, window); |
|
907 |
|
908 window.Close(); |
|
909 } |
|
910 #ifndef __VC32__ //visual studio 6 doesn't approve of pointer to member function template parameters |
|
911 /** |
|
912 Run the CPciOpenChunkTest for each type of chunk. This function also creates (and destroys) the |
|
913 necessary semaphores and locks. |
|
914 CPciOpenChunkTest objects are run in multiple threads using MultipleTestRun(). |
|
915 |
|
916 @param aDevice Handle to the test driver |
|
917 @param test RTest to use. |
|
918 @param aBufferLimit The maximum number of buffers which can be opened simultaneously |
|
919 */ |
|
920 void TestBufferOpenConcurrency(RPci& aDevice, RTest& test, TInt aBufferLimit) |
|
921 { |
|
922 RSemaphore semaphoreOpen; |
|
923 RSemaphore semaphoreClose; |
|
924 RFastLock lock; |
|
925 |
|
926 TInt r = semaphoreOpen.CreateLocal(aBufferLimit); |
|
927 test_KErrNone(r); |
|
928 |
|
929 r = semaphoreClose.CreateLocal(0); |
|
930 test_KErrNone(r); |
|
931 |
|
932 r = lock.CreateLocal(); |
|
933 test_KErrNone(r); |
|
934 |
|
935 const TInt iterations = 3; |
|
936 { |
|
937 test.Printf(_L("Opening %d PCI DChunks in %d threads\n"), aBufferLimit, aBufferLimit); |
|
938 CPciOpenChunkTest<&RPci::OpenPciDChunk> |
|
939 dChunkTest(_L("Concurrent-DChunk"), iterations, aDevice, semaphoreOpen, semaphoreClose, lock, aBufferLimit); |
|
940 |
|
941 MultipleTestRun(test, dChunkTest, aBufferLimit); |
|
942 } |
|
943 |
|
944 { |
|
945 test.Printf(_L("Opening %d PCI DPlatHwChunks in %d threads\n"), aBufferLimit, aBufferLimit); |
|
946 CPciOpenChunkTest<&RPci::OpenPciPlatHwChunk> |
|
947 platChunkTest(_L("Concurrent-DPlatHwChunk"), iterations, aDevice, semaphoreOpen, semaphoreClose, lock, aBufferLimit); |
|
948 |
|
949 MultipleTestRun(test, platChunkTest, aBufferLimit); |
|
950 } |
|
951 |
|
952 { |
|
953 test.Printf(_L("Opening %d PCI Mapped chunks in %d threads\n"), aBufferLimit, aBufferLimit); |
|
954 CPciOpenChunkTest<&RPci::OpenPciMappedChunk> |
|
955 mappedChunkTest(_L("Concurrent-DChunk(mapped)"), iterations, aDevice, semaphoreOpen, semaphoreClose, lock, aBufferLimit); |
|
956 |
|
957 MultipleTestRun(test, mappedChunkTest, aBufferLimit); |
|
958 } |
|
959 |
|
960 semaphoreOpen.Close(); |
|
961 semaphoreClose.Close(); |
|
962 lock.Close(); |
|
963 } |
|
964 #endif |
|
965 |
|
966 TInt E32Main() |
|
967 { |
|
968 __UHEAP_MARK; |
|
969 |
|
970 _LIT(KPci, "PCI"); |
|
971 RTest test(KPci); |
|
972 test.Start(_L("Running PCI tests\n")); |
|
973 |
|
974 TInt r = User::LoadLogicalDevice(KPciLdd); |
|
975 |
|
976 __KHEAP_MARK; |
|
977 |
|
978 if(r==KErrNotFound) |
|
979 { |
|
980 test.Printf(_L("No PCI system present - skipping test\n")); |
|
981 return KErrNone; |
|
982 } |
|
983 if(r!=KErrNone && r!=KErrAlreadyExists) |
|
984 { |
|
985 test_KErrNone(r); |
|
986 } |
|
987 |
|
988 test.Next(_L("Open non-existant device\n")); |
|
989 RPci device; |
|
990 TPciDevice unavailable; |
|
991 r = device.Open(unavailable); |
|
992 test_Equal(KErrNotFound, r); |
|
993 |
|
994 RPci pciInfo; |
|
995 r = pciInfo.Open(); |
|
996 test_KErrNone(r); |
|
997 |
|
998 test.Next(_L("Get test info from driver\n")); |
|
999 TPciTestInfo info; |
|
1000 r = pciInfo.GetTestInfo(info); |
|
1001 test_KErrNone(r); |
|
1002 pciInfo.Close(); |
|
1003 |
|
1004 test.Next(_L("Open test device\n")); |
|
1005 r = device.Open(info.iDevice); |
|
1006 test_KErrNone(r); |
|
1007 |
|
1008 test.Next(_L("Run Device Unit Test\n")); |
|
1009 r=TestRunPciUnitTest(device); |
|
1010 test_KErrNone(r); |
|
1011 |
|
1012 test.Next(_L("Read config space\n")); |
|
1013 TUserConfigSpace cs(device); |
|
1014 TestReadAddressSpace(cs, info.iCfgSpaceRead, test); |
|
1015 |
|
1016 test.Next(_L("Write config space\n")); |
|
1017 TestWriteAddressSpace(cs, info.iCfgSpaceWrite, test); |
|
1018 |
|
1019 test.Next(_L("Read memory space\n")); |
|
1020 TUserMemorySpace ms(device, info.iMemSpaceIndex); |
|
1021 TestReadAddressSpace(ms, info.iMemSpaceRead, test); |
|
1022 |
|
1023 test.Next(_L("Modify memory space\n")); |
|
1024 TestWriteAddressSpace(ms, info.iMemSpaceWrite, test); |
|
1025 |
|
1026 { |
|
1027 const TInt addrSpaceThreadCount = 4; |
|
1028 const TInt iterations = 100; |
|
1029 test.Next(_L("Concurrent config space reads")); |
|
1030 CPciAddressSpaceRead cfgSpaceRead(_L("Cfg Space Read"), iterations, device, cs, info.iCfgSpaceRead); |
|
1031 MultipleTestRun(test, cfgSpaceRead, addrSpaceThreadCount); |
|
1032 |
|
1033 test.Next(_L("Concurrent memory space reads")); |
|
1034 CPciAddressSpaceRead memSpaceRead(_L("Memory Space Read"), iterations, device, ms, info.iMemSpaceRead); |
|
1035 MultipleTestRun(test, memSpaceRead, addrSpaceThreadCount); |
|
1036 } |
|
1037 |
|
1038 TInt testDChunkSize = 0x4000; |
|
1039 test.Next(_L("Open and Close DChunks\n")); |
|
1040 TestOpenAndCloseDChunk(device,test,testDChunkSize); |
|
1041 |
|
1042 TInt testDPlatChunkSize = 0x2000; |
|
1043 test.Next(_L("Open and Close PlatHwChunks\n")); |
|
1044 TestOpenAndClosePciPlatHwChunk(device,test,testDPlatChunkSize); |
|
1045 |
|
1046 //TestPciMapppedChunk() fails for sizes greater than 4K. |
|
1047 //The issue is that a block of externally mapped memory must be |
|
1048 //naturally alligned in order to be accessible to the PCI bus (ie |
|
1049 //an 8k buffer would have to start at an address which is a |
|
1050 //multiple of 8k. |
|
1051 // |
|
1052 //Now we could fix this for sure on the kernel side, by making |
|
1053 //sure we only commit correctly aligned memory into the chunk (as |
|
1054 //the pci driver itself does), |
|
1055 //However, by using a 4k chunk, we know this will be on a page |
|
1056 //boundary so the alignment is correct (assuming the page size |
|
1057 //isn't changed). |
|
1058 TInt testMapppedChunkSize = 0x1000; |
|
1059 test.Next(_L("Open and Close Pci Mappped Chunk\n")); |
|
1060 TestPciMapppedChunk(device,test,testMapppedChunkSize); |
|
1061 |
|
1062 test.Next(_L("Open and Close Pci Window Chunk\n")); |
|
1063 TestPciWindowChunk(device,test); |
|
1064 |
|
1065 const TInt numberOfThreads = info.iNumberOfBars; |
|
1066 test.Printf(_L("Open buffers concurrently, max supported = %d\n"), numberOfThreads); |
|
1067 #ifndef __VC32__ |
|
1068 TestBufferOpenConcurrency(device, test, numberOfThreads); |
|
1069 #else |
|
1070 test.Printf(_L("TestBufferOpenConcurrency not implemented for WINS"), numberOfThreads); |
|
1071 #endif |
|
1072 |
|
1073 test.Next(_L("Test loop back")); |
|
1074 TestLoopBack(device, test); |
|
1075 |
|
1076 device.Close(); |
|
1077 __KHEAP_MARKEND; |
|
1078 |
|
1079 r = User::FreeLogicalDevice(KPciLdd); |
|
1080 test_KErrNone(r); |
|
1081 |
|
1082 test.End(); |
|
1083 test.Close(); |
|
1084 |
|
1085 __UHEAP_MARKEND; |
|
1086 return KErrNone; |
|
1087 } |
|