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