16969690aSAxel Dörfler/*
24535495dSIngo Weinhold * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
36969690aSAxel Dörfler * Copyright 2008, Axel D��rfler, axeld@pinc-software.de.
46969690aSAxel Dörfler * Distributed under the terms of the MIT License.
56969690aSAxel Dörfler */
66969690aSAxel Dörfler
7435c43f5SIngo Weinhold
86969690aSAxel Dörfler#include "dma_resources.h"
96969690aSAxel Dörfler
1070e2d4acSAxel Dörfler#include <device_manager.h>
1139b5c374SAxel Dörfler
126969690aSAxel Dörfler#include <kernel.h>
136969690aSAxel Dörfler#include <util/AutoLock.h>
14a8ad734fSIngo Weinhold#include <vm/vm.h>
156969690aSAxel Dörfler
16aa4ba93eSIngo Weinhold#include "IORequest.h"
176969690aSAxel Dörfler
186969690aSAxel Dörfler
197f12cc54SIngo Weinhold//#define TRACE_DMA_RESOURCE
208faff60cSAxel Dörfler#ifdef TRACE_DMA_RESOURCE
218faff60cSAxel Dörfler#	define TRACE(x...) dprintf(x)
228faff60cSAxel Dörfler#else
238faff60cSAxel Dörfler#	define TRACE(x...) ;
248faff60cSAxel Dörfler#endif
258faff60cSAxel Dörfler
268faff60cSAxel Dörfler
2739b5c374SAxel Dörflerextern device_manager_info gDeviceManagerModule;
2839b5c374SAxel Dörfler
29435c43f5SIngo Weinholdconst phys_size_t kMaxBounceBufferSize = 4 * B_PAGE_SIZE;
306969690aSAxel Dörfler
316969690aSAxel Dörfler
326969690aSAxel DörflerDMABuffer*
33025f7c32SIngo WeinholdDMABuffer::Create(size_t count)
346969690aSAxel Dörfler{
356969690aSAxel Dörfler	DMABuffer* buffer = (DMABuffer*)malloc(
36435c43f5SIngo Weinhold		sizeof(DMABuffer) + sizeof(generic_io_vec) * (count - 1));
376969690aSAxel Dörfler	if (buffer == NULL)
386969690aSAxel Dörfler		return NULL;
396969690aSAxel Dörfler
406969690aSAxel Dörfler	buffer->fVecCount = count;
416969690aSAxel Dörfler
426969690aSAxel Dörfler	return buffer;
436969690aSAxel Dörfler}
446969690aSAxel Dörfler
456969690aSAxel Dörfler
466969690aSAxel Dörflervoid
476969690aSAxel DörflerDMABuffer::SetVecCount(uint32 count)
486969690aSAxel Dörfler{
496969690aSAxel Dörfler	fVecCount = count;
506969690aSAxel Dörfler}
516969690aSAxel Dörfler
526969690aSAxel Dörfler
536969690aSAxel Dörflervoid
54435c43f5SIngo WeinholdDMABuffer::AddVec(generic_addr_t base, generic_size_t size)
556969690aSAxel Dörfler{
56435c43f5SIngo Weinhold	generic_io_vec& vec = fVecs[fVecCount++];
57435c43f5SIngo Weinhold	vec.base = base;
58435c43f5SIngo Weinhold	vec.length = size;
596969690aSAxel Dörfler}
606969690aSAxel Dörfler
616969690aSAxel Dörfler
628faff60cSAxel Dörflerbool
638faff60cSAxel DörflerDMABuffer::UsesBounceBufferAt(uint32 index)
648faff60cSAxel Dörfler{
65025f7c32SIngo Weinhold	if (index >= fVecCount || fBounceBuffer == NULL)
668faff60cSAxel Dörfler		return false;
678faff60cSAxel Dörfler
68435c43f5SIngo Weinhold	return fVecs[index].base >= fBounceBuffer->physical_address
69435c43f5SIngo Weinhold		&& fVecs[index].base
70025f7c32SIngo Weinhold				< fBounceBuffer->physical_address + fBounceBuffer->size;
718faff60cSAxel Dörfler}
728faff60cSAxel Dörfler
738faff60cSAxel Dörfler
74f8a59924SAxel Dörflervoid
75f8a59924SAxel DörflerDMABuffer::Dump() const
76f8a59924SAxel Dörfler{
77f8a59924SAxel Dörfler	kprintf("DMABuffer at %p\n", this);
78f8a59924SAxel Dörfler
791d578e15SIngo Weinhold	kprintf("  bounce buffer:      %p (physical %#" B_PRIxPHYSADDR ")\n",
80025f7c32SIngo Weinhold		fBounceBuffer->address, fBounceBuffer->physical_address);
811d578e15SIngo Weinhold	kprintf("  bounce buffer size: %" B_PRIxPHYSADDR "\n", fBounceBuffer->size);
824be4fc6bSAlex Smith	kprintf("  vecs:               %" B_PRIu32 "\n", fVecCount);
83f8a59924SAxel Dörfler
84f8a59924SAxel Dörfler	for (uint32 i = 0; i < fVecCount; i++) {
85435c43f5SIngo Weinhold		kprintf("    [%" B_PRIu32 "] %#" B_PRIxGENADDR ", %" B_PRIuGENADDR "\n",
86435c43f5SIngo Weinhold			i, fVecs[i].base, fVecs[i].length);
87f8a59924SAxel Dörfler	}
88f8a59924SAxel Dörfler}
89f8a59924SAxel Dörfler
90f8a59924SAxel Dörfler
916969690aSAxel Dörfler//	#pragma mark -
926969690aSAxel Dörfler
936969690aSAxel Dörfler
946969690aSAxel DörflerDMAResource::DMAResource()
95718d7149SJérôme Duval	:
966121ae66SJérôme Duval	fScratchVecs(NULL)
976969690aSAxel Dörfler{
986969690aSAxel Dörfler	mutex_init(&fLock, "dma resource");
996969690aSAxel Dörfler}
1006969690aSAxel Dörfler
1016969690aSAxel Dörfler
1026969690aSAxel DörflerDMAResource::~DMAResource()
1036969690aSAxel Dörfler{
1043c47ce84SJérôme Duval	mutex_lock(&fLock);
1056969690aSAxel Dörfler	mutex_destroy(&fLock);
1066969690aSAxel Dörfler	free(fScratchVecs);
107025f7c32SIngo Weinhold
108025f7c32SIngo Weinhold// TODO: Delete DMABuffers and BounceBuffers!
1096969690aSAxel Dörfler}
1106969690aSAxel Dörfler
1116969690aSAxel Dörfler
11239b5c374SAxel Dörflerstatus_t
113435c43f5SIngo WeinholdDMAResource::Init(device_node* node, generic_size_t blockSize,
114435c43f5SIngo Weinhold	uint32 bufferCount, uint32 bounceBufferCount)
11539b5c374SAxel Dörfler{
11639b5c374SAxel Dörfler	dma_restrictions restrictions;
11739b5c374SAxel Dörfler	memset(&restrictions, 0, sizeof(dma_restrictions));
11839b5c374SAxel Dörfler
11939b5c374SAxel Dörfler	// TODO: add DMA attributes instead of reusing block_io's
12039b5c374SAxel Dörfler
12139b5c374SAxel Dörfler	uint32 value;
12239b5c374SAxel Dörfler	if (gDeviceManagerModule.get_attr_uint32(node,
12370e2d4acSAxel Dörfler			B_DMA_ALIGNMENT, &value, true) == B_OK)
124a8ad734fSIngo Weinhold		restrictions.alignment = (generic_size_t)value + 1;
12539b5c374SAxel Dörfler
12639b5c374SAxel Dörfler	if (gDeviceManagerModule.get_attr_uint32(node,
12770e2d4acSAxel Dörfler			B_DMA_BOUNDARY, &value, true) == B_OK)
128a8ad734fSIngo Weinhold		restrictions.boundary = (generic_size_t)value + 1;
12939b5c374SAxel Dörfler
13039b5c374SAxel Dörfler	if (gDeviceManagerModule.get_attr_uint32(node,
13170e2d4acSAxel Dörfler			B_DMA_MAX_SEGMENT_BLOCKS, &value, true) == B_OK)
132a8ad734fSIngo Weinhold		restrictions.max_segment_size = (generic_size_t)value * blockSize;
13339b5c374SAxel Dörfler
13439b5c374SAxel Dörfler	if (gDeviceManagerModule.get_attr_uint32(node,
13570e2d4acSAxel Dörfler			B_DMA_MAX_TRANSFER_BLOCKS, &value, true) == B_OK)
136a8ad734fSIngo Weinhold		restrictions.max_transfer_size = (generic_size_t)value * blockSize;
13739b5c374SAxel Dörfler
13839b5c374SAxel Dörfler	if (gDeviceManagerModule.get_attr_uint32(node,
13970e2d4acSAxel Dörfler			B_DMA_MAX_SEGMENT_COUNT, &value, true) == B_OK)
14061b1a536SAxel Dörfler		restrictions.max_segment_count = value;
14139b5c374SAxel Dörfler
142ce97aa12SIngo Weinhold	uint64 value64;
143ce97aa12SIngo Weinhold	if (gDeviceManagerModule.get_attr_uint64(node,
144ce97aa12SIngo Weinhold			B_DMA_LOW_ADDRESS, &value64, true) == B_OK) {
145ce97aa12SIngo Weinhold		restrictions.low_address = value64;
146ce97aa12SIngo Weinhold	}
147ce97aa12SIngo Weinhold
148ce97aa12SIngo Weinhold	if (gDeviceManagerModule.get_attr_uint64(node,
149ce97aa12SIngo Weinhold			B_DMA_HIGH_ADDRESS, &value64, true) == B_OK) {
150ce97aa12SIngo Weinhold		restrictions.high_address = value64;
151ce97aa12SIngo Weinhold	}
152ce97aa12SIngo Weinhold
153025f7c32SIngo Weinhold	return Init(restrictions, blockSize, bufferCount, bounceBufferCount);
15439b5c374SAxel Dörfler}
15539b5c374SAxel Dörfler
15639b5c374SAxel Dörfler
1576969690aSAxel Dörflerstatus_t
158435c43f5SIngo WeinholdDMAResource::Init(const dma_restrictions& restrictions,
159435c43f5SIngo Weinhold	generic_size_t blockSize, uint32 bufferCount, uint32 bounceBufferCount)
1606969690aSAxel Dörfler{
1616969690aSAxel Dörfler	fRestrictions = restrictions;
1626969690aSAxel Dörfler	fBlockSize = blockSize == 0 ? 1 : blockSize;
1636969690aSAxel Dörfler	fBufferCount = bufferCount;
164025f7c32SIngo Weinhold	fBounceBufferCount = bounceBufferCount;
1656969690aSAxel Dörfler	fBounceBufferSize = 0;
1666969690aSAxel Dörfler
1676969690aSAxel Dörfler	if (fRestrictions.high_address == 0)
168435c43f5SIngo Weinhold		fRestrictions.high_address = ~(generic_addr_t)0;
1696969690aSAxel Dörfler	if (fRestrictions.max_segment_count == 0)
1706969690aSAxel Dörfler		fRestrictions.max_segment_count = 16;
1716969690aSAxel Dörfler	if (fRestrictions.alignment == 0)
1726969690aSAxel Dörfler		fRestrictions.alignment = 1;
17345a206a7SAxel Dörfler	if (fRestrictions.max_transfer_size == 0)
174435c43f5SIngo Weinhold		fRestrictions.max_transfer_size = ~(generic_size_t)0;
17545a206a7SAxel Dörfler	if (fRestrictions.max_segment_size == 0)
176435c43f5SIngo Weinhold		fRestrictions.max_segment_size = ~(generic_size_t)0;
1776969690aSAxel Dörfler
1786969690aSAxel Dörfler	if (_NeedsBoundsBuffers()) {
1798faff60cSAxel Dörfler		fBounceBufferSize = fRestrictions.max_segment_size
1808faff60cSAxel Dörfler			* min_c(fRestrictions.max_segment_count, 4);
1816969690aSAxel Dörfler		if (fBounceBufferSize > kMaxBounceBufferSize)
1828faff60cSAxel Dörfler			fBounceBufferSize = kMaxBounceBufferSize;
1838faff60cSAxel Dörfler		TRACE("DMAResource::Init(): chose bounce buffer size %lu\n",
1848faff60cSAxel Dörfler			fBounceBufferSize);
1856969690aSAxel Dörfler	}
1866969690aSAxel Dörfler
1871d578e15SIngo Weinhold	dprintf("DMAResource@%p: low/high %" B_PRIxGENADDR "/%" B_PRIxGENADDR
1881d578e15SIngo Weinhold		", max segment count %" B_PRIu32 ", align %" B_PRIuGENADDR ", "
1891d578e15SIngo Weinhold		"boundary %" B_PRIuGENADDR ", max transfer %" B_PRIuGENADDR
1901d578e15SIngo Weinhold		", max segment size %" B_PRIuGENADDR "\n", this,
19139b5c374SAxel Dörfler		fRestrictions.low_address, fRestrictions.high_address,
19239b5c374SAxel Dörfler		fRestrictions.max_segment_count, fRestrictions.alignment,
19339b5c374SAxel Dörfler		fRestrictions.boundary, fRestrictions.max_transfer_size,
19439b5c374SAxel Dörfler		fRestrictions.max_segment_size);
19539b5c374SAxel Dörfler
196435c43f5SIngo Weinhold	fScratchVecs = (generic_io_vec*)malloc(
197435c43f5SIngo Weinhold		sizeof(generic_io_vec) * fRestrictions.max_segment_count);
1986969690aSAxel Dörfler	if (fScratchVecs == NULL)
1996969690aSAxel Dörfler		return B_NO_MEMORY;
2006969690aSAxel Dörfler
2016969690aSAxel Dörfler	for (size_t i = 0; i < fBufferCount; i++) {
2026969690aSAxel Dörfler		DMABuffer* buffer;
203025f7c32SIngo Weinhold		status_t error = CreateBuffer(&buffer);
2046969690aSAxel Dörfler		if (error != B_OK)
2056969690aSAxel Dörfler			return error;
2066969690aSAxel Dörfler
2076969690aSAxel Dörfler		fDMABuffers.Add(buffer);
2086969690aSAxel Dörfler	}
2096969690aSAxel Dörfler
210025f7c32SIngo Weinhold	// TODO: create bounce buffers in as few areas as feasible
211025f7c32SIngo Weinhold	for (size_t i = 0; i < fBounceBufferCount; i++) {
212025f7c32SIngo Weinhold		DMABounceBuffer* buffer;
213025f7c32SIngo Weinhold		status_t error = CreateBounceBuffer(&buffer);
214025f7c32SIngo Weinhold		if (error != B_OK)
215025f7c32SIngo Weinhold			return error;
216025f7c32SIngo Weinhold
217025f7c32SIngo Weinhold		fBounceBuffers.Add(buffer);
218025f7c32SIngo Weinhold	}
219025f7c32SIngo Weinhold
2206969690aSAxel Dörfler	return B_OK;
2216969690aSAxel Dörfler}
2226969690aSAxel Dörfler
2236969690aSAxel Dörfler
2246969690aSAxel Dörflerstatus_t
225025f7c32SIngo WeinholdDMAResource::CreateBuffer(DMABuffer** _buffer)
226025f7c32SIngo Weinhold{
227025f7c32SIngo Weinhold	DMABuffer* buffer = DMABuffer::Create(fRestrictions.max_segment_count);
228025f7c32SIngo Weinhold	if (buffer == NULL)
229025f7c32SIngo Weinhold		return B_NO_MEMORY;
230025f7c32SIngo Weinhold
231025f7c32SIngo Weinhold	*_buffer = buffer;
232025f7c32SIngo Weinhold	return B_OK;
233025f7c32SIngo Weinhold}
234025f7c32SIngo Weinhold
235025f7c32SIngo Weinhold
236025f7c32SIngo Weinholdstatus_t
237025f7c32SIngo WeinholdDMAResource::CreateBounceBuffer(DMABounceBuffer** _buffer)
2386969690aSAxel Dörfler{
2396969690aSAxel Dörfler	void* bounceBuffer = NULL;
240435c43f5SIngo Weinhold	phys_addr_t physicalBase = 0;
2416969690aSAxel Dörfler	area_id area = -1;
242435c43f5SIngo Weinhold	phys_size_t size = ROUNDUP(fBounceBufferSize, B_PAGE_SIZE);
2436969690aSAxel Dörfler
244a8ad734fSIngo Weinhold	virtual_address_restrictions virtualRestrictions = {};
245a8ad734fSIngo Weinhold	virtualRestrictions.address_specification = B_ANY_KERNEL_ADDRESS;
246a8ad734fSIngo Weinhold	physical_address_restrictions physicalRestrictions = {};
247a8ad734fSIngo Weinhold	physicalRestrictions.low_address = fRestrictions.low_address;
248a8ad734fSIngo Weinhold	physicalRestrictions.high_address = fRestrictions.high_address;
249a8ad734fSIngo Weinhold	physicalRestrictions.alignment = fRestrictions.alignment;
250a8ad734fSIngo Weinhold	physicalRestrictions.boundary = fRestrictions.boundary;
251a8ad734fSIngo Weinhold	area = create_area_etc(B_SYSTEM_TEAM, "dma buffer", size, B_CONTIGUOUS,
252d1f280c8SHamish Morrison		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, 0, 0, &virtualRestrictions,
253a8ad734fSIngo Weinhold		&physicalRestrictions, &bounceBuffer);
254025f7c32SIngo Weinhold	if (area < B_OK)
255025f7c32SIngo Weinhold		return area;
256025f7c32SIngo Weinhold
257025f7c32SIngo Weinhold	physical_entry entry;
258025f7c32SIngo Weinhold	if (get_memory_map(bounceBuffer, size, &entry, 1) != B_OK) {
259025f7c32SIngo Weinhold		panic("get_memory_map() failed.");
260025f7c32SIngo Weinhold		delete_area(area);
261025f7c32SIngo Weinhold		return B_ERROR;
262025f7c32SIngo Weinhold	}
2636969690aSAxel Dörfler
26464d79effSIngo Weinhold	physicalBase = entry.address;
2656969690aSAxel Dörfler
266a8ad734fSIngo Weinhold	ASSERT(fRestrictions.high_address >= physicalBase + size);
2676969690aSAxel Dörfler
268025f7c32SIngo Weinhold	DMABounceBuffer* buffer = new(std::nothrow) DMABounceBuffer;
2696969690aSAxel Dörfler	if (buffer == NULL) {
2706969690aSAxel Dörfler		delete_area(area);
2716969690aSAxel Dörfler		return B_NO_MEMORY;
2726969690aSAxel Dörfler	}
2736969690aSAxel Dörfler
274025f7c32SIngo Weinhold	buffer->address = bounceBuffer;
275025f7c32SIngo Weinhold	buffer->physical_address = physicalBase;
276025f7c32SIngo Weinhold	buffer->size = size;
277025f7c32SIngo Weinhold
2786969690aSAxel Dörfler	*_buffer = buffer;
2796969690aSAxel Dörfler	return B_OK;
2806969690aSAxel Dörfler}
2816969690aSAxel Dörfler
2826969690aSAxel Dörfler
28345a206a7SAxel Dörflerinline void
284435c43f5SIngo WeinholdDMAResource::_RestrictBoundaryAndSegmentSize(generic_addr_t base,
285435c43f5SIngo Weinhold	generic_addr_t& length)
28645a206a7SAxel Dörfler{
28745a206a7SAxel Dörfler	if (length > fRestrictions.max_segment_size)
28845a206a7SAxel Dörfler		length = fRestrictions.max_segment_size;
28945a206a7SAxel Dörfler	if (fRestrictions.boundary > 0) {
290435c43f5SIngo Weinhold		generic_addr_t baseBoundary = base / fRestrictions.boundary;
29145a206a7SAxel Dörfler		if (baseBoundary
29245a206a7SAxel Dörfler				!= (base + (length - 1)) / fRestrictions.boundary) {
29345a206a7SAxel Dörfler			length = (baseBoundary + 1) * fRestrictions.boundary - base;
29445a206a7SAxel Dörfler		}
29545a206a7SAxel Dörfler	}
29645a206a7SAxel Dörfler}
29745a206a7SAxel Dörfler
29845a206a7SAxel Dörfler
2998faff60cSAxel Dörflervoid
300435c43f5SIngo WeinholdDMAResource::_CutBuffer(DMABuffer& buffer, phys_addr_t& physicalBounceBuffer,
301435c43f5SIngo Weinhold	phys_size_t& bounceLeft, generic_size_t toCut)
3028faff60cSAxel Dörfler{
3038faff60cSAxel Dörfler	int32 vecCount = buffer.VecCount();
3048faff60cSAxel Dörfler	for (int32 i = vecCount - 1; toCut > 0 && i >= 0; i--) {
305435c43f5SIngo Weinhold		generic_io_vec& vec = buffer.VecAt(i);
306435c43f5SIngo Weinhold		generic_size_t length = vec.length;
3078faff60cSAxel Dörfler		bool inBounceBuffer = buffer.UsesBounceBufferAt(i);
3088faff60cSAxel Dörfler
3098faff60cSAxel Dörfler		if (length <= toCut) {
3108faff60cSAxel Dörfler			vecCount--;
3118faff60cSAxel Dörfler			toCut -= length;
3128faff60cSAxel Dörfler
3138faff60cSAxel Dörfler			if (inBounceBuffer) {
3148faff60cSAxel Dörfler				bounceLeft += length;
3158faff60cSAxel Dörfler				physicalBounceBuffer -= length;
3168faff60cSAxel Dörfler			}
3178faff60cSAxel Dörfler		} else {
318435c43f5SIngo Weinhold			vec.length -= toCut;
3198faff60cSAxel Dörfler
3208faff60cSAxel Dörfler			if (inBounceBuffer) {
3218faff60cSAxel Dörfler				bounceLeft += toCut;
3228faff60cSAxel Dörfler				physicalBounceBuffer -= toCut;
3238faff60cSAxel Dörfler			}
3248faff60cSAxel Dörfler			break;
3258faff60cSAxel Dörfler		}
3268faff60cSAxel Dörfler	}
3278faff60cSAxel Dörfler
3288faff60cSAxel Dörfler	buffer.SetVecCount(vecCount);
3298faff60cSAxel Dörfler}
3308faff60cSAxel Dörfler
3318faff60cSAxel Dörfler
3328faff60cSAxel Dörfler/*!	Adds \a length bytes from the bounce buffer to the DMABuffer \a buffer.
3338faff60cSAxel Dörfler	Takes care of boundary, and segment restrictions. \a length must be aligned.
3348faff60cSAxel Dörfler	If \a fixedLength is requested, this function will fail if it cannot
3358faff60cSAxel Dörfler	satisfy the request.
3368faff60cSAxel Dörfler
3378faff60cSAxel Dörfler	\return 0 if the request cannot be satisfied. There could have been some
3388faff60cSAxel Dörfler		additions to the DMA buffer, and you will need to cut them back.
3398faff60cSAxel Dörfler	TODO: is that what we want here?
3408faff60cSAxel Dörfler	\return >0 the number of bytes added to the buffer.
3418faff60cSAxel Dörfler*/
342435c43f5SIngo Weinholdphys_size_t
343435c43f5SIngo WeinholdDMAResource::_AddBounceBuffer(DMABuffer& buffer,
344435c43f5SIngo Weinhold	phys_addr_t& physicalBounceBuffer, phys_size_t& bounceLeft,
345435c43f5SIngo Weinhold	generic_size_t length, bool fixedLength)
3468faff60cSAxel Dörfler{
3478faff60cSAxel Dörfler	if (bounceLeft < length) {
3488faff60cSAxel Dörfler		if (fixedLength)
3498faff60cSAxel Dörfler			return 0;
3508faff60cSAxel Dörfler
3518faff60cSAxel Dörfler		length = bounceLeft;
3528faff60cSAxel Dörfler	}
3538faff60cSAxel Dörfler
354435c43f5SIngo Weinhold	phys_size_t bounceUsed = 0;
3558faff60cSAxel Dörfler
3568faff60cSAxel Dörfler	uint32 vecCount = buffer.VecCount();
3578faff60cSAxel Dörfler	if (vecCount > 0) {
3588faff60cSAxel Dörfler		// see if we can join the bounce buffer with the previously last vec
359435c43f5SIngo Weinhold		generic_io_vec& vec = buffer.VecAt(vecCount - 1);
360435c43f5SIngo Weinhold		generic_addr_t vecBase = vec.base;
361435c43f5SIngo Weinhold		generic_size_t vecLength = vec.length;
3628faff60cSAxel Dörfler
3638faff60cSAxel Dörfler		if (vecBase + vecLength == physicalBounceBuffer) {
3648faff60cSAxel Dörfler			vecLength += length;
3658faff60cSAxel Dörfler			_RestrictBoundaryAndSegmentSize(vecBase, vecLength);
3668faff60cSAxel Dörfler
367435c43f5SIngo Weinhold			generic_size_t lengthDiff = vecLength - vec.length;
3688faff60cSAxel Dörfler			length -= lengthDiff;
3698faff60cSAxel Dörfler
3708faff60cSAxel Dörfler			physicalBounceBuffer += lengthDiff;
3718faff60cSAxel Dörfler			bounceLeft -= lengthDiff;
3728faff60cSAxel Dörfler			bounceUsed += lengthDiff;
3738faff60cSAxel Dörfler
374435c43f5SIngo Weinhold			vec.length = vecLength;
3758faff60cSAxel Dörfler		}
3768faff60cSAxel Dörfler	}
3778faff60cSAxel Dörfler
3788faff60cSAxel Dörfler	while (length > 0) {
3798faff60cSAxel Dörfler		// We need to add another bounce vec
3808faff60cSAxel Dörfler
3818faff60cSAxel Dörfler		if (vecCount == fRestrictions.max_segment_count)
3828faff60cSAxel Dörfler			return fixedLength ? 0 : bounceUsed;
3838faff60cSAxel Dörfler
384435c43f5SIngo Weinhold		generic_addr_t vecLength = length;
3858faff60cSAxel Dörfler		_RestrictBoundaryAndSegmentSize(physicalBounceBuffer, vecLength);
3868faff60cSAxel Dörfler
387435c43f5SIngo Weinhold		buffer.AddVec(physicalBounceBuffer, vecLength);
3888faff60cSAxel Dörfler		vecCount++;
3898faff60cSAxel Dörfler
3908faff60cSAxel Dörfler		physicalBounceBuffer += vecLength;
3918faff60cSAxel Dörfler		bounceLeft -= vecLength;
3928faff60cSAxel Dörfler		bounceUsed += vecLength;
3938faff60cSAxel Dörfler		length -= vecLength;
3948faff60cSAxel Dörfler	}
3958faff60cSAxel Dörfler
3968faff60cSAxel Dörfler	return bounceUsed;
3978faff60cSAxel Dörfler}
3988faff60cSAxel Dörfler
3998faff60cSAxel Dörfler
4006969690aSAxel Dörflerstatus_t
4010316483fSIngo WeinholdDMAResource::TranslateNext(IORequest* request, IOOperation* operation,
402435c43f5SIngo Weinhold	generic_size_t maxOperationLength)
4036969690aSAxel Dörfler{
4046969690aSAxel Dörfler	IOBuffer* buffer = request->Buffer();
40545a206a7SAxel Dörfler	off_t originalOffset = request->Offset() + request->Length()
40645a206a7SAxel Dörfler		- request->RemainingBytes();
40745a206a7SAxel Dörfler	off_t offset = originalOffset;
408435c43f5SIngo Weinhold	generic_size_t partialBegin = offset & (fBlockSize - 1);
40945a206a7SAxel Dörfler
41045a206a7SAxel Dörfler	// current iteration state
41145a206a7SAxel Dörfler	uint32 vecIndex = request->VecIndex();
41245a206a7SAxel Dörfler	uint32 vecOffset = request->VecOffset();
413435c43f5SIngo Weinhold	generic_size_t totalLength = min_c(request->RemainingBytes(),
41445a206a7SAxel Dörfler		fRestrictions.max_transfer_size);
4156969690aSAxel Dörfler
4160316483fSIngo Weinhold	if (maxOperationLength > 0
4170316483fSIngo Weinhold		&& maxOperationLength < totalLength + partialBegin) {
4180316483fSIngo Weinhold		totalLength = maxOperationLength - partialBegin;
4190316483fSIngo Weinhold	}
4200316483fSIngo Weinhold
4216969690aSAxel Dörfler	MutexLocker locker(fLock);
4226969690aSAxel Dörfler
4236969690aSAxel Dörfler	DMABuffer* dmaBuffer = fDMABuffers.RemoveHead();
4246969690aSAxel Dörfler	if (dmaBuffer == NULL)
4256969690aSAxel Dörfler		return B_BUSY;
4266969690aSAxel Dörfler
42745a206a7SAxel Dörfler	dmaBuffer->SetVecCount(0);
42845a206a7SAxel Dörfler
429435c43f5SIngo Weinhold	generic_io_vec* vecs = NULL;
4306969690aSAxel Dörfler	uint32 segmentCount = 0;
4316969690aSAxel Dörfler
4329951d585SIngo Weinhold	TRACE("  offset %Ld, remaining size: %lu, block size %lu -> partial: %lu\n",
4339951d585SIngo Weinhold		offset, request->RemainingBytes(), fBlockSize, partialBegin);
4346969690aSAxel Dörfler
4356969690aSAxel Dörfler	if (buffer->IsVirtual()) {
4366969690aSAxel Dörfler		// Unless we need the bounce buffer anyway, we have to translate the
4376969690aSAxel Dörfler		// virtual addresses to physical addresses, so we can check the DMA
4386969690aSAxel Dörfler		// restrictions.
4399951d585SIngo Weinhold		TRACE("  buffer is virtual %s\n", buffer->IsUser() ? "user" : "kernel");
4408faff60cSAxel Dörfler		// TODO: !partialOperation || totalLength >= fBlockSize
4418faff60cSAxel Dörfler		// TODO: Maybe enforce fBounceBufferSize >= 2 * fBlockSize.
44245a206a7SAxel Dörfler		if (true) {
443435c43f5SIngo Weinhold			generic_size_t transferLeft = totalLength;
4446969690aSAxel Dörfler			vecs = fScratchVecs;
4456969690aSAxel Dörfler
4468faff60cSAxel Dörfler			TRACE("  create physical map (for %ld vecs)\n", buffer->VecCount());
44745a206a7SAxel Dörfler			for (uint32 i = vecIndex; i < buffer->VecCount(); i++) {
448435c43f5SIngo Weinhold				generic_io_vec& vec = buffer->VecAt(i);
449435c43f5SIngo Weinhold				generic_addr_t base = vec.base + vecOffset;
450435c43f5SIngo Weinhold				generic_size_t size = vec.length - vecOffset;
45145a206a7SAxel Dörfler				vecOffset = 0;
4526969690aSAxel Dörfler				if (size > transferLeft)
4536969690aSAxel Dörfler					size = transferLeft;
4546969690aSAxel Dörfler
4556969690aSAxel Dörfler				while (size > 0 && segmentCount
4566969690aSAxel Dörfler						< fRestrictions.max_segment_count) {
4576969690aSAxel Dörfler					physical_entry entry;
4589951d585SIngo Weinhold					uint32 count = 1;
4594535495dSIngo Weinhold					get_memory_map_etc(request->TeamID(), (void*)base, size,
4609951d585SIngo Weinhold						&entry, &count);
4616969690aSAxel Dörfler
462435c43f5SIngo Weinhold					vecs[segmentCount].base = entry.address;
463435c43f5SIngo Weinhold					vecs[segmentCount].length = entry.size;
4646969690aSAxel Dörfler
4656969690aSAxel Dörfler					transferLeft -= entry.size;
466ed0303d1SAxel Dörfler					base += entry.size;
46745a206a7SAxel Dörfler					size -= entry.size;
4686969690aSAxel Dörfler					segmentCount++;
4696969690aSAxel Dörfler				}
4706969690aSAxel Dörfler
4716969690aSAxel Dörfler				if (transferLeft == 0)
4726969690aSAxel Dörfler					break;
4736969690aSAxel Dörfler			}
4746969690aSAxel Dörfler
4756969690aSAxel Dörfler			totalLength -= transferLeft;
4766969690aSAxel Dörfler		}
47745a206a7SAxel Dörfler
47845a206a7SAxel Dörfler		vecIndex = 0;
47945a206a7SAxel Dörfler		vecOffset = 0;
4806969690aSAxel Dörfler	} else {
48145a206a7SAxel Dörfler		// We do already have physical addresses.
4826969690aSAxel Dörfler		locker.Unlock();
4836969690aSAxel Dörfler		vecs = buffer->Vecs();
484b328324dSIngo Weinhold		segmentCount = min_c(buffer->VecCount() - vecIndex,
4856969690aSAxel Dörfler			fRestrictions.max_segment_count);
4866969690aSAxel Dörfler	}
4876969690aSAxel Dörfler
4888faff60cSAxel Dörfler#ifdef TRACE_DMA_RESOURCE
4898faff60cSAxel Dörfler	TRACE("  physical count %lu\n", segmentCount);
4908faff60cSAxel Dörfler	for (uint32 i = 0; i < segmentCount; i++) {
491435c43f5SIngo Weinhold		TRACE("    [%" B_PRIu32 "] %#" B_PRIxGENADDR ", %" B_PRIxGENADDR "\n",
492435c43f5SIngo Weinhold			i, vecs[vecIndex + i].base, vecs[vecIndex + i].length);
4938faff60cSAxel Dörfler	}
4948faff60cSAxel Dörfler#endif
4958faff60cSAxel Dörfler
4966969690aSAxel Dörfler	// check alignment, boundaries, etc. and set vecs in DMA buffer
4976969690aSAxel Dörfler
498025f7c32SIngo Weinhold	// Fetch a bounce buffer we can use for the DMABuffer.
499025f7c32SIngo Weinhold	// TODO: We should do that lazily when needed!
500025f7c32SIngo Weinhold	DMABounceBuffer* bounceBuffer = NULL;
501025f7c32SIngo Weinhold	if (_NeedsBoundsBuffers()) {
502025f7c32SIngo Weinhold		bounceBuffer = fBounceBuffers.Head();
503025f7c32SIngo Weinhold		if (bounceBuffer == NULL)
504025f7c32SIngo Weinhold			return B_BUSY;
505025f7c32SIngo Weinhold	}
506025f7c32SIngo Weinhold	dmaBuffer->SetBounceBuffer(bounceBuffer);
507025f7c32SIngo Weinhold
508435c43f5SIngo Weinhold	generic_size_t dmaLength = 0;
509435c43f5SIngo Weinhold	phys_addr_t physicalBounceBuffer = dmaBuffer->PhysicalBounceBufferAddress();
510435c43f5SIngo Weinhold	phys_size_t bounceLeft = fBounceBufferSize;
511435c43f5SIngo Weinhold	generic_size_t transferLeft = totalLength;
51245a206a7SAxel Dörfler
51345a206a7SAxel Dörfler	// If the offset isn't block-aligned, use the bounce buffer to bridge the
51445a206a7SAxel Dörfler	// gap to the start of the vec.
5158faff60cSAxel Dörfler	if (partialBegin > 0) {
516435c43f5SIngo Weinhold		generic_size_t length;
5178faff60cSAxel Dörfler		if (request->IsWrite()) {
5188faff60cSAxel Dörfler			// we always need to read in a whole block for the partial write
5198faff60cSAxel Dörfler			length = fBlockSize;
5208faff60cSAxel Dörfler		} else {
5218faff60cSAxel Dörfler			length = (partialBegin + fRestrictions.alignment - 1)
5228faff60cSAxel Dörfler				& ~(fRestrictions.alignment - 1);
5238faff60cSAxel Dörfler		}
52445a206a7SAxel Dörfler
5258faff60cSAxel Dörfler		if (_AddBounceBuffer(*dmaBuffer, physicalBounceBuffer, bounceLeft,
5268faff60cSAxel Dörfler				length, true) == 0) {
5278faff60cSAxel Dörfler			TRACE("  adding partial begin failed, length %lu!\n", length);
5288faff60cSAxel Dörfler			return B_BAD_VALUE;
5298faff60cSAxel Dörfler		}
53045a206a7SAxel Dörfler
53145a206a7SAxel Dörfler		dmaLength += length;
53245a206a7SAxel Dörfler
533435c43f5SIngo Weinhold		generic_size_t transferred = length - partialBegin;
534ff388d51SIngo Weinhold		vecOffset += transferred;
5358faff60cSAxel Dörfler		offset -= partialBegin;
536ff388d51SIngo Weinhold
537ff388d51SIngo Weinhold		if (transferLeft > transferred)
538ff388d51SIngo Weinhold			transferLeft -= transferred;
539ff388d51SIngo Weinhold		else
540ff388d51SIngo Weinhold			transferLeft = 0;
541ff388d51SIngo Weinhold
5428faff60cSAxel Dörfler		TRACE("  partial begin, using bounce buffer: offset: %lld, length: "