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:
parent
4477befef2
commit
145461d547
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user