dma_resource_test.cpp revision 33f9067b
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_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", "drivers/disk/dma_test/device_v1");
1011}
1012
1013
1014//	#pragma mark - device
1015
1016
1017status_t
1018dma_test_init_device(void *driverCookie, void **_deviceCookie)
1019{
1020	const dma_restrictions restrictions = {
1021		0x0,	// low
1022		0x0,	// high
1023		4,		// alignment
1024		0,		// boundary
1025		0,		// max transfer
1026		0,		// max segment count
1027		B_PAGE_SIZE, // max segment size
1028		0		// flags
1029	};
1030
1031	*_deviceCookie = driverCookie;
1032	sDMAResource = new(std::nothrow) DMAResource;
1033	if (sDMAResource == NULL)
1034		return B_NO_MEMORY;
1035
1036	status_t status = sDMAResource->Init(restrictions, DMA_TEST_BLOCK_SIZE,
1037		DMA_TEST_BUFFER_COUNT);
1038	if (status != B_OK) {
1039		delete sDMAResource;
1040		return status;
1041	}
1042
1043	sIOScheduler = new(std::nothrow) IOScheduler(sDMAResource);
1044	if (sIOScheduler == NULL) {
1045		delete sDMAResource;
1046		return B_NO_MEMORY;
1047	}
1048
1049	status = sIOScheduler->Init("dma test scheduler");
1050	if (status != B_OK) {
1051		delete sIOScheduler;
1052		delete sDMAResource;
1053		return status;
1054	}
1055
1056	sIOScheduler->SetCallback(&do_io, NULL);
1057	return B_OK;
1058}
1059
1060
1061void
1062dma_test_uninit_device(void *deviceCookie)
1063{
1064}
1065
1066
1067status_t
1068dma_test_open(void *deviceCookie, const char *path, int openMode,
1069	void **_cookie)
1070{
1071	return B_OK;
1072}
1073
1074
1075status_t
1076dma_test_close(void *cookie)
1077{
1078	return B_OK;
1079}
1080
1081
1082status_t
1083dma_test_free(void *cookie)
1084{
1085	return B_OK;
1086}
1087
1088
1089status_t
1090dma_test_read(void *cookie, off_t pos, void *buffer, size_t *_length)
1091{
1092	size_t length = *_length;
1093
1094	if (pos >= sAreaSize)
1095		return B_BAD_VALUE;
1096	if (pos + length > sAreaSize)
1097		length = sAreaSize - pos;
1098
1099#if 0
1100	IORequest request;
1101	status_t status = request.Init(pos, buffer, length, false,
1102		B_USER_IO_REQUEST);
1103	if (status != B_OK)
1104		return status;
1105
1106	status = sIOScheduler->ScheduleRequest(&request);
1107	if (status != B_OK)
1108		return status;
1109
1110	// TODO: wait for I/O request to finish!
1111	while (request.Status() > B_OK) {
1112		snooze(10000);
1113	}
1114#else
1115	status_t status = user_memcpy(buffer, (uint8*)sAreaAddress + pos, length);
1116#endif
1117
1118	if (status == B_OK)
1119		*_length = length;
1120	return status;
1121}
1122
1123
1124status_t
1125dma_test_write(void *cookie, off_t pos, const void *buffer, size_t *_length)
1126{
1127	size_t length = *_length;
1128
1129	if (pos >= sAreaSize)
1130		return B_BAD_VALUE;
1131	if (pos + length > sAreaSize)
1132		length = sAreaSize - pos;
1133
1134#if 0
1135	IORequest request;
1136	status_t status = request.Init(pos, (void*)buffer, length, true,
1137		B_USER_IO_REQUEST);
1138	if (status != B_OK)
1139		return status;
1140
1141	status = sIOScheduler->ScheduleRequest(&request);
1142	if (status != B_OK)
1143		return status;
1144
1145	// TODO: wait for I/O request to finish!
1146	while (request.Status() > B_OK) {
1147		snooze(10000);
1148	}
1149#else
1150	status_t status = user_memcpy((uint8*)sAreaAddress + pos, buffer, length);
1151#endif
1152
1153	if (status == B_OK)
1154		*_length = length;
1155
1156	return status;
1157}
1158
1159
1160status_t
1161dma_test_io(void *cookie, io_request *request)
1162{
1163	return B_BAD_VALUE;
1164}
1165
1166
1167status_t
1168dma_test_control(void *cookie, uint32 op, void *buffer, size_t length)
1169{
1170	switch (op) {
1171		case B_GET_DEVICE_SIZE:
1172			return user_memcpy(buffer, &sAreaSize, sizeof(size_t));
1173
1174		case B_SET_NONBLOCKING_IO:
1175		case B_SET_BLOCKING_IO:
1176			return B_OK;
1177
1178		case B_GET_READ_STATUS:
1179		case B_GET_WRITE_STATUS:
1180		{
1181			bool value = true;
1182			return user_memcpy(buffer, &value, sizeof(bool));
1183		}
1184
1185		case B_GET_GEOMETRY:
1186		case B_GET_BIOS_GEOMETRY:
1187		{
1188			device_geometry geometry;
1189			geometry.bytes_per_sector = DMA_TEST_BLOCK_SIZE;
1190			geometry.sectors_per_track = 1;
1191			geometry.cylinder_count = sAreaSize / DMA_TEST_BLOCK_SIZE;
1192			geometry.head_count = 1;
1193			geometry.device_type = B_DISK;
1194			geometry.removable = true;
1195			geometry.read_only = false;
1196			geometry.write_once = false;
1197
1198			return user_memcpy(buffer, &geometry, sizeof(device_geometry));
1199		}
1200
1201		case B_GET_MEDIA_STATUS:
1202		{
1203			status_t status = B_OK;
1204			return user_memcpy(buffer, &status, sizeof(status_t));
1205		}
1206
1207		case B_SET_UNINTERRUPTABLE_IO:
1208		case B_SET_INTERRUPTABLE_IO:
1209		case B_FLUSH_DRIVE_CACHE:
1210			return B_OK;
1211	}
1212	return B_BAD_VALUE;
1213}
1214
1215
1216module_dependency module_dependencies[] = {
1217	{B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager},
1218	{}
1219};
1220
1221
1222const static struct driver_module_info sDMATestDriverModule = {
1223	{
1224		"drivers/disk/dma_test/driver_v1",
1225		0,
1226		NULL
1227	},
1228
1229	dma_test_supports_device,
1230	dma_test_register_device,
1231	dma_test_init_driver,
1232	dma_test_uninit_driver,
1233	dma_test_register_child_devices
1234};
1235
1236const static struct device_module_info sDMATestDeviceModule = {
1237	{
1238		"drivers/disk/dma_test/device_v1",
1239		0,
1240		NULL
1241	},
1242
1243	dma_test_init_device,
1244	dma_test_uninit_device,
1245	NULL,
1246
1247	dma_test_open,
1248	dma_test_close,
1249	dma_test_free,
1250
1251	dma_test_read,
1252	dma_test_write,
1253	NULL,	// io
1254
1255	dma_test_control,
1256
1257	NULL,	// select
1258	NULL	// deselect
1259};
1260
1261const module_info* modules[] = {
1262	(module_info*)&sDMATestDriverModule,
1263	(module_info*)&sDMATestDeviceModule,
1264	NULL
1265};
1266