mirror of
https://github.com/reactos/reactos.git
synced 2025-02-24 17:34:57 +00:00
[USBEHCI_NEW]
- Store number of bytes transferred in the transfer descriptors - Perform queue head completion when the door bell ring has been acknowledged. Fixes race condition between multiple irps in the async list - Fix calculation of transfer length when the request is an bulk in operation - Use EndPointDescriptor member to access transfer type / pid direction - Use MmGetSystemAddressForMdlSafe to retrieve system address for urb buffer - Fix check if first transfer buffer finishes on first size if the size is of page_size - With these changes and little luck and good weather, usb mass storage devices have been seen to work in Windows XP SP3 - Code inspired of mjmartin usbehci driver and Haiku's usb stack svn path=/branches/usb-bringup/; revision=51506
This commit is contained in:
parent
547ea14508
commit
6321f84f30
3 changed files with 232 additions and 198 deletions
|
@ -127,6 +127,7 @@ typedef struct _QUEUE_TRANSFER_DESCRIPTOR
|
|||
//Software
|
||||
ULONG PhysicalAddr;
|
||||
LIST_ENTRY LinkedDescriptors;
|
||||
ULONG TotalBytesToTransfer;
|
||||
} QUEUE_TRANSFER_DESCRIPTOR, *PQUEUE_TRANSFER_DESCRIPTOR;
|
||||
|
||||
//
|
||||
|
|
|
@ -52,6 +52,8 @@ protected:
|
|||
PDMA_ADAPTER m_Adapter;
|
||||
PQUEUE_HEAD AsyncListQueueHead;
|
||||
LIST_ENTRY m_CompletedRequestAsyncList;
|
||||
LIST_ENTRY m_PendingRequestAsyncList;
|
||||
|
||||
|
||||
// queue head manipulation functions
|
||||
VOID LinkQueueHead(PQUEUE_HEAD HeadQueueHead, PQUEUE_HEAD NewQueueHead);
|
||||
|
@ -120,6 +122,11 @@ CUSBQueue::Initialize(
|
|||
//
|
||||
InitializeListHead(&m_CompletedRequestAsyncList);
|
||||
|
||||
//
|
||||
// Initialize pending async list head
|
||||
//
|
||||
InitializeListHead(&m_PendingRequestAsyncList);
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
|
@ -425,19 +432,157 @@ CUSBQueue::QueueHeadCompletion(
|
|||
NTSTATUS Status)
|
||||
{
|
||||
IUSBRequest *Request;
|
||||
USBD_STATUS UrbStatus;
|
||||
PQUEUE_HEAD NewQueueHead;
|
||||
|
||||
//
|
||||
// this function is called when a queue head has been completed
|
||||
// now unlink the queue head
|
||||
// FIXME: implement chained queue heads
|
||||
//
|
||||
PC_ASSERT(CurrentQH->Token.Bits.Active == 0);
|
||||
UnlinkQueueHead(CurrentQH);
|
||||
|
||||
//
|
||||
// get contained usb request
|
||||
//
|
||||
Request = (IUSBRequest*)CurrentQH->Request;
|
||||
|
||||
//
|
||||
// check if the request is complete
|
||||
//
|
||||
if (Request->IsRequestComplete() == FALSE)
|
||||
{
|
||||
//
|
||||
// request is still in complete
|
||||
// get new queue head
|
||||
//
|
||||
Status = Request->GetQueueHead(&NewQueueHead);
|
||||
|
||||
//
|
||||
// add to pending list
|
||||
//
|
||||
InsertTailList(&m_PendingRequestAsyncList, &NewQueueHead->LinkedQueueHeads);
|
||||
}
|
||||
|
||||
//
|
||||
// put queue head into completed queue head list
|
||||
//
|
||||
InsertTailList(&m_CompletedRequestAsyncList, &CurrentQH->LinkedQueueHeads);
|
||||
|
||||
}
|
||||
|
||||
VOID
|
||||
CUSBQueue::ProcessAsyncList(
|
||||
IN NTSTATUS Status,
|
||||
OUT PULONG ShouldRingDoorBell)
|
||||
{
|
||||
KIRQL OldLevel;
|
||||
PLIST_ENTRY Entry;
|
||||
PQUEUE_HEAD QueueHead;
|
||||
IUSBRequest * Request;
|
||||
BOOLEAN IsQueueHeadComplete;
|
||||
|
||||
//
|
||||
// lock completed async list
|
||||
//
|
||||
KeAcquireSpinLock(&m_Lock, &OldLevel);
|
||||
|
||||
//
|
||||
// walk async list
|
||||
//
|
||||
Entry = AsyncListQueueHead->LinkedQueueHeads.Flink;
|
||||
|
||||
while(Entry != &AsyncListQueueHead->LinkedQueueHeads)
|
||||
{
|
||||
//
|
||||
// get queue head structure
|
||||
//
|
||||
QueueHead = (PQUEUE_HEAD)CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
|
||||
|
||||
//
|
||||
// sanity check
|
||||
//
|
||||
PC_ASSERT(QueueHead->Request);
|
||||
|
||||
//
|
||||
// get IUSBRequest interface
|
||||
//
|
||||
Request = (IUSBRequest*)QueueHead->Request;
|
||||
|
||||
|
||||
//
|
||||
// move to next entry
|
||||
//
|
||||
Entry = Entry->Flink;
|
||||
|
||||
//
|
||||
// check if queue head is complete
|
||||
//
|
||||
IsQueueHeadComplete = Request->IsQueueHeadComplete(QueueHead);
|
||||
|
||||
DPRINT1("Request %p QueueHead %p Complete %d\n", Request, QueueHead, IsQueueHeadComplete);
|
||||
|
||||
//
|
||||
// check if queue head is complete
|
||||
//
|
||||
if (IsQueueHeadComplete)
|
||||
{
|
||||
//
|
||||
// current queue head is complete
|
||||
//
|
||||
QueueHeadCompletion(QueueHead, Status);
|
||||
|
||||
//
|
||||
// ring door bell is going to be necessary
|
||||
//
|
||||
*ShouldRingDoorBell = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// release lock
|
||||
//
|
||||
KeReleaseSpinLock(&m_Lock, OldLevel);
|
||||
}
|
||||
|
||||
|
||||
VOID
|
||||
CUSBQueue::InterruptCallback(
|
||||
IN NTSTATUS Status,
|
||||
OUT PULONG ShouldRingDoorBell)
|
||||
{
|
||||
|
||||
DPRINT1("CUSBQueue::InterruptCallback\n");
|
||||
|
||||
//
|
||||
// iterate asynchronous list
|
||||
//
|
||||
*ShouldRingDoorBell = FALSE;
|
||||
ProcessAsyncList(Status, ShouldRingDoorBell);
|
||||
|
||||
//
|
||||
// TODO: implement periodic schedule processing
|
||||
//
|
||||
}
|
||||
|
||||
VOID
|
||||
CUSBQueue::QueueHeadCleanup(
|
||||
PQUEUE_HEAD CurrentQH)
|
||||
{
|
||||
IUSBRequest * Request;
|
||||
BOOLEAN ShouldReleaseWhenDone;
|
||||
USBD_STATUS UrbStatus;
|
||||
|
||||
//
|
||||
// sanity checks
|
||||
//
|
||||
PC_ASSERT(CurrentQH->Token.Bits.Active == 0);
|
||||
PC_ASSERT(CurrentQH->Request);
|
||||
|
||||
|
||||
//
|
||||
// get request
|
||||
//
|
||||
Request = (IUSBRequest*)CurrentQH->Request;
|
||||
|
||||
//
|
||||
// sanity check
|
||||
//
|
||||
|
@ -481,145 +626,7 @@ CUSBQueue::QueueHeadCompletion(
|
|||
//
|
||||
// notify request that a queue head has been completed
|
||||
//
|
||||
Request->CompletionCallback(Status, UrbStatus, CurrentQH);
|
||||
|
||||
//
|
||||
// now unlink the queue head
|
||||
// FIXME: implement chained queue heads
|
||||
//
|
||||
UnlinkQueueHead(CurrentQH);
|
||||
|
||||
//
|
||||
// check if the request is complete
|
||||
//
|
||||
if (Request->IsRequestComplete() == FALSE)
|
||||
{
|
||||
//
|
||||
// request is still in complete
|
||||
// get new queue head
|
||||
//
|
||||
Status = Request->GetQueueHead(&NewQueueHead);
|
||||
|
||||
//
|
||||
// add to pending list
|
||||
//
|
||||
LinkQueueHead(AsyncListQueueHead, NewQueueHead);
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// put queue head into completed queue head list
|
||||
//
|
||||
InsertTailList(&m_CompletedRequestAsyncList, &CurrentQH->LinkedQueueHeads);
|
||||
}
|
||||
}
|
||||
|
||||
VOID
|
||||
CUSBQueue::ProcessAsyncList(
|
||||
IN NTSTATUS Status,
|
||||
OUT PULONG ShouldRingDoorBell)
|
||||
{
|
||||
KIRQL OldLevel;
|
||||
PLIST_ENTRY Entry;
|
||||
PQUEUE_HEAD QueueHead;
|
||||
IUSBRequest * Request;
|
||||
|
||||
//
|
||||
// lock completed async list
|
||||
//
|
||||
KeAcquireSpinLock(&m_Lock, &OldLevel);
|
||||
|
||||
//
|
||||
// walk async list
|
||||
//
|
||||
Entry = AsyncListQueueHead->LinkedQueueHeads.Flink;
|
||||
|
||||
while(Entry != &AsyncListQueueHead->LinkedQueueHeads)
|
||||
{
|
||||
//
|
||||
// get queue head structure
|
||||
//
|
||||
QueueHead = (PQUEUE_HEAD)CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
|
||||
|
||||
//
|
||||
// sanity check
|
||||
//
|
||||
PC_ASSERT(QueueHead->Request);
|
||||
|
||||
//
|
||||
// get IUSBRequest interface
|
||||
//
|
||||
Request = (IUSBRequest*)QueueHead->Request;
|
||||
|
||||
|
||||
//
|
||||
// move to next entry
|
||||
//
|
||||
Entry = Entry->Flink;
|
||||
|
||||
DPRINT1("Request %p QueueHead %p Complete %d\n", Request, QueueHead, Request->IsQueueHeadComplete(QueueHead));
|
||||
|
||||
//
|
||||
// check if queue head is complete
|
||||
//
|
||||
if (Request->IsQueueHeadComplete(QueueHead))
|
||||
{
|
||||
//
|
||||
// current queue head is complete
|
||||
//
|
||||
QueueHeadCompletion(QueueHead, Status);
|
||||
|
||||
//
|
||||
// ring door bell is going to be necessary
|
||||
//
|
||||
*ShouldRingDoorBell = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// release lock
|
||||
//
|
||||
KeReleaseSpinLock(&m_Lock, OldLevel);
|
||||
|
||||
}
|
||||
|
||||
|
||||
VOID
|
||||
CUSBQueue::InterruptCallback(
|
||||
IN NTSTATUS Status,
|
||||
OUT PULONG ShouldRingDoorBell)
|
||||
{
|
||||
|
||||
DPRINT1("CUSBQueue::InterruptCallback\n");
|
||||
|
||||
//
|
||||
// iterate asynchronous list
|
||||
//
|
||||
*ShouldRingDoorBell = FALSE;
|
||||
ProcessAsyncList(Status, ShouldRingDoorBell);
|
||||
|
||||
//
|
||||
// TODO: implement periodic schedule processing
|
||||
//
|
||||
}
|
||||
|
||||
VOID
|
||||
CUSBQueue::QueueHeadCleanup(
|
||||
PQUEUE_HEAD CurrentQH)
|
||||
{
|
||||
IUSBRequest * Request;
|
||||
BOOLEAN ShouldReleaseWhenDone;
|
||||
|
||||
//
|
||||
// sanity checks
|
||||
//
|
||||
PC_ASSERT(CurrentQH->Token.Bits.Active == 0);
|
||||
PC_ASSERT(CurrentQH->Request);
|
||||
|
||||
//
|
||||
// get request
|
||||
//
|
||||
Request = (IUSBRequest*)CurrentQH->Request;
|
||||
Request->CompletionCallback(STATUS_SUCCESS /*FIXME*/, UrbStatus, CurrentQH);
|
||||
|
||||
//
|
||||
// let IUSBRequest free the queue head
|
||||
|
@ -689,6 +696,27 @@ CUSBQueue::CompleteAsyncRequests()
|
|||
QueueHeadCleanup(CurrentQH);
|
||||
}
|
||||
|
||||
//
|
||||
// is there a pending async entry
|
||||
//
|
||||
if (!IsListEmpty(&m_PendingRequestAsyncList))
|
||||
{
|
||||
//
|
||||
// remove first entry
|
||||
//
|
||||
Entry = RemoveHeadList(&m_CompletedRequestAsyncList);
|
||||
|
||||
//
|
||||
// get queue head structure
|
||||
//
|
||||
CurrentQH = (PQUEUE_HEAD)CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
|
||||
|
||||
//
|
||||
// Add it to the pending list
|
||||
//
|
||||
LinkQueueHead(AsyncListQueueHead, CurrentQH);
|
||||
}
|
||||
|
||||
//
|
||||
// release lock
|
||||
//
|
||||
|
|
|
@ -61,6 +61,7 @@ public:
|
|||
UCHAR GetDeviceAddress();
|
||||
NTSTATUS BuildSetupPacket();
|
||||
NTSTATUS BuildSetupPacketFromURB();
|
||||
ULONG InternalCalculateTransferLength();
|
||||
|
||||
// constructor / destructor
|
||||
CUSBRequest(IUnknown *OuterUnknown){}
|
||||
|
@ -252,10 +253,15 @@ CUSBRequest::InitializeWithIrp(
|
|||
//
|
||||
if (!Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL)
|
||||
{
|
||||
//
|
||||
// sanity check
|
||||
//
|
||||
PC_ASSERT(Urb->UrbBulkOrInterruptTransfer.TransferBuffer);
|
||||
|
||||
//
|
||||
// Create one using TransferBuffer
|
||||
//
|
||||
DPRINT1("Creating Mdl from Urb Buffer\n");
|
||||
DPRINT1("Creating Mdl from Urb Buffer %p Length %lu\n", Urb->UrbBulkOrInterruptTransfer.TransferBuffer, Urb->UrbBulkOrInterruptTransfer.TransferBufferLength);
|
||||
m_TransferBufferMDL = IoAllocateMdl(Urb->UrbBulkOrInterruptTransfer.TransferBuffer,
|
||||
Urb->UrbBulkOrInterruptTransfer.TransferBufferLength,
|
||||
FALSE,
|
||||
|
@ -370,8 +376,15 @@ CUSBRequest::CompletionCallback(
|
|||
//
|
||||
Urb->UrbHeader.Length = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// calculate transfer length
|
||||
//
|
||||
Urb->UrbBulkOrInterruptTransfer.TransferBufferLength = InternalCalculateTransferLength();
|
||||
}
|
||||
|
||||
DPRINT1("Request %p Completing Irp %p NtStatusCode %x UrbStatusCode %x\n", this, m_Irp, NtStatusCode, UrbStatusCode);
|
||||
DPRINT1("Request %p Completing Irp %p NtStatusCode %x UrbStatusCode %x Transferred Length %lu\n", this, m_Irp, NtStatusCode, UrbStatusCode, Urb->UrbBulkOrInterruptTransfer.TransferBufferLength);
|
||||
|
||||
//
|
||||
// FIXME: check if the transfer was split
|
||||
|
@ -525,9 +538,6 @@ CUSBRequest::GetTransferType()
|
|||
ULONG
|
||||
CUSBRequest::InternalGetTransferType()
|
||||
{
|
||||
PIO_STACK_LOCATION IoStack;
|
||||
PURB Urb;
|
||||
PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor;
|
||||
ULONG TransferType;
|
||||
|
||||
//
|
||||
|
@ -535,38 +545,12 @@ CUSBRequest::InternalGetTransferType()
|
|||
//
|
||||
if (m_Irp)
|
||||
{
|
||||
//
|
||||
// get stack location
|
||||
//
|
||||
IoStack = IoGetCurrentIrpStackLocation(m_Irp);
|
||||
ASSERT(m_EndpointDescriptor);
|
||||
|
||||
//
|
||||
// get urb
|
||||
// end point is defined in the low byte of bmAttributes
|
||||
//
|
||||
Urb = (PURB)IoStack->Parameters.Others.Argument1;
|
||||
|
||||
//
|
||||
// check if there is a handle
|
||||
//
|
||||
if (Urb->UrbBulkOrInterruptTransfer.PipeHandle)
|
||||
{
|
||||
//
|
||||
// cast to end point
|
||||
//
|
||||
EndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR)Urb->UrbBulkOrInterruptTransfer.PipeHandle;
|
||||
|
||||
//
|
||||
// end point is defined in the low byte of bmAttributes
|
||||
//
|
||||
TransferType = (EndpointDescriptor->bmAttributes & USB_ENDPOINT_TYPE_MASK);
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// no pipe handle, assume it is a control transfer
|
||||
//
|
||||
TransferType = USB_ENDPOINT_TYPE_CONTROL;
|
||||
}
|
||||
TransferType = (m_EndpointDescriptor->bmAttributes & USB_ENDPOINT_TYPE_MASK);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -585,30 +569,13 @@ CUSBRequest::InternalGetTransferType()
|
|||
UCHAR
|
||||
CUSBRequest::InternalGetPidDirection()
|
||||
{
|
||||
PIO_STACK_LOCATION IoStack;
|
||||
PURB Urb;
|
||||
PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor;
|
||||
|
||||
ASSERT(m_Irp);
|
||||
//
|
||||
// get stack location
|
||||
//
|
||||
IoStack = IoGetCurrentIrpStackLocation(m_Irp);
|
||||
|
||||
//
|
||||
// get urb
|
||||
//
|
||||
Urb = (PURB)IoStack->Parameters.Others.Argument1;
|
||||
|
||||
//
|
||||
// cast to end point
|
||||
//
|
||||
EndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR)Urb->UrbBulkOrInterruptTransfer.PipeHandle;
|
||||
ASSERT(m_EndpointDescriptor);
|
||||
|
||||
//
|
||||
// end point is defined in the low byte of bEndpointAddress
|
||||
//
|
||||
return (EndpointDescriptor->bEndpointAddress & USB_ENDPOINT_DIRECTION_MASK) >> 7;
|
||||
return (m_EndpointDescriptor->bEndpointAddress & USB_ENDPOINT_DIRECTION_MASK) >> 7;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
|
@ -816,16 +783,19 @@ CUSBRequest::BuildBulkTransferQueueHead(
|
|||
//
|
||||
// get virtual base of mdl
|
||||
//
|
||||
Base = MmGetMdlVirtualAddress(m_TransferBufferMDL);
|
||||
Base = MmGetSystemAddressForMdlSafe(m_TransferBufferMDL, NormalPagePriority);
|
||||
BytesAvailable = m_TransferBufferLength;
|
||||
|
||||
PC_ASSERT(m_EndpointDescriptor);
|
||||
PC_ASSERT(Base);
|
||||
|
||||
|
||||
DPRINT1("EndPointAddress %x\n", m_EndpointDescriptor->bEndpointAddress);
|
||||
DPRINT1("EndPointDirection %x\n", USB_ENDPOINT_DIRECTION_IN(m_EndpointDescriptor->bEndpointAddress));
|
||||
|
||||
DPRINT1("Request %p Base Address %p TransferBytesLength %lu\n", this, Base, BytesAvailable);
|
||||
DPRINT1("Request %p Base Address %p TransferBytesLength %lu MDL %p\n", this, Base, BytesAvailable, m_TransferBufferMDL);
|
||||
DPRINT1("InternalGetPidDirection() %d EndPointAddress %x\n", InternalGetPidDirection(), m_EndpointDescriptor->bEndpointAddress & 0x0F);
|
||||
DPRINT1("Irp %p QueueHead %p\n", m_Irp, QueueHead);
|
||||
|
||||
//PC_ASSERT(InternalGetPidDirection() == USB_ENDPOINT_DIRECTION_IN(m_EndpointDescriptor->bEndpointAddress));
|
||||
|
||||
|
@ -881,7 +851,7 @@ CUSBRequest::BuildBulkTransferQueueHead(
|
|||
//
|
||||
// check if request fills another page
|
||||
//
|
||||
if (PageOffset + BytesAvailable >= PAGE_SIZE)
|
||||
if (PageOffset + BytesAvailable > PAGE_SIZE)
|
||||
{
|
||||
//
|
||||
// move to next page
|
||||
|
@ -976,6 +946,11 @@ CUSBRequest::BuildBulkTransferQueueHead(
|
|||
}
|
||||
}
|
||||
|
||||
//
|
||||
// store transfer bytes of descriptor
|
||||
//
|
||||
m_TransferDescriptors[Index]->TotalBytesToTransfer = m_TransferDescriptors[Index]->Token.Bits.TotalBytesToTransfer;
|
||||
|
||||
//
|
||||
// Go ahead and link descriptors
|
||||
//
|
||||
|
@ -1579,6 +1554,36 @@ CUSBRequest::GetTransferBuffer(
|
|||
*OutMDL = m_TransferBufferMDL;
|
||||
*TransferLength = m_TransferBufferLength;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------
|
||||
ULONG
|
||||
CUSBRequest::InternalCalculateTransferLength()
|
||||
{
|
||||
if (!m_Irp)
|
||||
{
|
||||
//
|
||||
// FIXME: get length for control request
|
||||
//
|
||||
return m_TransferBufferLength;
|
||||
}
|
||||
|
||||
//
|
||||
// sanity check
|
||||
//
|
||||
ASSERT(m_EndpointDescriptor);
|
||||
|
||||
if (USB_ENDPOINT_DIRECTION_IN(m_EndpointDescriptor->bEndpointAddress))
|
||||
{
|
||||
//
|
||||
// bulk in request
|
||||
//
|
||||
return m_TransferDescriptors[0]->TotalBytesToTransfer - m_TransferDescriptors[0]->Token.Bits.TotalBytesToTransfer;
|
||||
}
|
||||
|
||||
//
|
||||
// bulk out transfer
|
||||
//
|
||||
return m_TransferBufferLength;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------
|
||||
NTSTATUS
|
||||
|
|
Loading…
Reference in a new issue