xref: /haiku/src/system/kernel/device_manager/dma_resources.cpp (revision 1deede7388b04dbeec5af85cae7164735ea9e70d)
1 /*
2  * Copyright 2008-2011, 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 
8 #include "dma_resources.h"
9 
10 #include <device_manager.h>
11 
12 #include <kernel.h>
13 #include <util/AutoLock.h>
14 #include <vm/vm.h>
15 
16 #include "IORequest.h"
17 
18 
19 //#define TRACE_DMA_RESOURCE
20 #ifdef TRACE_DMA_RESOURCE
21 #	define TRACE(x...) dprintf(x)
22 #else
23 #	define TRACE(x...) ;
24 #endif
25 
26 
27 extern device_manager_info gDeviceManagerModule;
28 
29 const phys_size_t kMaxBounceBufferSize = 4 * B_PAGE_SIZE;
30 
31 
32 DMABuffer*
33 DMABuffer::Create(size_t count)
34 {
35 	DMABuffer* buffer = (DMABuffer*)malloc(
36 		sizeof(DMABuffer) + sizeof(generic_io_vec) * (count - 1));
37 	if (buffer == NULL)
38 		return NULL;
39 
40 	buffer->fVecCount = count;
41 
42 	return buffer;
43 }
44 
45 
46 void
47 DMABuffer::SetVecCount(uint32 count)
48 {
49 	fVecCount = count;
50 }
51 
52 
53 void
54 DMABuffer::AddVec(generic_addr_t base, generic_size_t size)
55 {
56 	generic_io_vec& vec = fVecs[fVecCount++];
57 	vec.base = base;
58 	vec.length = size;
59 }
60 
61 
62 bool
63 DMABuffer::UsesBounceBufferAt(uint32 index)
64 {
65 	if (index >= fVecCount || fBounceBuffer == NULL)
66 		return false;
67 
68 	return fVecs[index].base >= fBounceBuffer->physical_address
69 		&& fVecs[index].base
70 				< fBounceBuffer->physical_address + fBounceBuffer->size;
71 }
72 
73 
74 void
75 DMABuffer::Dump() const
76 {
77 	kprintf("DMABuffer at %p\n", this);
78 
79 	kprintf("  bounce buffer:      %p (physical %#" B_PRIxPHYSADDR ")\n",
80 		fBounceBuffer->address, fBounceBuffer->physical_address);
81 	kprintf("  bounce buffer size: %" B_PRIxPHYSADDR "\n", fBounceBuffer->size);
82 	kprintf("  vecs:               %" B_PRIu32 "\n", fVecCount);
83 
84 	for (uint32 i = 0; i < fVecCount; i++) {
85 		kprintf("    [%" B_PRIu32 "] %#" B_PRIxGENADDR ", %" B_PRIuGENADDR "\n",
86 			i, fVecs[i].base, fVecs[i].length);
87 	}
88 }
89 
90 
91 //	#pragma mark -
92 
93 
94 DMAResource::DMAResource()
95 	:
96 	fScratchVecs(NULL)
97 {
98 	mutex_init(&fLock, "dma resource");
99 }
100 
101 
102 DMAResource::~DMAResource()
103 {
104 	mutex_lock(&fLock);
105 	mutex_destroy(&fLock);
106 	free(fScratchVecs);
107 
108 // TODO: Delete DMABuffers and BounceBuffers!
109 }
110 
111 
112 status_t
113 DMAResource::Init(device_node* node, generic_size_t blockSize,
114 	uint32 bufferCount, uint32 bounceBufferCount)
115 {
116 	dma_restrictions restrictions;
117 	memset(&restrictions, 0, sizeof(dma_restrictions));
118 
119 	// TODO: add DMA attributes instead of reusing block_io's
120 
121 	uint32 value;
122 	if (gDeviceManagerModule.get_attr_uint32(node,
123 			B_DMA_ALIGNMENT, &value, true) == B_OK)
124 		restrictions.alignment = (generic_size_t)value + 1;
125 
126 	if (gDeviceManagerModule.get_attr_uint32(node,
127 			B_DMA_BOUNDARY, &value, true) == B_OK)
128 		restrictions.boundary = (generic_size_t)value + 1;
129 
130 	if (gDeviceManagerModule.get_attr_uint32(node,
131 			B_DMA_MAX_SEGMENT_BLOCKS, &value, true) == B_OK)
132 		restrictions.max_segment_size = (generic_size_t)value * blockSize;
133 
134 	if (gDeviceManagerModule.get_attr_uint32(node,
135 			B_DMA_MAX_TRANSFER_BLOCKS, &value, true) == B_OK)
136 		restrictions.max_transfer_size = (generic_size_t)value * blockSize;
137 
138 	if (gDeviceManagerModule.get_attr_uint32(node,
139 			B_DMA_MAX_SEGMENT_COUNT, &value, true) == B_OK)
140 		restrictions.max_segment_count = value;
141 
142 	uint64 value64;
143 	if (gDeviceManagerModule.get_attr_uint64(node,
144 			B_DMA_LOW_ADDRESS, &value64, true) == B_OK) {
145 		restrictions.low_address = value64;
146 	}
147 
148 	if (gDeviceManagerModule.get_attr_uint64(node,
149 			B_DMA_HIGH_ADDRESS, &value64, true) == B_OK) {
150 		restrictions.high_address = value64;
151 	}
152 
153 	return Init(restrictions, blockSize, bufferCount, bounceBufferCount);
154 }
155 
156 
157 status_t
158 DMAResource::Init(const dma_restrictions& restrictions,
159 	generic_size_t blockSize, uint32 bufferCount, uint32 bounceBufferCount)
160 {
161 	ASSERT(restrictions.alignment <= blockSize);
162 
163 	fRestrictions = restrictions;
164 	fBlockSize = blockSize == 0 ? 1 : blockSize;
165 	fBufferCount = bufferCount;
166 	fBounceBufferCount = bounceBufferCount;
167 	fBounceBufferSize = 0;
168 
169 	if (fRestrictions.high_address == 0)
170 		fRestrictions.high_address = ~(generic_addr_t)0;
171 	if (fRestrictions.max_segment_count == 0)
172 		fRestrictions.max_segment_count = 16;
173 	if (fRestrictions.alignment == 0)
174 		fRestrictions.alignment = 1;
175 	if (fRestrictions.max_transfer_size == 0)
176 		fRestrictions.max_transfer_size = ~(generic_size_t)0;
177 	if (fRestrictions.max_segment_size == 0)
178 		fRestrictions.max_segment_size = ~(generic_size_t)0;
179 
180 	if (_NeedsBoundsBuffers()) {
181 		fBounceBufferSize = fRestrictions.max_segment_size
182 			* min_c(fRestrictions.max_segment_count, 4);
183 		if (fBounceBufferSize > kMaxBounceBufferSize)
184 			fBounceBufferSize = kMaxBounceBufferSize;
185 		TRACE("DMAResource::Init(): chose bounce buffer size %lu\n",
186 			fBounceBufferSize);
187 	}
188 
189 	dprintf("DMAResource@%p: low/high %" B_PRIxGENADDR "/%" B_PRIxGENADDR
190 		", max segment count %" B_PRIu32 ", align %" B_PRIuGENADDR ", "
191 		"boundary %" B_PRIuGENADDR ", max transfer %" B_PRIuGENADDR
192 		", max segment size %" B_PRIuGENADDR "\n", this,
193 		fRestrictions.low_address, fRestrictions.high_address,
194 		fRestrictions.max_segment_count, fRestrictions.alignment,
195 		fRestrictions.boundary, fRestrictions.max_transfer_size,
196 		fRestrictions.max_segment_size);
197 
198 	fScratchVecs = (generic_io_vec*)malloc(
199 		sizeof(generic_io_vec) * fRestrictions.max_segment_count);
200 	if (fScratchVecs == NULL)
201 		return B_NO_MEMORY;
202 
203 	for (size_t i = 0; i < fBufferCount; i++) {
204 		DMABuffer* buffer;
205 		status_t error = CreateBuffer(&buffer);
206 		if (error != B_OK)
207 			return error;
208 
209 		fDMABuffers.Add(buffer);
210 	}
211 
212 	// TODO: create bounce buffers in as few areas as feasible
213 	for (size_t i = 0; i < fBounceBufferCount; i++) {
214 		DMABounceBuffer* buffer;
215 		status_t error = CreateBounceBuffer(&buffer);
216 		if (error != B_OK)
217 			return error;
218 
219 		fBounceBuffers.Add(buffer);
220 	}
221 
222 	return B_OK;
223 }
224 
225 
226 status_t
227 DMAResource::CreateBuffer(DMABuffer** _buffer)
228 {
229 	DMABuffer* buffer = DMABuffer::Create(fRestrictions.max_segment_count);
230 	if (buffer == NULL)
231 		return B_NO_MEMORY;
232 
233 	*_buffer = buffer;
234 	return B_OK;
235 }
236 
237 
238 status_t
239 DMAResource::CreateBounceBuffer(DMABounceBuffer** _buffer)
240 {
241 	void* bounceBuffer = NULL;
242 	phys_addr_t physicalBase = 0;
243 	area_id area = -1;
244 	phys_size_t size = ROUNDUP(fBounceBufferSize, B_PAGE_SIZE);
245 
246 	virtual_address_restrictions virtualRestrictions = {};
247 	virtualRestrictions.address_specification = B_ANY_KERNEL_ADDRESS;
248 	physical_address_restrictions physicalRestrictions = {};
249 	physicalRestrictions.low_address = fRestrictions.low_address;
250 	physicalRestrictions.high_address = fRestrictions.high_address;
251 	physicalRestrictions.alignment = fRestrictions.alignment;
252 	physicalRestrictions.boundary = fRestrictions.boundary;
253 	area = create_area_etc(B_SYSTEM_TEAM, "dma buffer", size, B_CONTIGUOUS,
254 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, 0, 0, &virtualRestrictions,
255 		&physicalRestrictions, &bounceBuffer);
256 	if (area < B_OK)
257 		return area;
258 
259 	physical_entry entry;
260 	if (get_memory_map(bounceBuffer, size, &entry, 1) != B_OK) {
261 		panic("get_memory_map() failed.");
262 		delete_area(area);
263 		return B_ERROR;
264 	}
265 
266 	physicalBase = entry.address;
267 
268 	ASSERT(fRestrictions.high_address >= physicalBase + size);
269 
270 	DMABounceBuffer* buffer = new(std::nothrow) DMABounceBuffer;
271 	if (buffer == NULL) {
272 		delete_area(area);
273 		return B_NO_MEMORY;
274 	}
275 
276 	buffer->address = bounceBuffer;
277 	buffer->physical_address = physicalBase;
278 	buffer->size = size;
279 
280 	*_buffer = buffer;
281 	return B_OK;
282 }
283 
284 
285 inline void
286 DMAResource::_RestrictBoundaryAndSegmentSize(generic_addr_t base,
287 	generic_addr_t& length)
288 {
289 	if (length > fRestrictions.max_segment_size)
290 		length = fRestrictions.max_segment_size;
291 	if (fRestrictions.boundary > 0) {
292 		generic_addr_t baseBoundary = base / fRestrictions.boundary;
293 		if (baseBoundary
294 				!= (base + (length - 1)) / fRestrictions.boundary) {
295 			length = (baseBoundary + 1) * fRestrictions.boundary - base;
296 		}
297 	}
298 }
299 
300 
301 void
302 DMAResource::_CutBuffer(DMABuffer& buffer, phys_addr_t& physicalBounceBuffer,
303 	phys_size_t& bounceLeft, generic_size_t toCut)
304 {
305 	int32 vecCount = buffer.VecCount();
306 	for (int32 i = vecCount - 1; toCut > 0 && i >= 0; i--) {
307 		generic_io_vec& vec = buffer.VecAt(i);
308 		generic_size_t length = vec.length;
309 		bool inBounceBuffer = buffer.UsesBounceBufferAt(i);
310 
311 		if (length <= toCut) {
312 			vecCount--;
313 			toCut -= length;
314 
315 			if (inBounceBuffer) {
316 				bounceLeft += length;
317 				physicalBounceBuffer -= length;
318 			}
319 		} else {
320 			vec.length -= toCut;
321 
322 			if (inBounceBuffer) {
323 				bounceLeft += toCut;
324 				physicalBounceBuffer -= toCut;
325 			}
326 			break;
327 		}
328 	}
329 
330 	buffer.SetVecCount(vecCount);
331 }
332 
333 
334 /*!	Adds \a length bytes from the bounce buffer to the DMABuffer \a buffer.
335 	Takes care of boundary, and segment restrictions. \a length must be aligned.
336 	If \a fixedLength is requested, this function will fail if it cannot
337 	satisfy the request.
338 
339 	\return 0 if the request cannot be satisfied. There could have been some
340 		additions to the DMA buffer, and you will need to cut them back.
341 	TODO: is that what we want here?
342 	\return >0 the number of bytes added to the buffer.
343 */
344 phys_size_t
345 DMAResource::_AddBounceBuffer(DMABuffer& buffer,
346 	phys_addr_t& physicalBounceBuffer, phys_size_t& bounceLeft,
347 	generic_size_t length, bool fixedLength)
348 {
349 	if (bounceLeft < length) {
350 		if (fixedLength)
351 			return 0;
352 
353 		length = bounceLeft;
354 	}
355 
356 	phys_size_t bounceUsed = 0;
357 
358 	uint32 vecCount = buffer.VecCount();
359 	if (vecCount > 0) {
360 		// see if we can join the bounce buffer with the previously last vec
361 		generic_io_vec& vec = buffer.VecAt(vecCount - 1);
362 		generic_addr_t vecBase = vec.base;
363 		generic_size_t vecLength = vec.length;
364 
365 		if (vecBase + vecLength == physicalBounceBuffer) {
366 			vecLength += length;
367 			_RestrictBoundaryAndSegmentSize(vecBase, vecLength);
368 
369 			generic_size_t lengthDiff = vecLength - vec.length;
370 			length -= lengthDiff;
371 
372 			physicalBounceBuffer += lengthDiff;
373 			bounceLeft -= lengthDiff;
374 			bounceUsed += lengthDiff;
375 
376 			vec.length = vecLength;
377 		}
378 	}
379 
380 	while (length > 0) {
381 		// We need to add another bounce vec
382 
383 		if (vecCount == fRestrictions.max_segment_count)
384 			return fixedLength ? 0 : bounceUsed;
385 
386 		generic_addr_t vecLength = length;
387 		_RestrictBoundaryAndSegmentSize(physicalBounceBuffer, vecLength);
388 
389 		buffer.AddVec(physicalBounceBuffer, vecLength);
390 		vecCount++;
391 
392 		physicalBounceBuffer += vecLength;
393 		bounceLeft -= vecLength;
394 		bounceUsed += vecLength;
395 		length -= vecLength;
396 	}
397 
398 	return bounceUsed;
399 }
400 
401 
402 status_t
403 DMAResource::TranslateNext(IORequest* request, IOOperation* operation,
404 	generic_size_t maxOperationLength)
405 {
406 	IOBuffer* buffer = request->Buffer();
407 	off_t originalOffset = request->Offset() + request->Length()
408 		- request->RemainingBytes();
409 	off_t offset = originalOffset;
410 	generic_size_t partialBegin = offset & (fBlockSize - 1);
411 
412 	// current iteration state
413 	uint32 vecIndex = request->VecIndex();
414 	uint32 vecOffset = request->VecOffset();
415 	generic_size_t totalLength = min_c(request->RemainingBytes(),
416 		fRestrictions.max_transfer_size);
417 
418 	if (maxOperationLength > 0
419 		&& maxOperationLength < totalLength + partialBegin) {
420 		totalLength = maxOperationLength - partialBegin;
421 	}
422 
423 	MutexLocker locker(fLock);
424 
425 	DMABuffer* dmaBuffer = fDMABuffers.RemoveHead();
426 	if (dmaBuffer == NULL)
427 		return B_BUSY;
428 
429 	dmaBuffer->SetVecCount(0);
430 
431 	generic_io_vec* vecs = NULL;
432 	uint32 segmentCount = 0;
433 
434 	TRACE("  offset %Ld, remaining size: %lu, block size %lu -> partial: %lu\n",
435 		offset, request->RemainingBytes(), fBlockSize, partialBegin);
436 
437 	if (buffer->IsVirtual()) {
438 		// Unless we need the bounce buffer anyway, we have to translate the
439 		// virtual addresses to physical addresses, so we can check the DMA
440 		// restrictions.
441 		TRACE("  buffer is virtual %s\n", buffer->IsUser() ? "user" : "kernel");
442 		// TODO: !partialOperation || totalLength >= fBlockSize
443 		// TODO: Maybe enforce fBounceBufferSize >= 2 * fBlockSize.
444 		if (true) {
445 			generic_size_t transferLeft = totalLength;
446 			vecs = fScratchVecs;
447 
448 			TRACE("  create physical map (for %ld vecs)\n", buffer->VecCount());
449 			for (uint32 i = vecIndex; i < buffer->VecCount(); i++) {
450 				generic_io_vec& vec = buffer->VecAt(i);
451 				generic_addr_t base = vec.base + vecOffset;
452 				generic_size_t size = vec.length - vecOffset;
453 				vecOffset = 0;
454 				if (size > transferLeft)
455 					size = transferLeft;
456 
457 				while (size > 0 && segmentCount
458 						< fRestrictions.max_segment_count) {
459 					physical_entry entry;
460 					uint32 count = 1;
461 					get_memory_map_etc(request->TeamID(), (void*)base, size,
462 						&entry, &count);
463 
464 					vecs[segmentCount].base = entry.address;
465 					vecs[segmentCount].length = entry.size;
466 
467 					transferLeft -= entry.size;
468 					base += entry.size;
469 					size -= entry.size;
470 					segmentCount++;
471 				}
472 
473 				if (transferLeft == 0)
474 					break;
475 			}
476 
477 			totalLength -= transferLeft;
478 		}
479 
480 		vecIndex = 0;
481 		vecOffset = 0;
482 	} else {
483 		// We do already have physical addresses.
484 		vecs = buffer->Vecs();
485 		segmentCount = min_c(buffer->VecCount() - vecIndex,
486 			fRestrictions.max_segment_count);
487 	}
488 
489 #ifdef TRACE_DMA_RESOURCE
490 	TRACE("  physical count %lu\n", segmentCount);
491 	for (uint32 i = 0; i < segmentCount; i++) {
492 		TRACE("    [%" B_PRIu32 "] %#" B_PRIxGENADDR ", %" B_PRIxGENADDR "\n",
493 			i, vecs[vecIndex + i].base, vecs[vecIndex + i].length);
494 	}
495 #endif
496 
497 	// check alignment, boundaries, etc. and set vecs in DMA buffer
498 
499 	// Fetch a bounce buffer we can use for the DMABuffer.
500 	// TODO: We should do that lazily when needed!
501 	DMABounceBuffer* bounceBuffer = NULL;
502 	if (_NeedsBoundsBuffers()) {
503 		bounceBuffer = fBounceBuffers.Head();
504 		if (bounceBuffer == NULL)
505 			return B_BUSY;
506 	}
507 	dmaBuffer->SetBounceBuffer(bounceBuffer);
508 
509 	generic_size_t dmaLength = 0;
510 	phys_addr_t physicalBounceBuffer = dmaBuffer->PhysicalBounceBufferAddress();
511 	phys_size_t bounceLeft = fBounceBufferSize;
512 	generic_size_t transferLeft = totalLength;
513 
514 	// If the offset isn't block-aligned, use the bounce buffer to bridge the
515 	// gap to the start of the vec.
516 	if (partialBegin > 0) {
517 		generic_size_t length;
518 		if (request->IsWrite()) {
519 			// we always need to read in a whole block for the partial write
520 			length = fBlockSize;
521 		} else {
522 			length = (partialBegin + fRestrictions.alignment - 1)
523 				& ~(fRestrictions.alignment - 1);
524 		}
525 
526 		if (_AddBounceBuffer(*dmaBuffer, physicalBounceBuffer, bounceLeft,
527 				length, true) == 0) {
528 			TRACE("  adding partial begin failed, length %lu!\n", length);
529 			return B_BAD_VALUE;
530 		}
531 
532 		dmaLength += length;
533 
534 		generic_size_t transferred = length - partialBegin;
535 		vecOffset += transferred;
536 		offset -= partialBegin;
537 
538 		if (transferLeft > transferred)
539 			transferLeft -= transferred;
540 		else
541 			transferLeft = 0;
542 
543 		TRACE("  partial begin, using bounce buffer: offset: %lld, length: "
544 			"%lu\n", offset, length);
545 	}
546 
547 	for (uint32 i = vecIndex;
548 			i < vecIndex + segmentCount && transferLeft > 0;) {
549 		if (dmaBuffer->VecCount() >= fRestrictions.max_segment_count)
550 			break;
551 
552 		const generic_io_vec& vec = vecs[i];
553 		if (vec.length <= vecOffset) {
554 			vecOffset -= vec.length;
555 			i++;
556 			continue;
557 		}
558 
559 		generic_addr_t base = vec.base + vecOffset;
560 		generic_size_t maxLength = vec.length - vecOffset;
561 		if (maxLength > transferLeft)
562 			maxLength = transferLeft;
563 		generic_size_t length = maxLength;
564 
565 		// Cut the vec according to transfer size, segment size, and boundary.
566 
567 		if (dmaLength + length > fRestrictions.max_transfer_size) {
568 			length = fRestrictions.max_transfer_size - dmaLength;
569 			TRACE("  vec %lu: restricting length to %lu due to transfer size "
570 				"limit\n", i, length);
571 		}
572 		_RestrictBoundaryAndSegmentSize(base, length);
573 
574 		phys_size_t useBounceBufferSize = 0;
575 
576 		// Check low address: use bounce buffer for range to low address.
577 		// Check alignment: if not aligned, use bounce buffer for complete vec.
578 		if (base < fRestrictions.low_address) {
579 			useBounceBufferSize = fRestrictions.low_address - base;
580 			TRACE("  vec %lu: below low address, using bounce buffer: %lu\n", i,
581 				useBounceBufferSize);
582 		} else if (base & (fRestrictions.alignment - 1)) {
583 			useBounceBufferSize = length;
584 			TRACE("  vec %lu: misalignment, using bounce buffer: %lu\n", i,
585 				useBounceBufferSize);
586 		}
587 
588 		// Enforce high address restriction
589 		if (base > fRestrictions.high_address)
590 			useBounceBufferSize = length;
591 		else if (base + length > fRestrictions.high_address)
592 			length = fRestrictions.high_address - base;
593 
594 		// Align length as well
595 		if (useBounceBufferSize == 0)
596 			length &= ~(fRestrictions.alignment - 1);
597 
598 		// If length is 0, use bounce buffer for complete vec.
599 		if (length == 0) {
600 			length = maxLength;
601 			useBounceBufferSize = length;
602 			TRACE("  vec %lu: 0 length, using bounce buffer: %lu\n", i,
603 				useBounceBufferSize);
604 		}
605 
606 		if (useBounceBufferSize > 0) {
607 			// alignment could still be wrong (we round up here)
608 			useBounceBufferSize = (useBounceBufferSize
609 				+ fRestrictions.alignment - 1) & ~(fRestrictions.alignment - 1);
610 
611 			length = _AddBounceBuffer(*dmaBuffer, physicalBounceBuffer,
612 				bounceLeft, useBounceBufferSize, false);
613 			if (length == 0) {
614 				TRACE("  vec %lu: out of bounce buffer space\n", i);
615 				// We don't have any bounce buffer space left, we need to move
616 				// this request to the next I/O operation.
617 				break;
618 			}
619 			TRACE("  vec %lu: final bounce length: %lu\n", i, length);
620 		} else {
621 			TRACE("  vec %lu: final length restriction: %lu\n", i, length);
622 			dmaBuffer->AddVec(base, length);
623 		}
624 
625 		dmaLength += length;
626 		vecOffset += length;
627 		transferLeft -= min_c(length, transferLeft);
628 	}
629 
630 	// If we're writing partially, we always need to have a block sized bounce
631 	// buffer (or else we would overwrite memory to be written on the read in
632 	// the first phase).
633 	off_t requestEnd = request->Offset() + request->Length();
634 	if (request->IsWrite()) {
635 		generic_size_t diff = dmaLength & (fBlockSize - 1);
636 
637 		// If the transfer length is block aligned and we're writing past the
638 		// end of the given data, we still have to check the whether the last
639 		// vec is a bounce buffer segment shorter than the block size. If so, we
640 		// have to cut back the complete block and use a bounce buffer for it
641 		// entirely.
642 		if (diff == 0 && offset + (off_t)dmaLength > requestEnd) {
643 			const generic_io_vec& dmaVec
644 				= dmaBuffer->VecAt(dmaBuffer->VecCount() - 1);
645 			ASSERT(dmaVec.base >= dmaBuffer->PhysicalBounceBufferAddress()
646 				&& dmaVec.base
647 					< dmaBuffer->PhysicalBounceBufferAddress()
648 						+ fBounceBufferSize);
649 				// We can be certain that the last vec is a bounce buffer vec,
650 				// since otherwise the DMA buffer couldn't exceed the end of the
651 				// request data.
652 			if (dmaVec.length < fBlockSize)
653 				diff = fBlockSize;
654 		}
655 
656 		if (diff != 0) {
657 			// Not yet block aligned -- cut back to the previous block and add
658 			// a block-sized bounce buffer segment.
659 			TRACE("  partial end write: %lu, diff %lu\n", dmaLength, diff);
660 
661 			_CutBuffer(*dmaBuffer, physicalBounceBuffer, bounceLeft, diff);
662 			dmaLength -= diff;
663 
664 			if (_AddBounceBuffer(*dmaBuffer, physicalBounceBuffer,
665 					bounceLeft, fBlockSize, true) == 0) {
666 				// If we cannot write anything, we can't process the request at
667 				// all.
668 				TRACE("  adding bounce buffer failed!!!\n");
669 				if (dmaLength == 0)
670 					return B_BAD_VALUE;
671 			} else
672 				dmaLength += fBlockSize;
673 		}
674 	}
675 
676 	// If total length not block aligned, use bounce buffer for padding (read
677 	// case only).
678 	while ((dmaLength & (fBlockSize - 1)) != 0) {
679 		TRACE("  dmaLength not block aligned: %lu\n", dmaLength);
680 			generic_size_t length
681 				= (dmaLength + fBlockSize - 1) & ~(fBlockSize - 1);
682 
683 		// If total length > max transfer size, segment count > max segment
684 		// count, truncate.
685 		// TODO: sometimes we can replace the last vec with the bounce buffer
686 		// to let it match the restrictions.
687 		if (length > fRestrictions.max_transfer_size
688 			|| dmaBuffer->VecCount() == fRestrictions.max_segment_count
689 			|| bounceLeft < length - dmaLength) {
690 			// cut the part of dma length
691 			TRACE("  can't align length due to max transfer size, segment "
692 				"count restrictions, or lacking bounce buffer space\n");
693 			generic_size_t toCut = dmaLength
694 				& (max_c(fBlockSize, fRestrictions.alignment) - 1);
695 			dmaLength -= toCut;
696 			if (dmaLength == 0) {
697 				// This can only happen, when we have too many small segments
698 				// and hit the max segment count. In this case we just use the
699 				// bounce buffer for as much as possible of the total length.
700 				dmaBuffer->SetVecCount(0);
701 				generic_addr_t base = dmaBuffer->PhysicalBounceBufferAddress();
702 				dmaLength = min_c(totalLength, fBounceBufferSize)
703 					& ~(max_c(fBlockSize, fRestrictions.alignment) - 1);
704 				_RestrictBoundaryAndSegmentSize(base, dmaLength);
705 				dmaBuffer->AddVec(base, dmaLength);
706 
707 				physicalBounceBuffer = base + dmaLength;
708 				bounceLeft = fBounceBufferSize - dmaLength;
709 			} else {
710 				_CutBuffer(*dmaBuffer, physicalBounceBuffer, bounceLeft, toCut);
711 			}
712 		} else {
713 			TRACE("  adding %lu bytes final bounce buffer\n",
714 				length - dmaLength);
715 			length -= dmaLength;
716 			length = _AddBounceBuffer(*dmaBuffer, physicalBounceBuffer,
717 				bounceLeft, length, true);
718 			if (length == 0)
719 				panic("don't do this to me!");
720 			dmaLength += length;
721 		}
722 	}
723 
724 	operation->SetBuffer(dmaBuffer);
725 	operation->SetBlockSize(fBlockSize);
726 	operation->SetOriginalRange(originalOffset,
727 		min_c(offset + (off_t)dmaLength, requestEnd) - originalOffset);
728 	operation->SetRange(offset, dmaLength);
729 	operation->SetPartial(partialBegin != 0,
730 		offset + (off_t)dmaLength > requestEnd);
731 
732 	// If we don't need the bounce buffer, we put it back, otherwise
733 	operation->SetUsesBounceBuffer(bounceLeft < fBounceBufferSize);
734 	if (operation->UsesBounceBuffer())
735 		fBounceBuffers.RemoveHead();
736 	else
737 		dmaBuffer->SetBounceBuffer(NULL);
738 
739 
740 	status_t error = operation->Prepare(request);
741 	if (error != B_OK)
742 		return error;
743 
744 	request->Advance(operation->OriginalLength());
745 
746 	return B_OK;
747 }
748 
749 
750 void
751 DMAResource::RecycleBuffer(DMABuffer* buffer)
752 {
753 	if (buffer == NULL)
754 		return;
755 
756 	MutexLocker _(fLock);
757 	fDMABuffers.Add(buffer);
758 	if (buffer->BounceBuffer() != NULL) {
759 		fBounceBuffers.Add(buffer->BounceBuffer());
760 		buffer->SetBounceBuffer(NULL);
761 	}
762 }
763 
764 
765 bool
766 DMAResource::_NeedsBoundsBuffers() const
767 {
768 	return fRestrictions.alignment > 1
769 		|| fRestrictions.low_address != 0
770 		|| fRestrictions.high_address != ~(generic_addr_t)0
771 		|| fBlockSize > 1;
772 }
773 
774 
775 
776 
777 #if 0
778 
779 
780 status_t
781 create_dma_resource(restrictions)
782 {
783 	// Restrictions are: transfer size, address space, alignment
784 	// segment min/max size, num segments
785 }
786 
787 
788 void
789 delete_dma_resource(resource)
790 {
791 }
792 
793 
794 dma_buffer_alloc(resource, size)
795 {
796 }
797 
798 
799 dma_buffer_free(buffer)
800 {
801 //	Allocates or frees memory in that DMA buffer.
802 }
803 
804 #endif	// 0
805