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