dma_resource_test.cpp revision feb6103338a4b2327058270b7ca9cf125d86ef55
1/*
2 * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2008, Axel D��rfler, axeld@pinc-software.de.
4 * Distributed under the terms of the MIT License.
5 */
6
7#include <stdio.h>
8#include <string.h>
9
10#include <device_manager.h>
11
12#include <vm.h>
13
14#include "dma_resources.h"
15#include "io_requests.h"
16#include "IOScheduler.h"
17
18
19#define DMA_TEST_BLOCK_SIZE		512
20#define DMA_TEST_BUFFER_COUNT	10
21
22
23class TestSuite;
24
25class TestSuiteContext {
26public:
27							TestSuiteContext();
28							~TestSuiteContext();
29
30			status_t		Init(size_t size);
31
32			addr_t			DataBase() const { return fDataBase; }
33			addr_t			PhysicalDataBase() const
34								{ return fPhysicalDataBase; }
35
36			addr_t			CompareBase() const { return fCompareBase; }
37
38			size_t			Size() const { return fSize; }
39
40private:
41			area_id			fDataArea;
42			addr_t			fDataBase;
43			addr_t			fPhysicalDataBase;
44			area_id			fCompareArea;
45			addr_t			fCompareBase;
46			size_t			fSize;
47};
48
49class Test : public DoublyLinkedListLinkImpl<Test> {
50public:
51							Test(TestSuite& suite, off_t offset, size_t length,
52								bool isWrite, uint32 flags);
53
54			Test&			AddSource(addr_t base, size_t length);
55			Test&			NextResult(off_t offset, bool partialBegin,
56								bool partialEnd);
57			Test&			AddTarget(addr_t base, size_t length,
58								bool usesBounceBuffer);
59
60			void			Run(DMAResource& resource);
61
62private:
63			addr_t			_SourceToVirtual(addr_t source);
64			addr_t			_SourceToCompare(addr_t source);
65			void			_Prepare();
66			void			_CheckCompare();
67			void			_CheckWrite();
68			void			_CheckResults();
69			status_t		_DoIO(IOOperation& operation);
70			void			_Panic(const char* message,...);
71
72			TestSuite&		fSuite;
73			off_t			fOffset;
74			size_t			fLength;
75			bool			fIsWrite;
76			uint32			fFlags;
77			iovec			fSourceVecs[32];
78			uint32			fSourceCount;
79
80			struct target_t {
81				addr_t		address;
82				size_t		length;
83				bool		uses_bounce_buffer;
84			};
85			struct result_t {
86				off_t		offset;
87				target_t	targets[32];
88				uint32		count;
89				bool		partial_begin;
90				bool		partial_end;
91			};
92			result_t		fResults[32];
93			uint32			fResultCount;
94};
95
96typedef DoublyLinkedList<Test> TestList;
97
98
99class TestSuite {
100public:
101	TestSuite(TestSuiteContext& context, const char* name,
102			const dma_restrictions& restrictions, size_t blockSize)
103		:
104		fContext(context)
105	{
106		dprintf("----- Run \"%s\" tests ---------------------------\n", name);
107		dprintf("  DMA restrictions: address %#lx - %#lx, align %lu, boundary "
108			"%lu,\n    max transfer %lu, max segs %lu, max seg size %lu, "
109			"flags %lx\n\n", restrictions.low_address,
110			restrictions.high_address, restrictions.alignment,
111			restrictions.boundary, restrictions.max_transfer_size,
112			restrictions.max_segment_count, restrictions.max_segment_size,
113			restrictions.flags);
114
115		status_t status = fDMAResource.Init(restrictions, blockSize, 10);
116		if (status != B_OK)
117			panic("initializing DMA resource failed: %s\n", strerror(status));
118	}
119
120	~TestSuite()
121	{
122		while (Test* test = fTests.RemoveHead()) {
123			delete test;
124		}
125	}
126
127	Test& AddTest(off_t offset, size_t length, bool isWrite, uint32 flags)
128	{
129		Test* test = new(std::nothrow) Test(*this, offset, length, isWrite,
130			flags);
131		fTests.Add(test);
132
133		return *test;
134	}
135
136	void Run()
137	{
138		TestList::Iterator iterator = fTests.GetIterator();
139		uint32 count = 1;
140		while (Test* test = iterator.Next()) {
141			dprintf("test %lu...\n", count++);
142			test->Run(fDMAResource);
143		}
144	}
145
146	addr_t DataBase() const { return fContext.DataBase(); }
147	addr_t PhysicalDataBase() const { return fContext.PhysicalDataBase(); }
148	addr_t CompareBase() const { return fContext.CompareBase(); }
149	size_t Size() const { return fContext.Size(); }
150
151private:
152	TestSuiteContext& fContext;
153	DMAResource		fDMAResource;
154	uint8*			fBase;
155	uint8*			fPhysicalBase;
156	size_t			fSize;
157	TestList		fTests;
158};
159
160
161struct device_manager_info* sDeviceManager;
162
163static area_id sArea;
164static size_t sAreaSize;
165static void* sAreaAddress;
166static DMAResource* sDMAResource;
167static IOScheduler* sIOScheduler;
168
169
170status_t
171do_io(void* data, IOOperation* operation)
172{
173	uint8* disk = (uint8*)sAreaAddress;
174	off_t offset = operation->Offset();
175
176	for (uint32 i = 0; i < operation->VecCount(); i++) {
177		const iovec& vec = operation->Vecs()[i];
178		addr_t base = (addr_t)vec.iov_base;
179		size_t length = vec.iov_len;
180		size_t pageOffset = base & ~(B_PAGE_SIZE - 1);
181
182		while (length > 0) {
183			size_t toCopy = min_c(length, B_PAGE_SIZE - pageOffset);
184
185			uint8* virtualAddress;
186			vm_get_physical_page(base - pageOffset, (addr_t*)&virtualAddress,
187				PHYSICAL_PAGE_NO_WAIT);
188
189			if (operation->IsWrite())
190				memcpy(disk + offset, virtualAddress + pageOffset, toCopy);
191			else
192				memcpy(virtualAddress + pageOffset, disk + offset, toCopy);
193
194			length -= toCopy;
195			offset += toCopy;
196			pageOffset = 0;
197		}
198	}
199
200	if (sIOScheduler != NULL)
201		sIOScheduler->OperationCompleted(operation, B_OK);
202	return B_OK;
203}
204
205
206//	#pragma mark -
207
208
209TestSuiteContext::TestSuiteContext()
210	:
211	fDataArea(-1),
212	fCompareArea(-1),
213	fSize(0)
214{
215}
216
217
218TestSuiteContext::~TestSuiteContext()
219{
220	delete_area(fDataArea);
221	delete_area(fCompareArea);
222}
223
224
225status_t
226TestSuiteContext::Init(size_t size)
227{
228	fDataArea = create_area("data buffer", (void**)&fDataBase,
229		B_ANY_KERNEL_ADDRESS, size, B_CONTIGUOUS,
230		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
231	if (fDataArea < B_OK)
232		return fDataArea;
233
234	physical_entry entry;
235	get_memory_map((void*)fDataBase, size, &entry, 1);
236
237	dprintf("DMA Test area %p, physical %p\n", (void*)fDataBase, entry.address);
238	fPhysicalDataBase = (addr_t)entry.address;
239
240	fCompareArea = create_area("compare buffer", (void**)&fCompareBase,
241		B_ANY_KERNEL_ADDRESS, size, B_FULL_LOCK,
242		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
243	if (fCompareArea < B_OK)
244		return fCompareArea;
245
246	fSize = size;
247	return B_OK;
248}
249
250
251//	#pragma mark -
252
253
254Test::Test(TestSuite& suite, off_t offset, size_t length, bool isWrite,
255		uint32 flags)
256	:
257	fSuite(suite),
258	fOffset(offset),
259	fLength(length),
260	fIsWrite(isWrite),
261	fFlags(flags),
262	fSourceCount(0),
263	fResultCount(0)
264{
265}
266
267
268Test&
269Test::AddSource(addr_t address, size_t length)
270{
271	fSourceVecs[fSourceCount].iov_base
272		= (void*)(((fFlags & B_PHYSICAL_IO_REQUEST) == 0
273			? fSuite.DataBase() : fSuite.PhysicalDataBase()) + address);
274	fSourceVecs[fSourceCount].iov_len = length;
275	fSourceCount++;
276
277	return *this;
278}
279
280
281Test&
282Test::NextResult(off_t offset, bool partialBegin, bool partialEnd)
283{
284	fResults[fResultCount].offset = offset;
285	fResults[fResultCount].count = 0;
286	fResults[fResultCount].partial_begin = partialBegin;
287	fResults[fResultCount].partial_end = partialEnd;
288	fResultCount++;
289
290	return *this;
291}
292
293
294Test&
295Test::AddTarget(addr_t base, size_t length, bool usesBounceBuffer)
296{
297	struct result_t& result = fResults[fResultCount - 1];
298	struct target_t& target = result.targets[result.count++];
299
300	target.address = base;
301	target.length = length;
302	target.uses_bounce_buffer = usesBounceBuffer;
303
304	return *this;
305}
306
307
308addr_t
309Test::_SourceToVirtual(addr_t source)
310{
311	if ((fFlags & B_PHYSICAL_IO_REQUEST) != 0)
312		return source - fSuite.PhysicalDataBase() + fSuite.DataBase();
313
314	return source;
315}
316
317
318addr_t
319Test::_SourceToCompare(addr_t source)
320{
321	if ((fFlags & B_PHYSICAL_IO_REQUEST) != 0)
322		return source - fSuite.PhysicalDataBase() + fSuite.CompareBase();
323
324	return source - fSuite.DataBase() + fSuite.CompareBase();
325}
326
327
328void
329Test::_Prepare()
330{
331	// prepare disk
332
333	uint8* disk = (uint8*)sAreaAddress;
334	for (size_t i = 0; i < sAreaSize; i++) {
335		disk[i] = i % 26 + 'a';
336	}
337
338	// prepare data
339
340	memset((void*)fSuite.DataBase(), 0xcc, fSuite.Size());
341
342	if (fIsWrite) {
343		off_t offset = fOffset;
344		size_t length = fLength;
345
346		for (uint32 i = 0; i < fSourceCount; i++) {
347			uint8* data = (uint8*)_SourceToVirtual(
348				(addr_t)fSourceVecs[i].iov_base);
349			size_t vecLength = min_c(fSourceVecs[i].iov_len, length);
350
351			for (uint32 j = 0; j < vecLength; j++) {
352				data[j] = (offset + j) % 10 + '0';
353			}
354			offset += vecLength;
355			length -= vecLength;
356		}
357	}
358
359	// prepare compare data
360
361	memset((void*)fSuite.CompareBase(), 0xcc, fSuite.Size());
362
363	if (fIsWrite) {
364		// copy data from source
365		off_t offset = fOffset;
366		size_t length = fLength;
367
368		for (uint32 i = 0; i < fSourceCount; i++) {
369			uint8* compare = (uint8*)_SourceToCompare(
370				(addr_t)fSourceVecs[i].iov_base);
371			size_t vecLength = min_c(fSourceVecs[i].iov_len, length);
372
373			memcpy(compare,
374				(void*)_SourceToVirtual((addr_t)fSourceVecs[i].iov_base),
375				vecLength);
376			offset += vecLength;
377			length -= vecLength;
378		}
379	} else {
380		// copy data from drive
381		off_t offset = fOffset;
382		size_t length = fLength;
383
384		for (uint32 i = 0; i < fSourceCount; i++) {
385			uint8* compare = (uint8*)_SourceToCompare(
386				(addr_t)fSourceVecs[i].iov_base);
387			size_t vecLength = min_c(fSourceVecs[i].iov_len, length);
388
389			memcpy(compare, disk + offset, vecLength);
390			offset += vecLength;
391			length -= vecLength;
392		}
393	}
394
395	if (fIsWrite)
396		_CheckCompare();
397}
398
399
400void
401Test::_CheckCompare()
402{
403	uint8* data = (uint8*)fSuite.DataBase();
404	uint8* compare = (uint8*)fSuite.CompareBase();
405
406	for (size_t i = 0; i < fSuite.Size(); i++) {
407		if (data[i] != compare[i]) {
408			dprintf("offset %lu differs, %s:\n", i,
409				fIsWrite ? "write" : "read");
410			i &= ~63;
411			dump_block((char*)&data[i], min_c(64, fSuite.Size() - i), "  ");
412			dprintf("should be:\n");
413			dump_block((char*)&compare[i], min_c(64, fSuite.Size() - i), "  ");
414
415			_Panic("Data %s differs", fIsWrite ? "write" : "read");
416		}
417	}
418}
419
420
421void
422Test::_CheckWrite()
423{
424	_CheckCompare();
425
426	// check if we overwrote parts we shouldn't have
427
428	uint8* disk = (uint8*)sAreaAddress;
429	for (size_t i = 0; i < sAreaSize; i++) {
430		if (i >= fOffset && i < fOffset + fLength)
431			continue;
432
433		if (disk[i] != i % 26 + 'a') {
434			dprintf("disk[i] %c, expected %c, i %lu, fLength + fOffset %Ld\n",
435				disk[i], (int)(i % 26 + 'a'), i, fLength + fOffset);
436			dprintf("offset %lu differs, touched innocent data:\n", i);
437			i &= ~63;
438			dump_block((char*)&disk[i], min_c(64, fSuite.Size() - i), "  ");
439
440			_Panic("Data %s differs", fIsWrite ? "write" : "read");
441		}
442	}
443
444	// check if the data we wanted to have on disk ended up there
445
446	off_t offset = fOffset;
447	size_t length = fLength;
448
449	for (uint32 i = 0; i < fSourceCount; i++) {
450		uint8* data = (uint8*)_SourceToVirtual(
451			(addr_t)fSourceVecs[i].iov_base);
452		size_t vecLength = min_c(fSourceVecs[i].iov_len, length);
453
454		for (uint32 j = 0; j < vecLength; j++) {
455			if (disk[offset + j] != data[j]) {
456				dprintf("offset %lu differs, found on disk:\n", j);
457				j &= ~63;
458				dump_block((char*)&disk[offset + j],
459					min_c(64, fSuite.Size() - i), "  ");
460				dprintf("should be:\n");
461				dump_block((char*)&data[j], min_c(64, fSuite.Size() - j), "  ");
462
463				_Panic("Data write differs");
464			}
465		}
466
467		offset += vecLength;
468		length -= vecLength;
469	}
470}
471
472
473void
474Test::_CheckResults()
475{
476	if (fIsWrite)
477		_CheckWrite();
478	else
479		_CheckCompare();
480}
481
482
483status_t
484Test::_DoIO(IOOperation& operation)
485{
486	return do_io(NULL, &operation);
487}
488
489
490void
491Test::Run(DMAResource& resource)
492{
493	_Prepare();
494
495	IORequest request;
496	status_t status = request.Init(fOffset, fSourceVecs, fSourceCount,
497		fLength, fIsWrite, fFlags);
498	if (status != B_OK)
499		_Panic("request init failed: %s\n", strerror(status));
500
501	uint32 resultIndex = 0;
502
503	IOOperation operation;
504	while (request.RemainingBytes() > 0) {
505		if (resultIndex >= fResultCount)
506			_Panic("no results left");
507
508		status_t status = resource.TranslateNext(&request, &operation);
509		if (status != B_OK) {
510			_Panic("DMAResource::TranslateNext() failed: %s\n",
511				strerror(status));
512			break;
513		}
514
515		DMABuffer* buffer = operation.Buffer();
516
517		dprintf("IOOperation: offset %Ld, length %lu (%Ld/%lu)\n",
518			operation.Offset(), operation.Length(), operation.OriginalOffset(),
519			operation.OriginalLength());
520		dprintf("  DMABuffer %p, %lu vecs, bounce buffer: %p (%p) %s\n", buffer,
521			buffer->VecCount(), buffer->BounceBuffer(),
522			(void*)buffer->PhysicalBounceBuffer(),
523			operation.UsesBounceBuffer() ? "used" : "unused");
524		for (uint32 i = 0; i < buffer->VecCount(); i++) {
525			dprintf("    [%lu] base %p, length %lu%s\n", i,
526				buffer->VecAt(i).iov_base, buffer->VecAt(i).iov_len,
527				buffer->UsesBounceBufferAt(i) ? ", bounce" : "");
528		}
529
530		dprintf("  remaining bytes: %lu\n", request.RemainingBytes());
531
532		// check results
533
534		const result_t& result = fResults[resultIndex];
535		if (result.count != buffer->VecCount())
536			panic("result count differs (expected %lu)\n", result.count);
537
538		for (uint32 i = 0; i < result.count; i++) {
539			const target_t& target = result.targets[i];
540			const iovec& vec = buffer->VecAt(i);
541
542			if (target.length != vec.iov_len)
543				_Panic("[%lu] length differs", i);
544
545			void* address;
546			if (target.uses_bounce_buffer) {
547				address = (void*)(target.address
548					+ (addr_t)buffer->PhysicalBounceBuffer());
549			} else
550				address = (void*)(target.address + fSuite.PhysicalDataBase());
551
552			if (address != vec.iov_base) {
553				_Panic("[%lu] address differs: %p, should be %p", i,
554					vec.iov_base, address);
555			}
556		}
557
558		_DoIO(operation);
559		operation.SetStatus(B_OK);
560		bool finished = operation.Finish();
561		bool isPartial = result.partial_begin || result.partial_end;
562		if (finished == (isPartial && fIsWrite))
563			_Panic("partial finished %s", finished ? "early" : "late");
564
565		if (!finished) {
566			dprintf("  operation not done yet!\n");
567			_DoIO(operation);
568			operation.SetStatus(B_OK);
569
570			isPartial = result.partial_begin && result.partial_end;
571			finished = operation.Finish();
572			if (finished == result.partial_begin && result.partial_end)
573				_Panic("partial finished %s", finished ? "early" : "late");
574
575			if (!finished) {
576				dprintf("  operation not done yet!\n");
577				_DoIO(operation);
578				operation.SetStatus(B_OK);
579
580				if (!operation.Finish())
581					_Panic("operation doesn't finish");
582			}
583		}
584
585		resultIndex++;
586	}
587
588	_CheckResults();
589}
590
591
592void
593Test::_Panic(const char* message,...)
594{
595	char buffer[1024];
596
597	va_list args;
598	va_start(args, message);
599	vsnprintf(buffer, sizeof(buffer), message, args);
600	va_end(args);
601
602	dprintf("test failed\n");
603	dprintf("  offset:  %lld\n", fOffset);
604	dprintf("  base:    %p (physical: %p)\n", (void*)fSuite.DataBase(),
605		(void*)fSuite.PhysicalDataBase());
606	dprintf("  length:  %lu\n", fLength);
607	dprintf("  write:   %d\n", fIsWrite);
608	dprintf("  flags:   %#lx\n", fFlags);
609	dprintf("  sources:\n");
610	for (uint32 i = 0; i < fSourceCount; i++) {
611		dprintf("    [%p, %lu]\n", fSourceVecs[i].iov_base,
612			fSourceVecs[i].iov_len);
613	}
614	for (uint32 i = 0; i < fResultCount; i++) {
615		const result_t& result = fResults[i];
616		dprintf("  result %lu:\n", i);
617		dprintf("    offset:  %lld\n", result.offset);
618		dprintf("    partial: %d/%d\n", result.partial_begin,
619			result.partial_end);
620
621		for (uint32 k = 0; k < result.count; k++) {
622			const target_t& target = result.targets[k];
623			dprintf("    [%p, %lu, %d]\n", (void*)target.address, target.length,
624				target.uses_bounce_buffer);
625		}
626	}
627
628	panic("%s", buffer);
629}
630
631
632static void
633run_tests_no_restrictions(TestSuiteContext& context)
634{
635	const dma_restrictions restrictions = {
636		0x0,	// low
637		0x0,	// high
638		0,		// alignment
639		0,		// boundary
640		0,		// max transfer
641		0,		// max segment count
642		0,		// max segment size
643		0		// flags
644	};
645
646	TestSuite suite(context, "no restrictions", restrictions, 512);
647
648	suite.AddTest(0, 1024, false, B_USER_IO_REQUEST)
649		.AddSource(0, 1024)
650		.NextResult(0, false, false)
651			.AddTarget(0, 1024, false);
652
653	// read partial begin/end
654	suite.AddTest(23, 1024, false, B_USER_IO_REQUEST)
655		.AddSource(0, 1024)
656		.NextResult(0, true, true)
657			.AddTarget(0, 23, true)
658			.AddTarget(0, 1024, false)
659			.AddTarget(23, 512 - 23, true);
660
661	// read less than a block
662	suite.AddTest(23, 30, false, B_USER_IO_REQUEST)
663		.AddSource(0, 1024)
664		.NextResult(0, true, true)
665			.AddTarget(0, 23, true)
666			.AddTarget(0, 30, false)
667			.AddTarget(23, 512 - 53, true);
668
669	// write begin/end
670	suite.AddTest(23, 1024, true, B_USER_IO_REQUEST)
671		.AddSource(0, 1024)
672		.NextResult(0, true, true)
673			.AddTarget(0, 512, true)
674			.AddTarget(489, 512, false)
675			.AddTarget(512, 512, true);
676
677	// read partial end, length < iovec length
678	suite.AddTest(0, 1028, false, B_USER_IO_REQUEST)
679		.AddSource(0, 512)
680		.AddSource(1024, 1024)
681		.NextResult(0, false, true)
682			.AddTarget(0, 512, false)
683			.AddTarget(1024, 516, false)
684			.AddTarget(0, 508, true);
685
686	// write partial end, length < iovec length
687	suite.AddTest(0, 1028, true, B_USER_IO_REQUEST)
688		.AddSource(0, 512)
689		.AddSource(1024, 1024)
690		.NextResult(0, false, true)
691			.AddTarget(0, 512, false)
692			.AddTarget(1024, 512, false)
693			.AddTarget(0, 512, true);
694
695	suite.Run();
696}
697
698
699static void
700run_tests_address_restrictions(TestSuiteContext& context)
701{
702	const dma_restrictions restrictions = {
703		context.PhysicalDataBase() + 512,	// low
704		0,		// high
705		0,		// alignment
706		0,		// boundary
707		0,		// max transfer
708		0,		// max segment count
709		0,		// max segment size
710		0		// flags
711	};
712
713	TestSuite suite(context, "address", restrictions, 512);
714
715	suite.AddTest(0, 1024, false, B_USER_IO_REQUEST)
716		.AddSource(0, 1024)
717		.NextResult(0, false, false)
718			.AddTarget(0, 512, true)
719			.AddTarget(512, 512, false);
720
721	suite.Run();
722}
723
724
725static void
726run_tests_alignment_restrictions(TestSuiteContext& context)
727{
728	const dma_restrictions restrictions = {
729		0x0,	// low
730		0x0,	// high
731		32,		// alignment
732		0,		// boundary
733		0,		// max transfer
734		0,		// max segment count
735		0,		// max segment size
736		0		// flags
737	};
738
739	TestSuite suite(context, "alignment", restrictions, 512);
740
741	suite.AddTest(0, 1024, false, B_PHYSICAL_IO_REQUEST)
742		.AddSource(16, 1024)
743		.NextResult(0, false, false)
744			.AddTarget(0, 1024, true);
745
746	suite.Run();
747}
748
749
750static void
751run_tests_boundary_restrictions(TestSuiteContext& context)
752{
753	const dma_restrictions restrictions = {
754		0x0,	// low
755		0x0,	// high
756		0,		// alignment
757		1024,	// boundary
758		0,		// max transfer
759		0,		// max segment count
760		0,		// max segment size
761		0		// flags
762	};
763
764	TestSuite suite(context, "boundary", restrictions, 512);
765
766	suite.AddTest(0, 2000, false, B_USER_IO_REQUEST)
767		.AddSource(0, 2048)
768		.NextResult(0, false, false)
769			.AddTarget(0, 1024, false)
770			.AddTarget(1024, 976, false)
771			.AddTarget(0, 48, true);
772
773	suite.Run();
774}
775
776
777static void
778run_tests_segment_restrictions(TestSuiteContext& context)
779{
780	const dma_restrictions restrictions = {
781		0x0,	// low
782		0x0,	// high
783		0,		// alignment
784		0,		// boundary
785		0,		// max transfer
786		4,		// max segment count
787		1024,	// max segment size
788		0		// flags
789	};
790
791	TestSuite suite(context, "segment", restrictions, 512);
792
793	suite.AddTest(0, 4096, false, B_USER_IO_REQUEST)
794		.AddSource(0, 4096)
795		.NextResult(0, false, false)
796			.AddTarget(0, 1024, false)
797			.AddTarget(1024, 1024, false)
798			.AddTarget(2048, 1024, false)
799			.AddTarget(3072, 1024, false);
800
801	suite.Run();
802}
803
804
805static void
806run_tests_transfer_restrictions(TestSuiteContext& context)
807{
808	const dma_restrictions restrictions = {
809		0x0,	// low
810		0x0,	// high
811		0,		// alignment
812		0,		// boundary
813		1024,	// max transfer
814		0,		// max segment count
815		0,		// max segment size
816		0		// flags
817	};
818
819	TestSuite suite(context, "transfer", restrictions, 512);
820
821	suite.AddTest(0, 4000, false, B_USER_IO_REQUEST)
822		.AddSource(0, 4096)
823		.NextResult(0, false, false)
824			.AddTarget(0, 1024, false)
825		.NextResult(0, false, false)
826			.AddTarget(1024, 1024, false)
827		.NextResult(0, false, false)
828			.AddTarget(2048, 1024, false)
829		.NextResult(0, false, false)
830			.AddTarget(3072, 1024 - 96, false)
831			.AddTarget(0, 96, true);
832
833	suite.Run();
834}
835
836
837static void
838run_tests_interesting_restrictions(TestSuiteContext& context)
839{
840	dma_restrictions restrictions = {
841		0x0,	// low
842		0x0,	// high
843		32,		// alignment
844		512,	// boundary
845		0,		// max transfer
846		0,		// max segment count
847		0,		// max segment size
848		0		// flags
849	};
850
851	TestSuite suite(context, "interesting", restrictions, 512);
852
853	// read with partial begin/end
854	suite.AddTest(32, 1000, false, B_USER_IO_REQUEST)
855		.AddSource(0, 1024)
856		.NextResult(0, true, true)
857			.AddTarget(0, 32, true)
858			.AddTarget(0, 512, false)
859			.AddTarget(512, 480, false)
860			.AddTarget(32, 480, true)
861			.AddTarget(512, 32, true);
862
863	// write with partial begin/end
864	suite.AddTest(32, 1000, true, B_USER_IO_REQUEST)
865		.AddSource(0, 1024)
866		.NextResult(0, true, true)
867			.AddTarget(0, 512, true)
868			.AddTarget(480, 32, false)
869			.AddTarget(512, 480, false)
870			.AddTarget(512, 512, true);
871
872	suite.Run();
873
874	restrictions = (dma_restrictions){
875		0x0,	// low
876		0x0,	// high
877		32,		// alignment
878		512,	// boundary
879		0,		// max transfer
880		4,		// max segment count
881		0,		// max segment size
882		0		// flags
883	};
884
885	TestSuite suite2(context, "interesting2", restrictions, 512);
886
887	suite2.AddTest(32, 1000, false, B_USER_IO_REQUEST)
888		.AddSource(0, 1024)
889		.NextResult(0, true, false)
890			.AddTarget(0, 32, true)
891			.AddTarget(0, 512, false)
892			.AddTarget(512, 480, false)
893		.NextResult(0, false, true)
894			.AddTarget(0, 512, true);
895
896	suite2.Run();
897}
898
899
900static void
901run_tests_mean_restrictions(TestSuiteContext& context)
902{
903	const dma_restrictions restrictions = {
904		context.PhysicalDataBase() + 1024,	// low
905		0x0,	// high
906		32,		// alignment
907		1024,	// boundary
908		0,		// max transfer
909		2,		// max segment count
910		512,	// max segment size
911		0		// flags
912	};
913
914	TestSuite suite(context, "mean", restrictions, 512);
915
916	suite.AddTest(0, 1024, false, B_USER_IO_REQUEST)
917		.AddSource(0, 1024)
918		.NextResult(0, false, false)
919			.AddTarget(0, 512, true)
920			.AddTarget(512, 512, true);
921
922	suite.AddTest(0, 1024, false, B_USER_IO_REQUEST)
923		.AddSource(1024 + 32, 1024)
924		.NextResult(0, false, false)
925			.AddTarget(1024 + 32, 512, false)
926		.NextResult(0, false, false)
927			.AddTarget(1568, 480, false)
928			.AddTarget(1568 + 480, 32, false);
929
930	suite.Run();
931}
932
933
934static void
935run_test()
936{
937	TestSuiteContext context;
938	status_t status = context.Init(4 * B_PAGE_SIZE);
939	if (status != B_OK)
940		return;
941
942	run_tests_no_restrictions(context);
943	run_tests_address_restrictions(context);
944	run_tests_alignment_restrictions(context);
945	run_tests_boundary_restrictions(context);
946	run_tests_segment_restrictions(context);
947	run_tests_transfer_restrictions(context);
948	run_tests_interesting_restrictions(context);
949	run_tests_mean_restrictions(context);
950
951	dprintf("All tests passed!\n");
952}
953
954
955//	#pragma mark - driver
956
957
958float
959dma_test_supports_device(device_node *parent)
960{
961	const char* bus = NULL;
962	if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)
963			== B_OK && !strcmp(bus, "generic"))
964		return 0.8;
965
966	return -1;
967}
968
969
970status_t
971dma_test_register_device(device_node *parent)
972{
973	device_attr attrs[] = {
974		{B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {string: "DMA Test"}},
975		{NULL}
976	};
977
978	return sDeviceManager->register_node(parent,
979		"drivers/disk/dma_resource_test/driver_v1", attrs, NULL, NULL);
980}
981
982
983status_t
984dma_test_init_driver(device_node *node, void **_driverCookie)
985{
986	sAreaSize = 10 * 1024 * 1024;
987	sArea = create_area("dma test", &sAreaAddress, B_ANY_KERNEL_ADDRESS,
988		sAreaSize, B_LAZY_LOCK, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
989	if (sArea < B_OK)
990		return sArea;
991
992	*_driverCookie = node;
993
994	run_test();
995	return B_OK;
996}
997
998
999void
1000dma_test_uninit_driver(void *driverCookie)
1001{
1002	delete_area(sArea);
1003}
1004
1005
1006status_t
1007dma_test_register_child_devices(void *driverCookie)
1008{
1009	return sDeviceManager->publish_device((device_node*)driverCookie,
1010		"disk/virtual/dma_test/raw",
1011		"drivers/disk/dma_resource_test/device_v1");
1012}
1013
1014
1015//	#pragma mark - device
1016
1017
1018status_t
1019dma_test_init_device(void *driverCookie, void **_deviceCookie)
1020{
1021	const dma_restrictions restrictions = {
1022		0x0,	// low
1023		0x0,	// high
1024		4,		// alignment
1025		0,		// boundary
1026		0,		// max transfer
1027		0,		// max segment count
1028		B_PAGE_SIZE, // max segment size
1029		0		// flags
1030	};
1031
1032	*_deviceCookie = driverCookie;
1033	sDMAResource = new(std::nothrow) DMAResource;
1034	if (sDMAResource == NULL)
1035		return B_NO_MEMORY;
1036
1037	status_t status = sDMAResource->Init(restrictions, DMA_TEST_BLOCK_SIZE,
1038		DMA_TEST_BUFFER_COUNT);
1039	if (status != B_OK) {
1040		delete sDMAResource;
1041		return status;
1042	}
1043
1044	sIOScheduler = new(std::nothrow) IOScheduler(sDMAResource);
1045	if (sIOScheduler == NULL) {
1046		delete sDMAResource;
1047		return B_NO_MEMORY;
1048	}
1049
1050	status = sIOScheduler->Init("dma test scheduler");
1051	if (status != B_OK) {
1052		delete sIOScheduler;
1053		delete sDMAResource;
1054		return status;
1055	}
1056
1057	sIOScheduler->SetCallback(&do_io, NULL);
1058	return B_OK;
1059}
1060
1061
1062void
1063dma_test_uninit_device(void *deviceCookie)
1064{
1065}
1066
1067
1068status_t
1069dma_test_open(void *deviceCookie, const char *path, int openMode,
1070	void **_cookie)
1071{
1072	return B_OK;
1073}
1074
1075
1076status_t
1077dma_test_close(void *cookie)
1078{
1079	return B_OK;
1080}
1081
1082
1083status_t
1084dma_test_free(void *cookie)
1085{
1086	return B_OK;
1087}
1088
1089
1090status_t
1091dma_test_read(void *cookie, off_t pos, void *buffer, size_t *_length)
1092{
1093	size_t length = *_length;
1094
1095	if (pos >= sAreaSize)
1096		return B_BAD_VALUE;
1097	if (pos + length > sAreaSize)
1098		length = sAreaSize - pos;
1099
1100#if 1
1101	IORequest request;
1102	status_t status = request.Init(pos, buffer, length, false,
1103		B_USER_IO_REQUEST);
1104	if (status != B_OK)
1105		return status;
1106
1107	status = sIOScheduler->ScheduleRequest(&request);
1108	if (status != B_OK)
1109		return status;
1110
1111	status = request.Wait(0, 0);
1112#else
1113	status_t status = user_memcpy(buffer, (uint8*)sAreaAddress + pos, length);
1114#endif
1115
1116	if (status == B_OK)
1117		*_length = length;
1118	return status;
1119}
1120
1121
1122status_t
1123dma_test_write(void *cookie, off_t pos, const void *buffer, size_t *_length)
1124{
1125	size_t length = *_length;
1126
1127	if (pos >= sAreaSize)
1128		return B_BAD_VALUE;
1129	if (pos + length > sAreaSize)
1130		length = sAreaSize - pos;
1131
1132#if 1
1133	IORequest request;
1134	status_t status = request.Init(pos, (void*)buffer, length, true,
1135		B_USER_IO_REQUEST);
1136	if (status != B_OK)
1137		return status;
1138
1139	status = sIOScheduler->ScheduleRequest(&request);
1140	if (status != B_OK)
1141		return status;
1142
1143	status = request.Wait(0, 0);
1144#else
1145	status_t status = user_memcpy((uint8*)sAreaAddress + pos, buffer, length);
1146#endif
1147
1148	if (status == B_OK)
1149		*_length = length;
1150
1151	return status;
1152}
1153
1154
1155status_t
1156dma_test_io(void *cookie, io_request *request)
1157{
1158	return B_BAD_VALUE;
1159}
1160
1161
1162status_t
1163dma_test_control(void *cookie, uint32 op, void *buffer, size_t length)
1164{
1165	switch (op) {
1166		case B_GET_DEVICE_SIZE:
1167			return user_memcpy(buffer, &sAreaSize, sizeof(size_t));
1168
1169		case B_SET_NONBLOCKING_IO:
1170		case B_SET_BLOCKING_IO:
1171			return B_OK;
1172
1173		case B_GET_READ_STATUS:
1174		case B_GET_WRITE_STATUS:
1175		{
1176			bool value = true;
1177			return user_memcpy(buffer, &value, sizeof(bool));
1178		}
1179
1180		case B_GET_GEOMETRY:
1181		case B_GET_BIOS_GEOMETRY:
1182		{
1183			device_geometry geometry;
1184			geometry.bytes_per_sector = DMA_TEST_BLOCK_SIZE;
1185			geometry.sectors_per_track = 1;
1186			geometry.cylinder_count = sAreaSize / DMA_TEST_BLOCK_SIZE;
1187			geometry.head_count = 1;
1188			geometry.device_type = B_DISK;
1189			geometry.removable = true;
1190			geometry.read_only = false;
1191			geometry.write_once = false;
1192
1193			return user_memcpy(buffer, &geometry, sizeof(device_geometry));
1194		}
1195
1196		case B_GET_MEDIA_STATUS:
1197		{
1198			status_t status = B_OK;
1199			return user_memcpy(buffer, &status, sizeof(status_t));
1200		}
1201
1202		case B_SET_UNINTERRUPTABLE_IO:
1203		case B_SET_INTERRUPTABLE_IO:
1204		case B_FLUSH_DRIVE_CACHE:
1205			return B_OK;
1206	}
1207	return B_BAD_VALUE;
1208}
1209
1210
1211module_dependency module_dependencies[] = {
1212	{B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager},
1213	{}
1214};
1215
1216
1217const static struct driver_module_info sDMATestDriverModule = {
1218	{
1219		"drivers/disk/dma_resource_test/driver_v1",
1220		0,
1221		NULL
1222	},
1223
1224	dma_test_supports_device,
1225	dma_test_register_device,
1226	dma_test_init_driver,
1227	dma_test_uninit_driver,
1228	dma_test_register_child_devices
1229};
1230
1231const static struct device_module_info sDMATestDeviceModule = {
1232	{
1233		"drivers/disk/dma_resource_test/device_v1",
1234		0,
1235		NULL
1236	},
1237
1238	dma_test_init_device,
1239	dma_test_uninit_device,
1240	NULL,
1241
1242	dma_test_open,
1243	dma_test_close,
1244	dma_test_free,
1245
1246	dma_test_read,
1247	dma_test_write,
1248	NULL,	// io
1249
1250	dma_test_control,
1251
1252	NULL,	// select
1253	NULL	// deselect
1254};
1255
1256const module_info* modules[] = {
1257	(module_info*)&sDMATestDriverModule,
1258	(module_info*)&sDMATestDeviceModule,
1259	NULL
1260};
1261