XHCI: Add support for fragmented transfers.
I was not actually aware that our USB stack already supported breaking (bulk) transfers up into smaller units as necessary; these APIs are not very well documented. This makes #15569 occur far less often, though it is still more than possible to trigger.
This commit is contained in:
parent
bc7fd43358
commit
3e0eea85ec
@ -788,7 +788,7 @@ XHCI::SubmitControlRequest(Transfer *transfer)
|
|||||||
status_t
|
status_t
|
||||||
XHCI::SubmitNormalRequest(Transfer *transfer)
|
XHCI::SubmitNormalRequest(Transfer *transfer)
|
||||||
{
|
{
|
||||||
TRACE("SubmitNormalRequest() length %ld\n", transfer->DataLength());
|
TRACE("SubmitNormalRequest() length %ld\n", transfer->FragmentLength());
|
||||||
|
|
||||||
Pipe *pipe = transfer->TransferPipe();
|
Pipe *pipe = transfer->TransferPipe();
|
||||||
usb_isochronous_data *isochronousData = transfer->IsochronousData();
|
usb_isochronous_data *isochronousData = transfer->IsochronousData();
|
||||||
@ -826,7 +826,7 @@ XHCI::SubmitNormalRequest(Transfer *transfer)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Now that we know trbSize, compute the count.
|
// Now that we know trbSize, compute the count.
|
||||||
const int32 trbCount = (transfer->DataLength() + trbSize - 1) / trbSize;
|
const int32 trbCount = (transfer->FragmentLength() + trbSize - 1) / trbSize;
|
||||||
|
|
||||||
xhci_td *td = CreateDescriptor(trbCount, trbCount, trbSize);
|
xhci_td *td = CreateDescriptor(trbCount, trbCount, trbSize);
|
||||||
if (td == NULL)
|
if (td == NULL)
|
||||||
@ -834,7 +834,7 @@ XHCI::SubmitNormalRequest(Transfer *transfer)
|
|||||||
|
|
||||||
// Normal Stage
|
// Normal Stage
|
||||||
const size_t maxPacketSize = pipe->MaxPacketSize();
|
const size_t maxPacketSize = pipe->MaxPacketSize();
|
||||||
size_t remaining = transfer->DataLength();
|
size_t remaining = transfer->FragmentLength();
|
||||||
for (int32 i = 0; i < trbCount; i++) {
|
for (int32 i = 0; i < trbCount; i++) {
|
||||||
int32 trbLength = (remaining < trbSize) ? remaining : trbSize;
|
int32 trbLength = (remaining < trbSize) ? remaining : trbSize;
|
||||||
remaining -= trbLength;
|
remaining -= trbLength;
|
||||||
@ -2914,7 +2914,8 @@ XHCI::FinishTransfers()
|
|||||||
bool directionIn = (transfer->TransferPipe()->Direction() != Pipe::Out);
|
bool directionIn = (transfer->TransferPipe()->Direction() != Pipe::Out);
|
||||||
|
|
||||||
status_t callbackStatus = B_OK;
|
status_t callbackStatus = B_OK;
|
||||||
switch (td->trb_completion_code) {
|
const uint8 completionCode = td->trb_completion_code;
|
||||||
|
switch (completionCode) {
|
||||||
case COMP_SHORT_PACKET:
|
case COMP_SHORT_PACKET:
|
||||||
case COMP_SUCCESS:
|
case COMP_SUCCESS:
|
||||||
callbackStatus = B_OK;
|
callbackStatus = B_OK;
|
||||||
@ -2938,18 +2939,19 @@ XHCI::FinishTransfers()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t actualLength = transfer->DataLength();
|
size_t actualLength = transfer->FragmentLength();
|
||||||
if (td->trb_completion_code != COMP_SUCCESS) {
|
if (completionCode != COMP_SUCCESS) {
|
||||||
actualLength = td->td_transferred;
|
actualLength = td->td_transferred;
|
||||||
if (td->td_transferred == -1)
|
if (td->td_transferred == -1)
|
||||||
actualLength = transfer->DataLength() - td->trb_left;
|
actualLength = transfer->FragmentLength() - td->trb_left;
|
||||||
TRACE("transfer not successful, actualLength=%" B_PRIuSIZE "\n",
|
TRACE("transfer not successful, actualLength=%" B_PRIuSIZE "\n",
|
||||||
actualLength);
|
actualLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
usb_isochronous_data* isochronousData = transfer->IsochronousData();
|
usb_isochronous_data* isochronousData = transfer->IsochronousData();
|
||||||
if (isochronousData != NULL) {
|
if (isochronousData != NULL) {
|
||||||
size_t packetSize = transfer->DataLength() / isochronousData->packet_count,
|
size_t packetSize = transfer->DataLength()
|
||||||
|
/ isochronousData->packet_count,
|
||||||
left = actualLength;
|
left = actualLength;
|
||||||
for (uint32 i = 0; i < isochronousData->packet_count; i++) {
|
for (uint32 i = 0; i < isochronousData->packet_count; i++) {
|
||||||
size_t size = min_c(packetSize, left);
|
size_t size = min_c(packetSize, left);
|
||||||
@ -2970,9 +2972,22 @@ XHCI::FinishTransfers()
|
|||||||
callbackStatus = status;
|
callbackStatus = status;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
transfer->Finished(callbackStatus, actualLength);
|
|
||||||
delete transfer;
|
|
||||||
FreeDescriptor(td);
|
FreeDescriptor(td);
|
||||||
|
|
||||||
|
// this transfer may still have data left
|
||||||
|
transfer->AdvanceByFragment(actualLength);
|
||||||
|
if (completionCode == COMP_SUCCESS
|
||||||
|
&& transfer->FragmentLength() > 0) {
|
||||||
|
TRACE("still %" B_PRIuSIZE " bytes left on transfer\n",
|
||||||
|
transfer->FragmentLength());
|
||||||
|
SubmitTransfer(transfer);
|
||||||
|
} else {
|
||||||
|
// The actualLength was already handled in AdvanceByFragment.
|
||||||
|
transfer->Finished(callbackStatus, 0);
|
||||||
|
delete transfer;
|
||||||
|
}
|
||||||
|
|
||||||
mutex_lock(&fFinishedLock);
|
mutex_lock(&fFinishedLock);
|
||||||
}
|
}
|
||||||
mutex_unlock(&fFinishedLock);
|
mutex_unlock(&fFinishedLock);
|
||||||
|
Loading…
Reference in New Issue
Block a user