* Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2008, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*/
#include <stdio.h>
#include <string.h>
#include <device_manager.h>
#include <vm/vm.h>
#include "dma_resources.h"
#include "io_requests.h"
#include "IOSchedulerSimple.h"
#define DMA_TEST_BLOCK_SIZE 512
#define DMA_TEST_BUFFER_COUNT 10
#define DMA_TEST_BOUNCE_BUFFER_COUNT 128
class TestSuite;
class TestSuiteContext {
public:
TestSuiteContext();
~TestSuiteContext();
status_t Init(size_t size, bool contiguous = true);
addr_t DataBase() const { return fDataBase; }
addr_t PhysicalDataBase() const
{ return fPhysicalDataBase; }
addr_t SecondPhysicalDataBase() const
{ return fSecondPhysicalDataBase; }
bool IsContiguous() const
{ return fSecondPhysicalDataBase == 0; }
addr_t CompareBase() const { return fCompareBase; }
size_t Size() const { return fSize; }
private:
void _Uninit();
area_id fDataArea;
addr_t fDataBase;
addr_t fPhysicalDataBase;
addr_t fSecondPhysicalDataBase;
area_id fCompareArea;
addr_t fCompareBase;
size_t fSize;
};
class Test : public DoublyLinkedListLinkImpl<Test> {
public:
Test(TestSuite& suite, off_t offset, size_t length,
bool isWrite, uint32 flags);
Test& AddSource(addr_t base, size_t length);
Test& NextResult(off_t offset, bool partialBegin,
bool partialEnd);
Test& AddTarget(addr_t base, size_t length,
bool usesBounceBuffer);
void Run(DMAResource& resource);
private:
addr_t _SourceToVirtual(addr_t source);
addr_t _SourceToCompare(addr_t source);
void _Prepare();
void _CheckCompare();
void _CheckWrite();
void _CheckResults();
status_t _DoIO(IOOperation& operation);
void _Panic(const char* message,...);
TestSuite& fSuite;
off_t fOffset;
size_t fLength;
bool fIsWrite;
uint32 fFlags;
generic_io_vec fSourceVecs[32];
uint32 fSourceCount;
struct target_t {
addr_t address;
size_t length;
bool uses_bounce_buffer;
};
struct result_t {
off_t offset;
target_t targets[32];
uint32 count;
bool partial_begin;
bool partial_end;
};
result_t fResults[32];
uint32 fResultCount;
};
typedef DoublyLinkedList<Test> TestList;
class TestSuite {
public:
TestSuite(TestSuiteContext& context, const char* name,
const dma_restrictions& restrictions, size_t blockSize)
:
fContext(context)
{
dprintf("----- Run \"%s\" tests ---------------------------\n", name);
dprintf(" DMA restrictions: address %#" B_PRIxGENADDR " - %#"
B_PRIxGENADDR ", align %" B_PRIuGENADDR ", boundary %" B_PRIuGENADDR
",\n max transfer %" B_PRIuGENADDR ", max segs %" B_PRIx32
", max seg size %" B_PRIxGENADDR ", flags %" B_PRIx32 "\n\n",
restrictions.low_address, restrictions.high_address,
restrictions.alignment, restrictions.boundary,
restrictions.max_transfer_size, restrictions.max_segment_count,
restrictions.max_segment_size, restrictions.flags);
status_t status = fDMAResource.Init(restrictions, blockSize, 10, 10);
if (status != B_OK)
panic("initializing DMA resource failed: %s\n", strerror(status));
}
~TestSuite()
{
while (Test* test = fTests.RemoveHead()) {
delete test;
}
}
Test& AddTest(off_t offset, size_t length, bool isWrite, uint32 flags)
{
Test* test = new(std::nothrow) Test(*this, offset, length, isWrite,
flags);
fTests.Add(test);
return *test;
}
void Run()
{
TestList::Iterator iterator = fTests.GetIterator();
uint32 count = 1;
while (Test* test = iterator.Next()) {
dprintf("test %lu...\n", count++);
test->Run(fDMAResource);
}
}
addr_t DataBase() const { return fContext.DataBase(); }
addr_t PhysicalDataBase() const { return fContext.PhysicalDataBase(); }
addr_t SecondPhysicalDataBase() const
{ return fContext.SecondPhysicalDataBase(); }
bool IsContiguous() const { return fContext.IsContiguous(); }
addr_t CompareBase() const { return fContext.CompareBase(); }
size_t Size() const { return fContext.Size(); }
private:
TestSuiteContext& fContext;
DMAResource fDMAResource;
uint8* fBase;
uint8* fPhysicalBase;
size_t fSize;
TestList fTests;
};
struct device_manager_info* sDeviceManager;
static area_id sArea;
static size_t sAreaSize;
static void* sAreaAddress;
static DMAResource* sDMAResource;
static IOScheduler* sIOScheduler;
status_t
do_io(void* data, IOOperation* operation)
{
uint8* disk = (uint8*)sAreaAddress;
off_t offset = operation->Offset();
for (uint32 i = 0; i < operation->VecCount(); i++) {
const generic_io_vec& vec = operation->Vecs()[i];
generic_addr_t base = vec.base;
generic_size_t length = vec.length;
if (operation->IsWrite())
vm_memcpy_from_physical(disk + offset, base, length, false);
else
vm_memcpy_to_physical(base, disk + offset, length, false);
}
if (sIOScheduler != NULL)
sIOScheduler->OperationCompleted(operation, B_OK, operation->Length());
return B_OK;
}
TestSuiteContext::TestSuiteContext()
:
fDataArea(-1),
fCompareArea(-1),
fSize(0)
{
}
TestSuiteContext::~TestSuiteContext()
{
_Uninit();
}
void
TestSuiteContext::_Uninit()
{
delete_area(fDataArea);
delete_area(fCompareArea);
}
status_t
TestSuiteContext::Init(size_t size, bool contiguous)
{
if (!contiguous) {
if (size != B_PAGE_SIZE * 2)
return B_NOT_SUPPORTED;
while (true) {
fDataArea = create_area("data buffer", (void**)&fDataBase,
B_ANY_KERNEL_ADDRESS, size, B_FULL_LOCK,
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
if (fDataArea < B_OK)
return fDataArea;
physical_entry entry[2];
get_memory_map((void*)fDataBase, 2 * B_PAGE_SIZE, entry, 2);
if (entry[0].size == B_PAGE_SIZE) {
fPhysicalDataBase = (addr_t)entry[0].address;
fSecondPhysicalDataBase = (addr_t)entry[1].address;
dprintf("DMA Test area %p, physical %#" B_PRIxPHYSADDR
", second physical %#" B_PRIxPHYSADDR "\n",
(void*)fDataBase, entry[0].address, entry[1].address);
break;
}
delete_area(fDataArea);
}
} else {
fDataArea = create_area("data buffer", (void**)&fDataBase,
B_ANY_KERNEL_ADDRESS, size, B_CONTIGUOUS,
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
if (fDataArea < B_OK)
return fDataArea;
physical_entry entry;
get_memory_map((void*)fDataBase, B_PAGE_SIZE, &entry, 1);
fPhysicalDataBase = (addr_t)entry.address;
fSecondPhysicalDataBase = 0;
dprintf("DMA Test area %p, physical %#" B_PRIxPHYSADDR "\n",
(void*)fDataBase, entry.address);
}
fCompareArea = create_area("compare buffer", (void**)&fCompareBase,
B_ANY_KERNEL_ADDRESS, size, B_FULL_LOCK,
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
if (fCompareArea < B_OK)
return fCompareArea;
fSize = size;
return B_OK;
}
Test::Test(TestSuite& suite, off_t offset, size_t length, bool isWrite,
uint32 flags)
:
fSuite(suite),
fOffset(offset),
fLength(length),
fIsWrite(isWrite),
fFlags(flags),
fSourceCount(0),
fResultCount(0)
{
}
Test&
Test::AddSource(addr_t address, size_t length)
{
if (fSuite.IsContiguous() || (fFlags & B_PHYSICAL_IO_REQUEST) != 0
|| address < B_PAGE_SIZE) {
fSourceVecs[fSourceCount].base
= ((fFlags & B_PHYSICAL_IO_REQUEST) == 0
? fSuite.DataBase() : fSuite.PhysicalDataBase()) + address;
} else {
fSourceVecs[fSourceCount].base
= fSuite.SecondPhysicalDataBase() + address;
}
fSourceVecs[fSourceCount].length = length;
fSourceCount++;
return *this;
}
Test&
Test::NextResult(off_t offset, bool partialBegin, bool partialEnd)
{
fResults[fResultCount].offset = offset;
fResults[fResultCount].count = 0;
fResults[fResultCount].partial_begin = partialBegin;
fResults[fResultCount].partial_end = partialEnd;
fResultCount++;
return *this;
}
Test&
Test::AddTarget(addr_t base, size_t length, bool usesBounceBuffer)
{
struct result_t& result = fResults[fResultCount - 1];
struct target_t& target = result.targets[result.count++];
target.address = base;
target.length = length;
target.uses_bounce_buffer = usesBounceBuffer;
return *this;
}
addr_t
Test::_SourceToVirtual(addr_t source)
{
if ((fFlags & B_PHYSICAL_IO_REQUEST) != 0) {
if (!fSuite.IsContiguous() && source >= B_PAGE_SIZE)
return source - fSuite.SecondPhysicalDataBase() + fSuite.DataBase();
return source - fSuite.PhysicalDataBase() + fSuite.DataBase();
}
return source;
}
addr_t
Test::_SourceToCompare(addr_t source)
{
if ((fFlags & B_PHYSICAL_IO_REQUEST) != 0) {
if (!fSuite.IsContiguous() && source >= B_PAGE_SIZE) {
return source - fSuite.SecondPhysicalDataBase()
+ fSuite.CompareBase();
}
return source - fSuite.PhysicalDataBase() + fSuite.CompareBase();
}
return source - fSuite.DataBase() + fSuite.CompareBase();
}
void
Test::_Prepare()
{
uint8* disk = (uint8*)sAreaAddress;
for (size_t i = 0; i < sAreaSize; i++) {
disk[i] = i % 26 + 'a';
}
memset((void*)fSuite.DataBase(), 0xcc, fSuite.Size());
if (fIsWrite) {
off_t offset = fOffset;
size_t length = fLength;
for (uint32 i = 0; i < fSourceCount; i++) {
uint8* data = (uint8*)_SourceToVirtual(fSourceVecs[i].base);
size_t vecLength = min_c(fSourceVecs[i].length, length);
for (uint32 j = 0; j < vecLength; j++) {
data[j] = (offset + j) % 10 + '0';
}
offset += vecLength;
length -= vecLength;
}
}
memset((void*)fSuite.CompareBase(), 0xcc, fSuite.Size());
if (fIsWrite) {
off_t offset = fOffset;
size_t length = fLength;
for (uint32 i = 0; i < fSourceCount; i++) {
uint8* compare = (uint8*)_SourceToCompare(fSourceVecs[i].base);
size_t vecLength = min_c(fSourceVecs[i].length, length);
memcpy(compare, (void*)_SourceToVirtual(fSourceVecs[i].base),
vecLength);
offset += vecLength;
length -= vecLength;
}
} else {
off_t offset = fOffset;
size_t length = fLength;
for (uint32 i = 0; i < fSourceCount; i++) {
uint8* compare = (uint8*)_SourceToCompare(fSourceVecs[i].base);
size_t vecLength = min_c(fSourceVecs[i].length, length);
memcpy(compare, disk + offset, vecLength);
offset += vecLength;
length -= vecLength;
}
}
if (fIsWrite)
_CheckCompare();
}
void
Test::_CheckCompare()
{
uint8* data = (uint8*)fSuite.DataBase();
uint8* compare = (uint8*)fSuite.CompareBase();
for (size_t i = 0; i < fSuite.Size(); i++) {
if (data[i] != compare[i]) {
dprintf("offset %lu differs, %s:\n", i,
fIsWrite ? "write" : "read");
i &= ~63;
dump_block((char*)&data[i], min_c(64, fSuite.Size() - i), " ");
dprintf("should be:\n");
dump_block((char*)&compare[i], min_c(64, fSuite.Size() - i), " ");
_Panic("Data %s differs", fIsWrite ? "write" : "read");
}
}
}
void
Test::_CheckWrite()
{
_CheckCompare();
uint8* disk = (uint8*)sAreaAddress;
for (size_t i = 0; i < sAreaSize; i++) {
if (i >= fOffset && i < fOffset + fLength)
continue;
if (disk[i] != i % 26 + 'a') {
dprintf("disk[i] %c, expected %c, i %lu, fLength + fOffset %lld\n",
disk[i], (int)(i % 26 + 'a'), i, fLength + fOffset);
dprintf("offset %lu differs, touched innocent data:\n", i);
i &= ~63;
dump_block((char*)&disk[i], min_c(64, fSuite.Size() - i), " ");
_Panic("Data %s differs", fIsWrite ? "write" : "read");
}
}
off_t offset = fOffset;
size_t length = fLength;
for (uint32 i = 0; i < fSourceCount; i++) {
uint8* data = (uint8*)_SourceToVirtual(fSourceVecs[i].base);
size_t vecLength = min_c(fSourceVecs[i].length, length);
for (uint32 j = 0; j < vecLength; j++) {
if (disk[offset + j] != data[j]) {
dprintf("offset %lu differs, found on disk:\n", j);
j &= ~63;
dump_block((char*)&disk[offset + j],
min_c(64, fSuite.Size() - i), " ");
dprintf("should be:\n");
dump_block((char*)&data[j], min_c(64, fSuite.Size() - j), " ");
_Panic("Data write differs");
}
}
offset += vecLength;
length -= vecLength;
}
}
void
Test::_CheckResults()
{
if (fIsWrite)
_CheckWrite();
else
_CheckCompare();
}
status_t
Test::_DoIO(IOOperation& operation)
{
return do_io(NULL, &operation);
}
void
Test::Run(DMAResource& resource)
{
_Prepare();
IORequest request;
status_t status = request.Init(fOffset, fSourceVecs, fSourceCount,
fLength, fIsWrite, fFlags);
if (status != B_OK)
_Panic("request init failed: %s\n", strerror(status));
uint32 resultIndex = 0;
IOOperation operation;
while (request.RemainingBytes() > 0) {
if (resultIndex >= fResultCount)
_Panic("no results left");
status_t status = resource.TranslateNext(&request, &operation, 0);
if (status != B_OK) {
_Panic("DMAResource::TranslateNext() failed: %s\n",
strerror(status));
break;
}
DMABuffer* buffer = operation.Buffer();
dprintf("IOOperation: offset %" B_PRIdOFF ", length %" B_PRIuGENADDR
" (%" B_PRIdOFF "/%" B_PRIuGENADDR ")\n", operation.Offset(),
operation.Length(), operation.OriginalOffset(),
operation.OriginalLength());
dprintf(" DMABuffer %p, %lu vecs, bounce buffer: %p (%p) %s\n", buffer,
buffer->VecCount(), buffer->BounceBufferAddress(),
(void*)buffer->PhysicalBounceBufferAddress(),
operation.UsesBounceBuffer() ? "used" : "unused");
for (uint32 i = 0; i < buffer->VecCount(); i++) {
dprintf(" [%" B_PRIu32 "] base %#" B_PRIxGENADDR ", length %"
B_PRIuGENADDR "%s\n", i, buffer->VecAt(i).base,
buffer->VecAt(i).length,
buffer->UsesBounceBufferAt(i) ? ", bounce" : "");
}
dprintf(" remaining bytes: %" B_PRIuGENADDR "\n",
request.RemainingBytes());
const result_t& result = fResults[resultIndex];
if (result.count != buffer->VecCount())
panic("result count differs (expected %lu)\n", result.count);
for (uint32 i = 0; i < result.count; i++) {
const target_t& target = result.targets[i];
const generic_io_vec& vec = buffer->VecAt(i);
if (target.length != vec.length)
_Panic("[%lu] length differs", i);
generic_addr_t address;
if (target.uses_bounce_buffer) {
address = target.address
+ (addr_t)buffer->PhysicalBounceBufferAddress();
} else if (fSuite.IsContiguous() || target.address < B_PAGE_SIZE) {
address = target.address + fSuite.PhysicalDataBase();
} else {
address = target.address - B_PAGE_SIZE
+ fSuite.SecondPhysicalDataBase();
}
if (address != vec.base) {
_Panic("[%" B_PRIu32 "] address differs: %#" B_PRIxGENADDR
", should be %#" B_PRIuGENADDR "", i, vec.base, address);
}
}
_DoIO(operation);
operation.SetStatus(B_OK);
bool finished = operation.Finish();
bool isPartial = result.partial_begin || result.partial_end;
if (finished == (isPartial && fIsWrite))
_Panic("partial finished %s", finished ? "early" : "late");
if (!finished) {
dprintf(" operation not done yet!\n");
_DoIO(operation);
operation.SetStatus(B_OK);
isPartial = result.partial_begin && result.partial_end;
finished = operation.Finish();
if (finished == result.partial_begin && result.partial_end)
_Panic("partial finished %s", finished ? "early" : "late");
if (!finished) {
dprintf(" operation not done yet!\n");
_DoIO(operation);
operation.SetStatus(B_OK);
if (!operation.Finish())
_Panic("operation doesn't finish");
}
}
request.OperationFinished(&operation, operation.Status(),
false,
operation.OriginalOffset() - operation.Parent()->Offset()
+ operation.OriginalLength());
resultIndex++;
}
_CheckResults();
}
void
Test::_Panic(const char* message,...)
{
char buffer[1024];
va_list args;
va_start(args, message);
vsnprintf(buffer, sizeof(buffer), message, args);
va_end(args);
dprintf("test failed\n");
dprintf(" offset: %lld\n", fOffset);
dprintf(" base: %p (physical: %p)\n", (void*)fSuite.DataBase(),
(void*)fSuite.PhysicalDataBase());
dprintf(" length: %lu\n", fLength);
dprintf(" write: %d\n", fIsWrite);
dprintf(" flags: %#lx\n", fFlags);
dprintf(" sources:\n");
for (uint32 i = 0; i < fSourceCount; i++) {
dprintf(" [%#" B_PRIxGENADDR ", %" B_PRIuGENADDR "]\n",
fSourceVecs[i].base, fSourceVecs[i].length);
}
for (uint32 i = 0; i < fResultCount; i++) {
const result_t& result = fResults[i];
dprintf(" result %lu:\n", i);
dprintf(" offset: %lld\n", result.offset);
dprintf(" partial: %d/%d\n", result.partial_begin,
result.partial_end);
for (uint32 k = 0; k < result.count; k++) {
const target_t& target = result.targets[k];
dprintf(" [%p, %lu, %d]\n", (void*)target.address, target.length,
target.uses_bounce_buffer);
}
}
panic("%s", buffer);
}
static void
run_tests_no_restrictions(TestSuiteContext& context)
{
const dma_restrictions restrictions = {
0x0,
0x0,
0,
0,
0,
0,
0,
0
};
TestSuite suite(context, "no restrictions", restrictions, 512);
suite.AddTest(0, 1024, false, 0)
.AddSource(0, 1024)
.NextResult(0, false, false)
.AddTarget(0, 1024, false);
suite.AddTest(23, 1024, false, 0)
.AddSource(0, 1024)
.NextResult(0, true, true)
.AddTarget(0, 23, true)
.AddTarget(0, 1024, false)
.AddTarget(23, 512 - 23, true);
suite.AddTest(23, 30, false, 0)
.AddSource(0, 1024)
.NextResult(0, true, true)
.AddTarget(0, 23, true)
.AddTarget(0, 30, false)
.AddTarget(23, 512 - 53, true);
suite.AddTest(23, 1024, true, 0)
.AddSource(0, 1024)
.NextResult(0, true, true)
.AddTarget(0, 512, true)
.AddTarget(489, 512, false)
.AddTarget(512, 512, true);
suite.AddTest(0, 1028, false, 0)
.AddSource(0, 512)
.AddSource(1024, 1024)
.NextResult(0, false, true)
.AddTarget(0, 512, false)
.AddTarget(1024, 516, false)
.AddTarget(0, 508, true);
suite.AddTest(0, 1028, true, 0)
.AddSource(0, 512)
.AddSource(1024, 1024)
.NextResult(0, false, true)
.AddTarget(0, 512, false)
.AddTarget(1024, 512, false)
.AddTarget(0, 512, true);
suite.Run();
}
static void
run_tests_address_restrictions(TestSuiteContext& context)
{
const dma_restrictions restrictions = {
context.PhysicalDataBase() + 512,
0,
0,
0,
0,
0,
0,
0
};
TestSuite suite(context, "address", restrictions, 512);
suite.AddTest(0, 1024, false, 0)
.AddSource(0, 1024)
.NextResult(0, false, false)
.AddTarget(0, 512, true)
.AddTarget(512, 512, false);
suite.Run();
}
static void
run_tests_alignment_restrictions(TestSuiteContext& context)
{
const dma_restrictions restrictions = {
0x0,
0x0,
32,
0,
0,
0,
0,
0
};
TestSuite suite(context, "alignment", restrictions, 512);
suite.AddTest(0, 1024, false, B_PHYSICAL_IO_REQUEST)
.AddSource(16, 1024)
.NextResult(0, false, false)
.AddTarget(0, 1024, true);
suite.AddTest(0, 2559, true, B_PHYSICAL_IO_REQUEST)
.AddSource(0, 2559)
.NextResult(0, false, true)
.AddTarget(0, 2048, false)
.AddTarget(0, 512, true);
suite.AddTest(0, 51, true, B_PHYSICAL_IO_REQUEST)
.AddSource(0, 4096)
.NextResult(0, false, true)
.AddTarget(0, 512, true);
suite.AddTest(32, 51, true, B_PHYSICAL_IO_REQUEST)
.AddSource(0, 4096)
.NextResult(0, true, false)
.AddTarget(0, 512, true);
suite.Run();
}
static void
run_tests_boundary_restrictions(TestSuiteContext& context)
{
const dma_restrictions restrictions = {
0x0,
0x0,
0,
1024,
0,
0,
0,
0
};
TestSuite suite(context, "boundary", restrictions, 512);
suite.AddTest(0, 2000, false, 0)
.AddSource(0, 2048)
.NextResult(0, false, false)
.AddTarget(0, 1024, false)
.AddTarget(1024, 976, false)
.AddTarget(0, 48, true);
suite.Run();
}
static void
run_tests_segment_restrictions(TestSuiteContext& context)
{
const dma_restrictions restrictions = {
0x0,
0x0,
0,
0,
0,
4,
1024,
0
};
TestSuite suite(context, "segment", restrictions, 512);
suite.AddTest(0, 4096, false, 0)
.AddSource(0, 4096)
.NextResult(0, false, false)
.AddTarget(0, 1024, false)
.AddTarget(1024, 1024, false)
.AddTarget(2048, 1024, false)
.AddTarget(3072, 1024, false);
suite.AddTest(0, 2560, false, B_PHYSICAL_IO_REQUEST)
.AddSource(0, 512)
.AddSource(1024, 512)
.AddSource(2048, 512)
.AddSource(3072, 512)
.AddSource(4096, 512)
.NextResult(0, false, false)
.AddTarget(0, 512, false)
.AddTarget(1024, 512, false)
.AddTarget(2048, 512, false)
.AddTarget(3072, 512, false)
.NextResult(2048, false, false)
.AddTarget(4096, 512, false);
suite.Run();
}
static void
run_tests_transfer_restrictions(TestSuiteContext& context)
{
const dma_restrictions restrictions = {
0x0,
0x0,
0,
0,
1024,
0,
0,
0
};
TestSuite suite(context, "transfer", restrictions, 512);
suite.AddTest(0, 4000, false, 0)
.AddSource(0, 4096)
.NextResult(0, false, false)
.AddTarget(0, 1024, false)
.NextResult(0, false, false)
.AddTarget(1024, 1024, false)
.NextResult(0, false, false)
.AddTarget(2048, 1024, false)
.NextResult(0, false, false)
.AddTarget(3072, 1024 - 96, false)
.AddTarget(0, 96, true);
suite.Run();
}
static void
run_tests_interesting_restrictions(TestSuiteContext& context)
{
dma_restrictions restrictions = {
0x0,
0x0,
32,
512,
0,
0,
0,
0
};
TestSuite suite(context, "interesting", restrictions, 512);
suite.AddTest(32, 1000, false, 0)
.AddSource(0, 1024)
.NextResult(0, true, true)
.AddTarget(0, 32, true)
.AddTarget(0, 512, false)
.AddTarget(512, 480, false)
.AddTarget(32, 480, true)
.AddTarget(512, 32, true);
suite.AddTest(32, 1000, true, 0)
.AddSource(0, 1024)
.NextResult(0, true, true)
.AddTarget(0, 512, true)
.AddTarget(480, 32, false)
.AddTarget(512, 480, false)
.AddTarget(512, 512, true);
suite.Run();
restrictions = (dma_restrictions){
0x0,
0x0,
32,
512,
0,
4,
0,
0
};
TestSuite suite2(context, "interesting2", restrictions, 512);
suite2.AddTest(32, 1000, false, 0)
.AddSource(0, 1024)
.NextResult(0, true, false)
.AddTarget(0, 32, true)
.AddTarget(0, 512, false)
.AddTarget(512, 480, false)
.NextResult(0, false, true)
.AddTarget(0, 512, true);
suite2.Run();
}
static void
run_tests_mean_restrictions(TestSuiteContext& context)
{
const dma_restrictions restrictions = {
context.PhysicalDataBase() + 1024,
0x0,
32,
1024,
0,
2,
512,
0
};
TestSuite suite(context, "mean", restrictions, 512);
suite.AddTest(0, 1024, false, 0)
.AddSource(0, 1024)
.NextResult(0, false, false)
.AddTarget(0, 512, true)
.AddTarget(512, 512, true);
suite.AddTest(0, 1024, false, 0)
.AddSource(1024 + 32, 1024)
.NextResult(0, false, false)
.AddTarget(1024 + 32, 512, false)
.NextResult(0, false, false)
.AddTarget(1568, 480, false)
.AddTarget(1568 + 480, 32, false);
suite.Run();
}
static void
run_tests_non_contiguous_no_restrictions(TestSuiteContext& context)
{
const dma_restrictions restrictions = {
0x0,
0x0,
0,
0,
0,
0,
0,
0
};
TestSuite suite(context, "non contiguous, no restrictions", restrictions,
512);
suite.AddTest(0, 8192, false, 0)
.AddSource(0, 8192)
.NextResult(0, false, false)
.AddTarget(0, 4096, false)
.AddTarget(4096, 4096, false);
suite.Run();
}
static void
run_test()
{
TestSuiteContext context;
status_t status = context.Init(4 * B_PAGE_SIZE);
if (status != B_OK)
return;
run_tests_no_restrictions(context);
run_tests_address_restrictions(context);
run_tests_alignment_restrictions(context);
run_tests_boundary_restrictions(context);
run_tests_segment_restrictions(context);
run_tests_transfer_restrictions(context);
run_tests_interesting_restrictions(context);
run_tests_mean_restrictions(context);
status = context.Init(2 * B_PAGE_SIZE, false);
if (status != B_OK)
return;
run_tests_non_contiguous_no_restrictions(context);
dprintf("All tests passed!\n");
}
static float
dma_test_supports_device(device_node *parent)
{
const char* bus = NULL;
if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)
== B_OK && !strcmp(bus, "generic"))
return 0.8;
return -1;
}
static status_t
dma_test_register_device(device_node *parent)
{
device_attr attrs[] = {
{B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {string: "DMA Test"}},
{NULL}
};
return sDeviceManager->register_node(parent,
"drivers/disk/dma_resource_test/driver_v1", attrs, NULL, NULL);
}
static status_t
dma_test_init_driver(device_node *node, void **_driverCookie)
{
sAreaSize = 10 * 1024 * 1024;
sArea = create_area("dma test", &sAreaAddress, B_ANY_KERNEL_ADDRESS,
sAreaSize, B_LAZY_LOCK, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
if (sArea < B_OK)
return sArea;
*_driverCookie = node;
run_test();
return B_OK;
}
static void
dma_test_uninit_driver(void *driverCookie)
{
delete_area(sArea);
}
static status_t
dma_test_register_child_devices(void *driverCookie)
{
return sDeviceManager->publish_device((device_node*)driverCookie,
"disk/virtual/dma_test/raw",
"drivers/disk/dma_resource_test/device_v1");
}
static status_t
dma_test_init_device(void *driverCookie, void **_deviceCookie)
{
const dma_restrictions restrictions = {
0x0,
0x0,
4,
0,
0,
0,
B_PAGE_SIZE,
0
};
*_deviceCookie = driverCookie;
sDMAResource = new(std::nothrow) DMAResource;
if (sDMAResource == NULL)
return B_NO_MEMORY;
status_t status = sDMAResource->Init(restrictions, DMA_TEST_BLOCK_SIZE,
DMA_TEST_BUFFER_COUNT, DMA_TEST_BOUNCE_BUFFER_COUNT);
if (status != B_OK) {
delete sDMAResource;
return status;
}
sIOScheduler = new(std::nothrow) IOSchedulerSimple(sDMAResource);
if (sIOScheduler == NULL) {
delete sDMAResource;
return B_NO_MEMORY;
}
status = sIOScheduler->Init("dma test scheduler");
if (status != B_OK) {
delete sIOScheduler;
delete sDMAResource;
return status;
}
sIOScheduler->SetCallback(&do_io, NULL);
return B_OK;
}
static void
dma_test_uninit_device(void *deviceCookie)
{
}
static status_t
dma_test_open(void *deviceCookie, const char *path, int openMode,
void **_cookie)
{
return B_OK;
}
static status_t
dma_test_close(void *cookie)
{
return B_OK;
}
static status_t
dma_test_free(void *cookie)
{
return B_OK;
}
static status_t
dma_test_read(void *cookie, off_t pos, void *buffer, size_t *_length)
{
size_t length = *_length;
if (pos >= sAreaSize)
return B_BAD_VALUE;
if (pos + length > sAreaSize)
length = sAreaSize - pos;
#if 1
IORequest request;
status_t status = request.Init(pos, (addr_t)buffer, length, false, 0);
if (status != B_OK)
return status;
status = sIOScheduler->ScheduleRequest(&request);
if (status != B_OK)
return status;
status = request.Wait(0, 0);
dprintf("dma_test_read(): request.Wait() returned: %s\n", strerror(status));
#else
status_t status = user_memcpy(buffer, (uint8*)sAreaAddress + pos, length);
#endif
if (status == B_OK)
*_length = length;
return status;
}
static status_t
dma_test_write(void *cookie, off_t pos, const void *buffer, size_t *_length)
{
size_t length = *_length;
if (pos >= sAreaSize)
return B_BAD_VALUE;
if (pos + length > sAreaSize)
length = sAreaSize - pos;
#if 1
IORequest request;
status_t status = request.Init(pos, (addr_t)buffer, length, true, 0);
if (status != B_OK)
return status;
status = sIOScheduler->ScheduleRequest(&request);
if (status != B_OK)
return status;
status = request.Wait(0, 0);
dprintf("dma_test_write(): request.Wait() returned: %s\n",
strerror(status));
#else
status_t status = user_memcpy((uint8*)sAreaAddress + pos, buffer, length);
#endif
if (status == B_OK)
*_length = length;
return status;
}
static status_t
dma_test_io(void *cookie, io_request *request)
{
dprintf("dma_test_io(%p)\n", request);
return sIOScheduler->ScheduleRequest(request);
}
static status_t
dma_test_control(void *cookie, uint32 op, void *buffer, size_t length)
{
switch (op) {
case B_GET_DEVICE_SIZE:
return user_memcpy(buffer, &sAreaSize, sizeof(size_t));
case B_SET_NONBLOCKING_IO:
case B_SET_BLOCKING_IO:
return B_OK;
case B_GET_READ_STATUS:
case B_GET_WRITE_STATUS:
{
bool value = true;
return user_memcpy(buffer, &value, sizeof(bool));
}
case B_GET_GEOMETRY:
case B_GET_BIOS_GEOMETRY:
{
device_geometry geometry;
geometry.bytes_per_sector = DMA_TEST_BLOCK_SIZE;
geometry.sectors_per_track = 1;
geometry.cylinder_count = sAreaSize / DMA_TEST_BLOCK_SIZE;
geometry.head_count = 1;
geometry.device_type = B_DISK;
geometry.removable = true;
geometry.read_only = false;
geometry.write_once = false;
return user_memcpy(buffer, &geometry, sizeof(device_geometry));
}
case B_GET_MEDIA_STATUS:
{
status_t status = B_OK;
return user_memcpy(buffer, &status, sizeof(status_t));
}
case B_SET_UNINTERRUPTABLE_IO:
case B_SET_INTERRUPTABLE_IO:
case B_FLUSH_DRIVE_CACHE:
return B_OK;
}
return B_BAD_VALUE;
}
module_dependency module_dependencies[] = {
{B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager},
{}
};
static const struct driver_module_info sDMATestDriverModule = {
{
"drivers/disk/dma_resource_test/driver_v1",
0,
NULL
},
dma_test_supports_device,
dma_test_register_device,
dma_test_init_driver,
dma_test_uninit_driver,
dma_test_register_child_devices
};
static const struct device_module_info sDMATestDeviceModule = {
{
"drivers/disk/dma_resource_test/device_v1",
0,
NULL
},
dma_test_init_device,
dma_test_uninit_device,
NULL,
dma_test_open,
dma_test_close,
dma_test_free,
dma_test_read,
dma_test_write,
dma_test_io,
dma_test_control,
NULL,
NULL
};
const module_info* modules[] = {
(module_info*)&sDMATestDriverModule,
(module_info*)&sDMATestDeviceModule,
NULL
};