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