Implemented fragmented USB transfers. The transfer length will max out at a certain point to not overflow the allocator. The fragmented transfers are resubmitted until all fragments are sent / received.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20416 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz 2007-03-25 08:49:13 +00:00
parent 4477befef2
commit 145461d547
5 changed files with 100 additions and 9 deletions

View File

@ -14,6 +14,9 @@ Transfer::Transfer(Pipe *pipe)
: fPipe(pipe),
fVector(&fData),
fVectorCount(0),
fBaseAddress(NULL),
fFragmented(false),
fActualLength(0),
fCallback(NULL),
fCallbackCookie(NULL),
fRequestData(NULL)
@ -42,6 +45,7 @@ Transfer::SetRequestData(usb_request_data *data)
void
Transfer::SetData(uint8 *data, size_t dataLength)
{
fBaseAddress = data;
fData.iov_base = data;
fData.iov_len = dataLength;
@ -56,6 +60,7 @@ Transfer::SetVector(iovec *vector, size_t vectorCount)
fVector = new(std::nothrow) iovec[vectorCount];
memcpy(fVector, vector, vectorCount * sizeof(iovec));
fVectorCount = vectorCount;
fBaseAddress = fVector[0].iov_base;
}
@ -65,10 +70,37 @@ Transfer::VectorLength()
size_t length = 0;
for (size_t i = 0; i < fVectorCount; i++)
length += fVector[i].iov_len;
// the data is to large and would overflow the allocator
if (length > USB_MAX_FRAGMENT_SIZE) {
length = USB_MAX_FRAGMENT_SIZE;
fFragmented = true;
}
return length;
}
void
Transfer::AdvanceByFragment(size_t actualLength)
{
size_t length = USB_MAX_FRAGMENT_SIZE;
for (size_t i = 0; i < fVectorCount; i++) {
if (fVector[i].iov_len <= length) {
length -= fVector[i].iov_len;
fVector[i].iov_len = 0;
continue;
}
(uint8 *)fVector[i].iov_base += length;
fVector[i].iov_len -= length;
break;
}
fActualLength += actualLength;
}
void
Transfer::SetCallback(usb_callback_func callback, void *cookie)
{
@ -81,5 +113,6 @@ void
Transfer::Finished(uint32 status, size_t actualLength)
{
if (fCallback)
fCallback(fCallbackCookie, status, fVector[0].iov_base, actualLength);
fCallback(fCallbackCookie, status, fBaseAddress,
fActualLength + actualLength);
}

View File

@ -85,6 +85,7 @@ typedef enum {
#define USB_OBJECT_DEVICE 0x00000040
#define USB_OBJECT_HUB 0x00000080
#define USB_MAX_FRAGMENT_SIZE B_PAGE_SIZE * 96
class Stack {
public:
@ -528,6 +529,9 @@ public:
size_t VectorCount() { return fVectorCount; };
size_t VectorLength();
bool IsFragmented() { return fFragmented; };
void AdvanceByFragment(size_t actualLength);
void SetCallback(usb_callback_func callback,
void *cookie);
@ -539,6 +543,9 @@ private:
iovec fData;
iovec *fVector;
size_t fVectorCount;
void *fBaseAddress;
bool fFragmented;
size_t fActualLength;
usb_callback_func fCallback;
void *fCallbackCookie;

View File

@ -1055,11 +1055,11 @@ EHCI::FinishTransfers()
#ifndef HAIKU_TARGET_PLATFORM_HAIKU
area_id clonedArea = -1;
void *clonedMemory = NULL;
if (transfer->user_area >= B_OK) {
// we got a userspace output buffer, need to clone
// the area for that space first and map the iovecs
// to this cloned area.
void *clonedMemory = NULL;
clonedArea = clone_area("userspace accessor",
&clonedMemory, B_ANY_ADDRESS,
B_WRITE_AREA | B_KERNEL_WRITE_AREA,
@ -1076,8 +1076,11 @@ EHCI::FinishTransfers()
&nextDataToggle);
#ifndef HAIKU_TARGET_PLATFORM_HAIKU
if (clonedArea >= B_OK)
if (clonedArea >= B_OK) {
for (size_t i = 0; i < vectorCount; i++)
(uint8 *)vector[i].iov_base -= (addr_t)clonedMemory;
delete_area(clonedArea);
}
#endif // !HAIKU_TARGET_PLATFORM_HAIKU
} else {
// calculate transfered length
@ -1086,8 +1089,24 @@ EHCI::FinishTransfers()
&nextDataToggle);
}
UnlinkQueueHead(transfer->queue_head, &fFreeListHead);
transfer->transfer->TransferPipe()->SetDataToggle(nextDataToggle);
if (transfer->transfer->IsFragmented()) {
// this transfer may still have data left
transfer->transfer->AdvanceByFragment(actualLength);
if (transfer->transfer->VectorLength() > 0) {
FreeDescriptorChain(transfer->data_descriptor);
FillQueueWithData(transfer->transfer,
transfer->queue_head,
&transfer->data_descriptor, NULL);
break;
}
// the transfer is done, but we already set the
// actualLength with AdvanceByFragment()
actualLength = 0;
}
UnlinkQueueHead(transfer->queue_head, &fFreeListHead);
transfer->transfer->Finished(B_OK, actualLength);
transferDone = true;
break;
@ -1348,7 +1367,8 @@ EHCI::FillQueueWithData(Transfer *transfer, ehci_qh *queueHead,
queueHead->overlay.alt_next_phy = EHCI_QTD_TERMINATE;
*_dataDescriptor = firstDescriptor;
*_directionIn = directionIn;
if (_directionIn)
*_directionIn = directionIn;
return B_OK;
}

View File

@ -506,6 +506,13 @@ UHCI::SubmitTransfer(Transfer *transfer)
if (transfer->TransferPipe()->Type() & USB_OBJECT_CONTROL_PIPE)
return SubmitRequest(transfer);
return SubmitTransfer(transfer, false);
}
status_t
UHCI::SubmitTransfer(Transfer *transfer, bool resubmit)
{
Pipe *pipe = transfer->TransferPipe();
bool directionIn = (pipe->Direction() == Pipe::In);
@ -539,6 +546,12 @@ UHCI::SubmitTransfer(Transfer *transfer)
}
uhci_qh *transferQueue = CreateTransferQueue(firstDescriptor);
if (resubmit) {
// the transfer is already pending, we just submit another fragment
queue->AppendTransfer(transferQueue);
return B_OK;
}
result = AddPendingTransfer(transfer, queue, transferQueue,
firstDescriptor, firstDescriptor, directionIn);
if (result < B_OK) {
@ -818,11 +831,11 @@ UHCI::FinishTransfers()
#ifndef HAIKU_TARGET_PLATFORM_HAIKU
area_id clonedArea = -1;
void *clonedMemory = NULL;
if (transfer->user_area >= B_OK) {
// we got a userspace output buffer, need to clone
// the area for that space first and map the iovecs
// to this cloned area.
void *clonedMemory = NULL;
clonedArea = clone_area("userspace accessor",
&clonedMemory, B_ANY_ADDRESS,
B_WRITE_AREA | B_KERNEL_WRITE_AREA,
@ -839,8 +852,11 @@ UHCI::FinishTransfers()
&lastDataToggle);
#ifndef HAIKU_TARGET_PLATFORM_HAIKU
if (clonedArea >= B_OK)
if (clonedArea >= B_OK) {
for (size_t i = 0; i < vectorCount; i++)
(uint8 *)vector[i].iov_base -= (addr_t)clonedMemory;
delete_area(clonedArea);
}
#endif // !HAIKU_TARGET_PLATFORM_HAIKU
} else {
// read the actual length that was sent
@ -848,9 +864,24 @@ UHCI::FinishTransfers()
transfer->first_descriptor, &lastDataToggle);
}
transfer->transfer->TransferPipe()->SetDataToggle(lastDataToggle == 0);
if (transfer->transfer->IsFragmented()) {
// this transfer may still have data left
transfer->transfer->AdvanceByFragment(actualLength);
if (transfer->transfer->VectorLength() > 0) {
// resubmit the advanced transfer so the rest
// of the buffers are transmitted over the bus
SubmitTransfer(transfer->transfer, true);
break;
}
// the transfer is done, but we already set the
// actualLength with AdvanceByFragment()
actualLength = 0;
}
FreeDescriptorChain(transfer->first_descriptor);
FreeTransferQueue(transfer->transfer_queue);
transfer->transfer->TransferPipe()->SetDataToggle(lastDataToggle == 0);
transfer->transfer->Finished(B_OK, actualLength);
transferDone = true;
break;
@ -873,7 +904,6 @@ UHCI::FinishTransfers()
delete transfer->transfer;
delete transfer;
transfer = next;
Unlock();
}
} else {

View File

@ -68,6 +68,7 @@ public:
status_t Start();
virtual status_t SubmitTransfer(Transfer *transfer);
status_t SubmitTransfer(Transfer *transfer, bool resubmit);
status_t SubmitRequest(Transfer *transfer);
static status_t AddTo(Stack *stack);