|
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 "../misc/test_thread.h" |
|
66 #include <e32std.h> |
|
67 #define __E32TEST_EXTENSION__ |
|
68 #include <e32test.h> |
|
69 #include "t_pci.h" |
|
70 #include <assp/naviengine/pci.h> |
|
71 |
|
72 class RPci; |
|
73 /** |
|
74 Extends RChunk to hold the PCI address |
|
75 associated with a chunk. |
|
76 */ |
|
77 class RPciChunk: public RChunk |
|
78 { |
|
79 public: |
|
80 TUint PciBase() |
|
81 { |
|
82 return iPciBaseAddr; |
|
83 } |
|
84 |
|
85 /** |
|
86 Return the PCI accessible size |
|
87 */ |
|
88 TInt Size() const |
|
89 { |
|
90 return iPciSize; |
|
91 } |
|
92 |
|
93 private: |
|
94 friend class RPci; |
|
95 TUint iPciBaseAddr; |
|
96 TInt iPciSize; //size of the region mapped into PCI |
|
97 }; |
|
98 |
|
99 typedef TInt (RPci::*ChunkOpenFn)(RPciChunk&, TInt, TRequestStatus*); |
|
100 |
|
101 class RPci : public RBusLogicalChannel |
|
102 { |
|
103 public: |
|
104 TInt Open(); |
|
105 TInt GetTestInfo(TPciTestInfo& aTestInfo); |
|
106 |
|
107 TInt Open(const TPciDevice&); |
|
108 |
|
109 TUint AccessConfigSpace(const TUserConfigSpace& aCs); |
|
110 TUint AccessMemorySpace(const TUserMemorySpace& aMs); |
|
111 TInt OpenPciDChunk(RPciChunk& aPciChunk,TInt aPciChunkSize, TRequestStatus* aStatus=0); |
|
112 TInt OpenPciPlatHwChunk(RPciChunk& aPciHwChunk,TInt aPciChunkSize, TRequestStatus* aStatus=0); |
|
113 TInt OpenPciMappedChunk(RPciChunk& aPciMappedChunk,TInt aPciChunkSize, TRequestStatus* aStatus=0); |
|
114 TInt OpenPciWindowChunk(RChunk& aPciWindowChunk); |
|
115 TInt RunUnitTests(); |
|
116 private: |
|
117 TInt DoOpenPciChunk(RPciChunk& aPciChunk, TInt aPciChunkSize, TPciTestCmd aCmd, TRequestStatus* aStatus); |
|
118 }; |
|
119 |
|
120 inline TInt RPci::Open() |
|
121 { |
|
122 return DoCreate(KPciLddFactory, TVersion(), KNullUnit, NULL, NULL); |
|
123 } |
|
124 |
|
125 inline TInt RPci::Open(const TPciDevice& aDevice) |
|
126 { |
|
127 TPckgC<TPciDevice> devicePkg(aDevice); |
|
128 return DoCreate(KPciLddFactory, TVersion(), KNullUnit, NULL, &devicePkg); |
|
129 } |
|
130 |
|
131 inline TInt RPci::GetTestInfo(TPciTestInfo& aTestInfo) |
|
132 { |
|
133 TPckg<TPciTestInfo> info(aTestInfo); |
|
134 return DoControl(EGetTestInfo, &info); |
|
135 } |
|
136 |
|
137 inline TInt RPci::RunUnitTests() |
|
138 { |
|
139 return DoControl(ERunUnitTests); |
|
140 } |
|
141 |
|
142 TUint RPci::AccessConfigSpace(const TUserConfigSpace& aCs) |
|
143 { |
|
144 TPckgC<TUserConfigSpace> pkg(aCs); |
|
145 return DoControl(EAccessConfigSpace, &pkg); |
|
146 } |
|
147 |
|
148 TUint RPci::AccessMemorySpace(const TUserMemorySpace& aMs) |
|
149 { |
|
150 TPckgC<TUserMemorySpace> pkg(aMs); |
|
151 return DoControl(EAccessMemorySpace, &pkg); |
|
152 } |
|
153 |
|
154 TInt RPci::OpenPciDChunk(RPciChunk& aPciChunk,TInt aPciChunkSize, TRequestStatus* aStatus) |
|
155 { |
|
156 return DoOpenPciChunk(aPciChunk, aPciChunkSize, EOpenPciDChunk, aStatus); |
|
157 } |
|
158 |
|
159 TInt RPci::OpenPciPlatHwChunk(RPciChunk& aPciHwChunk,TInt aPciChunkSize, TRequestStatus* aStatus) |
|
160 { |
|
161 return DoOpenPciChunk(aPciHwChunk, aPciChunkSize, EOpenPciPlatHwChunk, aStatus); |
|
162 } |
|
163 |
|
164 TInt RPci::OpenPciMappedChunk(RPciChunk& aPciMappedChunk,TInt aPciChunkSize, TRequestStatus* aStatus) |
|
165 { |
|
166 return DoOpenPciChunk(aPciMappedChunk, aPciChunkSize, EOpenPciMappedChunk, aStatus); |
|
167 } |
|
168 |
|
169 TInt RPci::OpenPciWindowChunk(RChunk& aPciWindowChunk) |
|
170 { |
|
171 TUint chunkHandle = DoControl(EOpenPciWindowChunk); |
|
172 return aPciWindowChunk.SetReturnedHandle(chunkHandle); |
|
173 } |
|
174 |
|
175 TInt RPci::DoOpenPciChunk(RPciChunk& aPciChunk, TInt aPciChunkSize, TPciTestCmd aCmd, TRequestStatus* aStatus) |
|
176 { |
|
177 const TInt constPciChunkSize = aPciChunkSize; |
|
178 TPciChunkCreateInfo info(constPciChunkSize, aPciChunk.iPciBaseAddr, aStatus); |
|
179 TPckgC<TPciChunkCreateInfo> pkg(info); |
|
180 |
|
181 TUint chunkHandle = DoControl(aCmd, &pkg); |
|
182 |
|
183 const TInt r = aPciChunk.SetReturnedHandle(chunkHandle); |
|
184 if(r == KErrNone) |
|
185 { |
|
186 aPciChunk.iPciSize = constPciChunkSize; |
|
187 } |
|
188 return r; |
|
189 } |
|
190 |
|
191 TUserPciSpace::TUserPciSpace(RPci& aPci) |
|
192 :iPci(&aPci) |
|
193 {} |
|
194 |
|
195 TUserConfigSpace::TUserConfigSpace(RPci& aPci) |
|
196 :TUserPciSpace(aPci) |
|
197 {} |
|
198 |
|
199 TUint TUserConfigSpace::Call() |
|
200 { |
|
201 return iPci->AccessConfigSpace(*this); |
|
202 } |
|
203 |
|
204 TUserPciSpace* TUserConfigSpace::Clone() const |
|
205 { |
|
206 return new TUserConfigSpace(*this); |
|
207 } |
|
208 |
|
209 TUserMemorySpace::TUserMemorySpace(RPci& aPci, TInt aBarIndex) |
|
210 :TUserPciSpace(aPci), iBarIndex(aBarIndex) |
|
211 {} |
|
212 |
|
213 TUint TUserMemorySpace::Call() |
|
214 { |
|
215 return iPci->AccessMemorySpace(*this); |
|
216 } |
|
217 |
|
218 TUserPciSpace* TUserMemorySpace::Clone() const |
|
219 { |
|
220 return new TUserMemorySpace(*this); |
|
221 } |
|
222 |
|
223 /** |
|
224 Test address allocator |
|
225 */ |
|
226 TInt TestRunPciUnitTest(RPci& pci) |
|
227 { |
|
228 return pci.RunUnitTests(); |
|
229 } |
|
230 |
|
231 |
|
232 /** |
|
233 Read from a defined address in memory or config space, compare against expected values. |
|
234 8,16, and 32 bit accesses performed. |
|
235 |
|
236 @param aSpace Object gving access to either the config or memory space of a PCI device |
|
237 @param aInfo Contains the address and expected value of a dword |
|
238 */ |
|
239 void TestReadAddressSpace(TUserPciSpace& aSpace, const TPciTestInfo::TAddrSpaceTest& aInfo, RTest& test, TBool aVerbose=EFalse) |
|
240 { |
|
241 const TUint os = aInfo.iOffset; |
|
242 //Iterate over different widths, and possible |
|
243 //subfields of 32 bit word |
|
244 for(TInt bitWidth=32; bitWidth>=8; bitWidth>>=1) |
|
245 { |
|
246 const TInt numberOfFields = (32/bitWidth); |
|
247 for(TInt i=0; i< numberOfFields; i++) |
|
248 { |
|
249 const TInt extraByteOffset = i * (bitWidth >> 3); |
|
250 const TInt byteOffset = os + extraByteOffset; |
|
251 if(aVerbose) |
|
252 test.Printf(_L("Access bitWidth=%d byte offset=%d\n"), bitWidth, byteOffset); |
|
253 |
|
254 const TUint expected = aInfo.Expected(bitWidth, byteOffset); |
|
255 const TUint read = aSpace.Read(bitWidth, byteOffset); |
|
256 if(aVerbose) |
|
257 test.Printf(_L("expect 0x%08x, read 0x%08x\n"), expected, read); |
|
258 test_Equal(expected, read); |
|
259 } |
|
260 } |
|
261 } |
|
262 |
|
263 /** |
|
264 Verify writes and modifications to a defined address in memory or config space. 8,16, and 32 bit |
|
265 accesses performed. |
|
266 |
|
267 @param aSpace Object gving access to either the config or memory space of a PCI device |
|
268 @param aInfo Contains the address of a (at least partially) writable dword |
|
269 */ |
|
270 void TestWriteAddressSpace(TUserPciSpace& aSpace, TPciTestInfo::TAddrSpaceTest& aInfo, RTest& test, TBool aVerbose=EFalse) |
|
271 { |
|
272 const TUint original = aSpace.Read(32, aInfo.iOffset); |
|
273 const TUint os = aInfo.iOffset; |
|
274 TUint mask = ~aInfo.iReadOnlyMask; |
|
275 |
|
276 //The pattern will be truncated when used with bit widths |
|
277 //less than 32. |
|
278 const TUint initPattern = 0xFFFFFFFF; |
|
279 |
|
280 for(TInt bitWidth=32; bitWidth>=8; bitWidth>>=1) |
|
281 { |
|
282 const TUint pattern = initPattern >> (32-bitWidth); |
|
283 const TInt numberOfFields = (32/bitWidth); |
|
284 for(TInt i=0; i< numberOfFields; i++) |
|
285 { |
|
286 const TInt extraByteOffset = i * (bitWidth >> 3); |
|
287 const TInt byteOffset = os + extraByteOffset; |
|
288 if(aVerbose) |
|
289 test.Printf(_L("Access bitWidth=%d byte offset=%d\n"), bitWidth, byteOffset); |
|
290 //the full dword we expect |
|
291 //currently assume that the unwritable bits will be 0 |
|
292 const TUint writeExpect = (pattern << (bitWidth * i) ) & mask; |
|
293 const TUint clearExpect = 0; |
|
294 |
|
295 //do write followed by clear |
|
296 const TUint expect[] = {writeExpect, clearExpect}; |
|
297 const TUint write[] = {pattern, 0}; |
|
298 for(TInt n = 0; n < 2; n++) |
|
299 { |
|
300 aSpace.Write(bitWidth, byteOffset, write[n]); |
|
301 TUint result = aSpace.Read(32, os); |
|
302 |
|
303 if(aVerbose) |
|
304 test.Printf(_L("wrote 0x%08x, expect 0x%08x, read 0x%08x\n"), |
|
305 write[n], expect[n], result); |
|
306 test_Equal(expect[n], result); |
|
307 } |
|
308 |
|
309 //test Modify calls. Set then clear pattern |
|
310 TUint set[] = {pattern, 0}; |
|
311 TUint clear[] = {0, pattern}; |
|
312 |
|
313 for(TInt m = 0; m < 2; m++) |
|
314 { |
|
315 aSpace.Modify(bitWidth, byteOffset, clear[m], set[m]); |
|
316 TUint result = aSpace.Read(32, os); |
|
317 |
|
318 if(aVerbose) |
|
319 test.Printf(_L("clear 0x%08x, set 0x%08x, expect 0x%08x, read 0x%08x\n"), clear[m], set[m], expect[m], result); |
|
320 test_Equal(expect[m], result); |
|
321 } |
|
322 } |
|
323 } |
|
324 |
|
325 //restore orginal value or we will not be able to access device |
|
326 aSpace.Write(32, os, original); |
|
327 } |
|
328 |
|
329 |
|
330 /** |
|
331 Verify that a PCI DChunk can be opened and closed from user side |
|
332 |
|
333 @param pci The RPci object to use |
|
334 @param test The RTest object to use |
|
335 @param aPciChunkSize The size of the DChunk which would be created |
|
336 */ |
|
337 void TestOpenAndCloseDChunk(RPci& pci,RTest& test,TInt aPciChunkSize) |
|
338 { |
|
339 RPciChunk testPciDChunk; |
|
340 |
|
341 // Create and open Chunk |
|
342 TRequestStatus status; |
|
343 TInt r = pci.OpenPciDChunk(testPciDChunk,aPciChunkSize, &status); |
|
344 test_KErrNone(r); |
|
345 |
|
346 test(testPciDChunk.IsWritable()); |
|
347 test(testPciDChunk.IsReadable()); |
|
348 |
|
349 test.Printf(_L("PCI Chunk base = 0x%08x\n"), testPciDChunk.Base()); |
|
350 test.Printf(_L("PCI Chunk size = %d\n"), testPciDChunk.Size()); |
|
351 test.Printf(_L("PCI Address = 0x%08x\n"), testPciDChunk.PciBase()); |
|
352 |
|
353 //Close Chunk |
|
354 test.Next(_L("Close PCI Chunk handle")); |
|
355 |
|
356 RTest::CloseHandleAndWaitForDestruction(testPciDChunk); |
|
357 User::WaitForRequest(status); |
|
358 } |
|
359 |
|
360 /** |
|
361 Verify that a PCI PlatHwChunk can be opened and closed from user side |
|
362 |
|
363 |
|
364 @param pci The RPci object to use |
|
365 @param test The RTest object to use |
|
366 @param aPciChunkSize The size of the PlatHwChunk which would be created |
|
367 */ |
|
368 void TestOpenAndClosePciPlatHwChunk(RPci& pci,RTest& test,TInt aPciChunkSize) |
|
369 { |
|
370 RPciChunk testPciPlatHwChunk; |
|
371 |
|
372 // Create and open Chunk |
|
373 TRequestStatus status; |
|
374 TInt r = pci.OpenPciPlatHwChunk(testPciPlatHwChunk,aPciChunkSize, &status); |
|
375 test_KErrNone(r); |
|
376 |
|
377 test(testPciPlatHwChunk.IsWritable()); |
|
378 test(testPciPlatHwChunk.IsReadable()); |
|
379 |
|
380 test.Printf(_L("PCI Chunk base = 0x%08x\n"), testPciPlatHwChunk.Base()); |
|
381 test.Printf(_L("PCI Chunk size = %d\n"), testPciPlatHwChunk.Size()); |
|
382 test.Printf(_L("PCI Address = 0x%08x\n"), testPciPlatHwChunk.PciBase()); |
|
383 |
|
384 //Close Chunk |
|
385 testPciPlatHwChunk.Close(); |
|
386 User::WaitForRequest(status); |
|
387 test.Next(_L("Closed PCI PlatHwChunk handle")); |
|
388 } |
|
389 |
|
390 /** |
|
391 Verify that pci-mapped DChunk can be opended and closed form user side |
|
392 |
|
393 @param pci The RPci object to use |
|
394 @param test The RTest object to use |
|
395 @param aPciChunkSize The size of the pci-mapped DChunk which would be created |
|
396 */ |
|
397 void TestPciMapppedChunk(RPci& pci,RTest& test,TInt aPciChunkSize) |
|
398 { |
|
399 RPciChunk testPciMappedChunk; |
|
400 |
|
401 // Create and open Chunk |
|
402 TRequestStatus status; |
|
403 TInt r = pci.OpenPciMappedChunk(testPciMappedChunk,aPciChunkSize, &status); |
|
404 test_KErrNone(r); |
|
405 |
|
406 test(testPciMappedChunk.IsWritable()); |
|
407 test(testPciMappedChunk.IsReadable()); |
|
408 |
|
409 test.Printf(_L("PCI Chunk base = 0x%08x\n"), testPciMappedChunk.Base()); |
|
410 test.Printf(_L("PCI Chunk size = %d\n"), testPciMappedChunk.Size()); |
|
411 test.Printf(_L("PCI Address = 0x%08x\n"), testPciMappedChunk.PciBase()); |
|
412 |
|
413 //Close Chunk |
|
414 testPciMappedChunk.Close(); |
|
415 User::WaitForRequest(status); |
|
416 test.Next(_L("Closed PCI Mapped Chunk handle")); |
|
417 } |
|
418 |
|
419 /** |
|
420 Verify that an RChunk can be open to grant access to the internal PCI window from the user side |
|
421 |
|
422 @param pci The RPci object to use |
|
423 @param test The RTest object to use |
|
424 */ |
|
425 void TestPciWindowChunk(RPci& pci,RTest& test) |
|
426 { |
|
427 RChunk testPciWindowChunk; |
|
428 |
|
429 // Create and open DChunk |
|
430 TInt r = pci.OpenPciWindowChunk(testPciWindowChunk); |
|
431 test_KErrNone(r); |
|
432 |
|
433 test(testPciWindowChunk.IsWritable()); |
|
434 test(testPciWindowChunk.IsReadable()); |
|
435 |
|
436 test.Printf(_L("PCI Window Chunk base = 0x%08x\n"), testPciWindowChunk.Base()); |
|
437 test.Printf(_L("PCI Window Chunk size = %d\n"), testPciWindowChunk.Size()); |
|
438 |
|
439 //Close Chunk |
|
440 testPciWindowChunk.Close(); |
|
441 test.Next(_L("Closed PCI Window Chunk handle")); |
|
442 } |
|
443 |
|
444 |
|
445 class CPciTest : public CTest |
|
446 { |
|
447 protected: |
|
448 CPciTest(const TDesC& aName, TInt aIterations, RPci& aDevice) |
|
449 : CTest(aName, aIterations), iDevice(aDevice) |
|
450 {} |
|
451 |
|
452 RPci iDevice; |
|
453 }; |
|
454 |
|
455 /** |
|
456 Each instance of test will open a chunk, using the function specified in |
|
457 the template argument, FUNC. |
|
458 |
|
459 The total number of chunks that can be opened by all instances is limited |
|
460 by iMaxCount. |
|
461 |
|
462 All intances of the test will hold their chunk open until iMaxCount has |
|
463 been reached. |
|
464 */ |
|
465 template<ChunkOpenFn FUNC> |
|
466 class CPciOpenChunkTest : public CPciTest |
|
467 { |
|
468 public: |
|
469 CPciOpenChunkTest(const TDesC& aName, TInt aIterations, RPci& aDevice, |
|
470 RSemaphore aSemOpen, RSemaphore aSemClose, RFastLock aLock, TInt aMaxCount) |
|
471 :CPciTest(aName, aIterations, aDevice), |
|
472 iSemOpen(aSemOpen), iSemClose(aSemClose), iLock(aLock), iMaxCount(aMaxCount) |
|
473 { |
|
474 } |
|
475 |
|
476 virtual void RunTest() |
|
477 { |
|
478 RTest test(iName); |
|
479 RPciChunk chunk; |
|
480 |
|
481 iSemOpen.Wait(); |
|
482 TRequestStatus status; |
|
483 const TInt chunkSize = 0x400; |
|
484 //open chunk by calling FUNC |
|
485 TInt r = ((iDevice).*(FUNC))(chunk, chunkSize, &status); |
|
486 test_KErrNone(r); |
|
487 |
|
488 iLock.Wait(); |
|
489 iOpenCount++; |
|
490 test.Printf(_L("Opened chunk %d\n"), iOpenCount); |
|
491 if(iOpenCount == iMaxCount) |
|
492 { |
|
493 test.Printf(_L("Opened=%d, max=%d: Allow chunks to close\n"), iOpenCount, iMaxCount); |
|
494 //release all waiting threads |
|
495 //plus 1 preincrement so this |
|
496 //thread also passes |
|
497 iSemClose.Signal(iOpenCount); |
|
498 iOpenCount = 0; |
|
499 } |
|
500 iLock.Signal(); |
|
501 |
|
502 |
|
503 iSemClose.Wait(); |
|
504 chunk.Close(); |
|
505 User::WaitForRequest(status); |
|
506 |
|
507 // permit another chunk to be opened |
|
508 iSemOpen.Signal(); |
|
509 test.Close(); |
|
510 } |
|
511 |
|
512 virtual CTest* Clone() const |
|
513 { |
|
514 //make shallow copy |
|
515 return new CPciOpenChunkTest(*this); |
|
516 } |
|
517 |
|
518 |
|
519 private: |
|
520 RSemaphore& iSemOpen; ///!< Represents the number of available PCI mappings |
|
521 RSemaphore& iSemClose; ///!< Represents the number of threads waiting to close their chunk |
|
522 RFastLock& iLock; |
|
523 static TInt iOpenCount; |
|
524 const TInt iMaxCount; |
|
525 }; |
|
526 |
|
527 template<ChunkOpenFn FUNC> |
|
528 TInt CPciOpenChunkTest<FUNC>::iOpenCount = 0; |
|
529 |
|
530 |
|
531 /** |
|
532 Test which will perform various reads from a PCI address |
|
533 space (config or memory) and confirm that values are read |
|
534 as expected |
|
535 */ |
|
536 class CPciAddressSpaceRead : public CPciTest |
|
537 { |
|
538 public: |
|
539 CPciAddressSpaceRead(const TDesC& aName, TInt aIterations, RPci& aDevice, |
|
540 const TUserPciSpace& aSpace, const TPciTestInfo::TAddrSpaceTest& aInfo) |
|
541 :CPciTest(aName, aIterations, aDevice), |
|
542 iAddressSpace(aSpace.Clone()), iSpaceTestInfo(aInfo) |
|
543 { |
|
544 } |
|
545 |
|
546 CPciAddressSpaceRead(const CPciAddressSpaceRead& aOther) |
|
547 :CPciTest(aOther)/* TODO-REVIEW have object-sliced aOther - is this ok?*/, |
|
548 iAddressSpace(aOther.iAddressSpace->Clone()), iSpaceTestInfo(aOther.iSpaceTestInfo) |
|
549 { |
|
550 } |
|
551 |
|
552 virtual ~CPciAddressSpaceRead() |
|
553 { |
|
554 delete iAddressSpace; |
|
555 } |
|
556 |
|
557 virtual void RunTest() |
|
558 { |
|
559 __UHEAP_MARK; |
|
560 RTest test(iName); |
|
561 TestReadAddressSpace(*iAddressSpace, iSpaceTestInfo, test); |
|
562 test.Close(); |
|
563 __UHEAP_MARKEND; |
|
564 } |
|
565 |
|
566 virtual CTest* Clone() const |
|
567 { |
|
568 //make shallow copy |
|
569 return new CPciAddressSpaceRead(*this); |
|
570 } |
|
571 |
|
572 private: |
|
573 TUserPciSpace* iAddressSpace; |
|
574 const TPciTestInfo::TAddrSpaceTest& iSpaceTestInfo; |
|
575 }; |
|
576 |
|
577 /** |
|
578 For aBuffer, test writing to it then reading back from aWindow |
|
579 then write via window and read back from chunk |
|
580 |
|
581 @param test The RTest object to use |
|
582 @param aBuffer RChunk corresponding to a PCI accessible buffer |
|
583 @param aWindow RChunk coressponding an appropriate System-to-PCI memory window |
|
584 It is presumed to start at PCI address 0 |
|
585 */ |
|
586 void DoLoopBackTest(RTest& test, RPciChunk aBuffer, RChunk aWindow) |
|
587 { |
|
588 test.Start(_L("Test accessing memory via PCI")); |
|
589 |
|
590 TUint8* const bufferBase = aBuffer.Base(); |
|
591 const TUint bufferSize = aBuffer.Size(); |
|
592 const TUint bufferPciBase = aBuffer.PciBase(); |
|
593 |
|
594 TUint8* const windowBase = aWindow.Base(); |
|
595 const TUint windowSize = aWindow.Size(); |
|
596 |
|
597 #define PRINT(N) RDebug::Printf("%s = 0x%08x (%d)", #N, (N), (N)) |
|
598 PRINT(bufferBase); |
|
599 PRINT(bufferSize); |
|
600 PRINT(bufferPciBase); |
|
601 |
|
602 PRINT(windowBase); |
|
603 PRINT(windowSize); |
|
604 |
|
605 #undef PRINT |
|
606 |
|
607 //need to check that the end of the buffer |
|
608 //is within the windowed region |
|
609 test(bufferPciBase + bufferSize <= windowSize); |
|
610 TUint8* const bufferBaseWithinWindow = windowBase + bufferPciBase; |
|
611 |
|
612 test.Next(_L("write chunk")); |
|
613 for(TUint i = 0; i < bufferSize; ++i) |
|
614 { |
|
615 //each byte will hold its own offset modulo 256 |
|
616 bufferBase[i] = (TUint8)i; |
|
617 } |
|
618 |
|
619 test.Next(_L("read back via window")); |
|
620 for(TUint j=0; j < bufferSize; ++j) |
|
621 { |
|
622 const TUint8 result = bufferBaseWithinWindow[j]; |
|
623 test_Equal(j%256, result); |
|
624 } |
|
625 |
|
626 //clear chunk |
|
627 memclr(bufferBase, bufferSize); |
|
628 test.Next(_L("write via window")); |
|
629 for(TUint k=0; k < bufferSize; ++k) |
|
630 { |
|
631 //each byte will hold its own offset modulo 256 |
|
632 bufferBaseWithinWindow[k] = (TUint8)k; |
|
633 } |
|
634 |
|
635 test.Next(_L("read back from chunk")); |
|
636 for(TUint l=0; l < bufferSize; ++l) |
|
637 { |
|
638 const TUint8 result = bufferBase[l]; |
|
639 test_Equal(l%256, result); |
|
640 } |
|
641 |
|
642 test.End(); |
|
643 } |
|
644 |
|
645 /** |
|
646 Take care of opening a chunk, running the test and closing |
|
647 */ |
|
648 template<ChunkOpenFn OPEN_FUNC> |
|
649 inline void LoopBackTest(RPci& aPci, RTest& test, RChunk& aWindow) |
|
650 { |
|
651 RPciChunk pciChunk; |
|
652 const TInt chunkSize = 0x400; //1k |
|
653 |
|
654 //call the specified chunk opening function |
|
655 TRequestStatus status; |
|
656 TInt r = ((aPci).*(OPEN_FUNC))(pciChunk, chunkSize, &status); |
|
657 test_KErrNone(r); |
|
658 DoLoopBackTest(test, pciChunk, aWindow); |
|
659 pciChunk.Close(); |
|
660 User::WaitForRequest(status); |
|
661 } |
|
662 |
|
663 /** |
|
664 Run the loopback test for the 3 types of buffer supported by the PCI driver. |
|
665 DChunk |
|
666 DPlatChunk |
|
667 Mapped In external memory |
|
668 */ |
|
669 void TestLoopBack(RPci& aPci, RTest& test) |
|
670 { |
|
671 test.Next(_L("Open PCI window")); |
|
672 RChunk window; |
|
673 |
|
674 TInt r = aPci.OpenPciWindowChunk(window); |
|
675 test_KErrNone(r); |
|
676 |
|
677 test.Next(_L("DChunk")); |
|
678 LoopBackTest<&RPci::OpenPciDChunk>(aPci, test, window); |
|
679 |
|
680 test.Next(_L("DPlatHwChunk")); |
|
681 LoopBackTest<&RPci::OpenPciPlatHwChunk>(aPci, test, window); |
|
682 |
|
683 test.Next(_L("DChunk (mapped in)")); |
|
684 LoopBackTest<&RPci::OpenPciMappedChunk>(aPci, test, window); |
|
685 |
|
686 window.Close(); |
|
687 } |
|
688 #ifndef __VC32__ //visual studio 6 doesn't approve of pointer to member function template parameters |
|
689 /** |
|
690 Run the CPciOpenChunkTest for each type of chunk. This function also creates (and destroys) the |
|
691 necessary semaphores and locks. |
|
692 CPciOpenChunkTest objects are run in multiple threads using MultipleTestRun(). |
|
693 |
|
694 @param aDevice Handle to the test driver |
|
695 @param test RTest to use. |
|
696 @param aBufferLimit The maximum number of buffers which can be opened simultaneously |
|
697 */ |
|
698 void TestBufferOpenConcurrency(RPci& aDevice, RTest& test, TInt aBufferLimit) |
|
699 { |
|
700 RSemaphore semaphoreOpen; |
|
701 RSemaphore semaphoreClose; |
|
702 RFastLock lock; |
|
703 |
|
704 TInt r = semaphoreOpen.CreateLocal(aBufferLimit); |
|
705 test_KErrNone(r); |
|
706 |
|
707 r = semaphoreClose.CreateLocal(0); |
|
708 test_KErrNone(r); |
|
709 |
|
710 r = lock.CreateLocal(); |
|
711 test_KErrNone(r); |
|
712 |
|
713 const TInt iterations = 3; |
|
714 { |
|
715 test.Printf(_L("Opening %d PCI DChunks in %d threads\n"), aBufferLimit, aBufferLimit); |
|
716 CPciOpenChunkTest<&RPci::OpenPciDChunk> |
|
717 dChunkTest(_L("Concurrent-DChunk"), iterations, aDevice, semaphoreOpen, semaphoreClose, lock, aBufferLimit); |
|
718 |
|
719 MultipleTestRun(test, dChunkTest, aBufferLimit); |
|
720 } |
|
721 |
|
722 { |
|
723 test.Printf(_L("Opening %d PCI DPlatHwChunks in %d threads\n"), aBufferLimit, aBufferLimit); |
|
724 CPciOpenChunkTest<&RPci::OpenPciPlatHwChunk> |
|
725 platChunkTest(_L("Concurrent-DPlatHwChunk"), iterations, aDevice, semaphoreOpen, semaphoreClose, lock, aBufferLimit); |
|
726 |
|
727 MultipleTestRun(test, platChunkTest, aBufferLimit); |
|
728 } |
|
729 |
|
730 { |
|
731 test.Printf(_L("Opening %d PCI Mapped chunks in %d threads\n"), aBufferLimit, aBufferLimit); |
|
732 CPciOpenChunkTest<&RPci::OpenPciMappedChunk> |
|
733 mappedChunkTest(_L("Concurrent-DChunk(mapped)"), iterations, aDevice, semaphoreOpen, semaphoreClose, lock, aBufferLimit); |
|
734 |
|
735 MultipleTestRun(test, mappedChunkTest, aBufferLimit); |
|
736 } |
|
737 |
|
738 semaphoreOpen.Close(); |
|
739 semaphoreClose.Close(); |
|
740 lock.Close(); |
|
741 } |
|
742 #endif |
|
743 |
|
744 TInt E32Main() |
|
745 { |
|
746 __UHEAP_MARK; |
|
747 |
|
748 _LIT(KPci, "PCI"); |
|
749 RTest test(KPci); |
|
750 test.Start(_L("Running PCI tests\n")); |
|
751 |
|
752 TInt r = User::LoadLogicalDevice(KPciLdd); |
|
753 |
|
754 __KHEAP_MARK; |
|
755 |
|
756 if(r==KErrNotFound) |
|
757 { |
|
758 test.Printf(_L("No PCI system present - skipping test\n")); |
|
759 return KErrNone; |
|
760 } |
|
761 if(r!=KErrNone && r!=KErrAlreadyExists) |
|
762 { |
|
763 test_KErrNone(r); |
|
764 } |
|
765 |
|
766 test.Next(_L("Open non-existant device\n")); |
|
767 RPci device; |
|
768 TPciDevice unavailable; |
|
769 r = device.Open(unavailable); |
|
770 test_Equal(KErrNotFound, r); |
|
771 |
|
772 RPci pciInfo; |
|
773 r = pciInfo.Open(); |
|
774 test_KErrNone(r); |
|
775 |
|
776 test.Next(_L("Get test info from driver\n")); |
|
777 TPciTestInfo info; |
|
778 r = pciInfo.GetTestInfo(info); |
|
779 test_KErrNone(r); |
|
780 pciInfo.Close(); |
|
781 |
|
782 test.Next(_L("Open test device\n")); |
|
783 r = device.Open(info.iDevice); |
|
784 test_KErrNone(r); |
|
785 |
|
786 test.Next(_L("Run Device Unit Test\n")); |
|
787 r=TestRunPciUnitTest(device); |
|
788 test_KErrNone(r); |
|
789 |
|
790 test.Next(_L("Read config space\n")); |
|
791 TUserConfigSpace cs(device); |
|
792 TestReadAddressSpace(cs, info.iCfgSpaceRead, test); |
|
793 |
|
794 test.Next(_L("Write config space\n")); |
|
795 TestWriteAddressSpace(cs, info.iCfgSpaceWrite, test); |
|
796 |
|
797 test.Next(_L("Read memory space\n")); |
|
798 TUserMemorySpace ms(device, info.iMemSpaceIndex); |
|
799 TestReadAddressSpace(ms, info.iMemSpaceRead, test); |
|
800 |
|
801 test.Next(_L("Modify memory space\n")); |
|
802 TestWriteAddressSpace(ms, info.iMemSpaceWrite, test); |
|
803 |
|
804 { |
|
805 const TInt addrSpaceThreadCount = 4; |
|
806 const TInt iterations = 100; |
|
807 test.Next(_L("Concurrent config space reads")); |
|
808 CPciAddressSpaceRead cfgSpaceRead(_L("Cfg Space Read"), iterations, device, cs, info.iCfgSpaceRead); |
|
809 MultipleTestRun(test, cfgSpaceRead, addrSpaceThreadCount); |
|
810 |
|
811 test.Next(_L("Concurrent memory space reads")); |
|
812 CPciAddressSpaceRead memSpaceRead(_L("Memory Space Read"), iterations, device, ms, info.iMemSpaceRead); |
|
813 MultipleTestRun(test, memSpaceRead, addrSpaceThreadCount); |
|
814 } |
|
815 |
|
816 TInt testDChunkSize = 0x4000; |
|
817 test.Next(_L("Open and Close DChunks\n")); |
|
818 TestOpenAndCloseDChunk(device,test,testDChunkSize); |
|
819 |
|
820 TInt testDPlatChunkSize = 0x2000; |
|
821 test.Next(_L("Open and Close PlatHwChunks\n")); |
|
822 TestOpenAndClosePciPlatHwChunk(device,test,testDPlatChunkSize); |
|
823 |
|
824 //TestPciMapppedChunk() fails for sizes greater than 4K. |
|
825 //The issue is that a block of externally mapped memory must be |
|
826 //naturally alligned in order to be accessible to the PCI bus (ie |
|
827 //an 8k buffer would have to start at an address which is a |
|
828 //multiple of 8k. |
|
829 // |
|
830 //Now we could fix this for sure on the kernel side, by making |
|
831 //sure we only commit correctly aligned memory into the chunk (as |
|
832 //the pci driver itself does), |
|
833 //However, by using a 4k chunk, we know this will be on a page |
|
834 //boundary so the alignment is correct (assuming the page size |
|
835 //isn't changed). |
|
836 TInt testMapppedChunkSize = 0x1000; |
|
837 test.Next(_L("Open and Close Pci Mappped Chunk\n")); |
|
838 TestPciMapppedChunk(device,test,testMapppedChunkSize); |
|
839 |
|
840 test.Next(_L("Open and Close Pci Window Chunk\n")); |
|
841 TestPciWindowChunk(device,test); |
|
842 |
|
843 const TInt numberOfThreads = info.iNumberOfBars; |
|
844 test.Printf(_L("Open buffers concurrently, max supported = %d\n"), numberOfThreads); |
|
845 #ifndef __VC32__ |
|
846 TestBufferOpenConcurrency(device, test, numberOfThreads); |
|
847 #else |
|
848 test.Printf(_L("TestBufferOpenConcurrency not implemented for WINS"), numberOfThreads); |
|
849 #endif |
|
850 |
|
851 test.Next(_L("Test loop back")); |
|
852 TestLoopBack(device, test); |
|
853 |
|
854 device.Close(); |
|
855 __KHEAP_MARKEND; |
|
856 |
|
857 r = User::FreeLogicalDevice(KPciLdd); |
|
858 test_KErrNone(r); |
|
859 |
|
860 test.End(); |
|
861 test.Close(); |
|
862 |
|
863 __UHEAP_MARKEND; |
|
864 return KErrNone; |
|
865 } |