[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:
Johannes Anderwald 2011-04-30 17:44:43 +00:00
parent 547ea14508
commit 6321f84f30
3 changed files with 232 additions and 198 deletions

View file

@ -127,6 +127,7 @@ typedef struct _QUEUE_TRANSFER_DESCRIPTOR
//Software
ULONG PhysicalAddr;
LIST_ENTRY LinkedDescriptors;
ULONG TotalBytesToTransfer;
} QUEUE_TRANSFER_DESCRIPTOR, *PQUEUE_TRANSFER_DESCRIPTOR;
//

View file

@ -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
//

View file

@ -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