Author: janderwald Date: Wed Feb 22 17:24:43 2012 New Revision: 55809
URL: http://svn.reactos.org/svn/reactos?rev=55809&view=rev Log: [USBUHCI] - Queue dpc when the interrupt indicates completion of a transfer or an error interrupt - Implement checking if a queue head is complete - Free queue heads and associated endpoint descriptors
Modified: trunk/reactos/drivers/usb/usbuhci/hardware.cpp trunk/reactos/drivers/usb/usbuhci/hardware.h trunk/reactos/drivers/usb/usbuhci/interfaces.h trunk/reactos/drivers/usb/usbuhci/usb_queue.cpp trunk/reactos/drivers/usb/usbuhci/usb_request.cpp
Modified: trunk/reactos/drivers/usb/usbuhci/hardware.cpp URL: http://svn.reactos.org/svn/reactos/trunk/reactos/drivers/usb/usbuhci/hardwar... ============================================================================== --- trunk/reactos/drivers/usb/usbuhci/hardware.cpp [iso-8859-1] (original) +++ trunk/reactos/drivers/usb/usbuhci/hardware.cpp [iso-8859-1] Wed Feb 22 17:24:43 2012 @@ -686,7 +686,7 @@ m_QueueHead[Index]->PhysicalAddress = Address.LowPart; m_QueueHead[Index]->ElementPhysical = QH_TERMINATE;
- if (Index > 1) + if (Index > 0) { // // link queue heads @@ -696,6 +696,48 @@ } }
+ DPRINT1("Index %lu QueueHead %p LinkPhysical %x ElementPhysical %x PhysicalAddress %x Request %x NextElementDescriptor %x\n", + 0, + m_QueueHead[0], + m_QueueHead[0]->LinkPhysical, + m_QueueHead[0]->ElementPhysical, + m_QueueHead[0]->PhysicalAddress, + m_QueueHead[0]->Request, + m_QueueHead[0]->NextElementDescriptor); + DPRINT1("Index %lu QueueHead %p LinkPhysical %x ElementPhysical %x PhysicalAddress %x Request %x NextElementDescriptor %x\n", + 1, + m_QueueHead[1], + m_QueueHead[1]->LinkPhysical, + m_QueueHead[1]->ElementPhysical, + m_QueueHead[1]->PhysicalAddress, + m_QueueHead[1]->Request, + m_QueueHead[1]->NextElementDescriptor); + + DPRINT1("Index %lu QueueHead %p LinkPhysical %x ElementPhysical %x PhysicalAddress %x Request %x NextElementDescriptor %x\n", + 2, + m_QueueHead[2], + m_QueueHead[2]->LinkPhysical, + m_QueueHead[2]->ElementPhysical, + m_QueueHead[2]->PhysicalAddress, + m_QueueHead[2]->Request, + m_QueueHead[2]->NextElementDescriptor); + DPRINT1("Index %lu QueueHead %p LinkPhysical %x ElementPhysical %x PhysicalAddress %x Request %x NextElementDescriptor %x\n", + 3, + m_QueueHead[3], + m_QueueHead[3]->LinkPhysical, + m_QueueHead[3]->ElementPhysical, + m_QueueHead[3]->PhysicalAddress, + m_QueueHead[3]->Request, + m_QueueHead[3]->NextElementDescriptor); + DPRINT1("Index %lu QueueHead %p LinkPhysical %x ElementPhysical %x PhysicalAddress %x Request %x NextElementDescriptor %x\n", + 4, + m_QueueHead[4], + m_QueueHead[4]->LinkPhysical, + m_QueueHead[4]->ElementPhysical, + m_QueueHead[4]->PhysicalAddress, + m_QueueHead[4]->Request, + m_QueueHead[4]->NextElementDescriptor); + // // terminate last queue head with stray descriptor // @@ -708,7 +750,7 @@ DPRINT1("[USBUHCI] Failed to allocate queue head %x Index %x\n", Status, Index); return Status; } - +#if 0 // // init stray descriptor // @@ -722,7 +764,7 @@ // m_QueueHead[4]->LinkPhysical = m_StrayDescriptor->PhysicalAddress; m_QueueHead[4]->NextLogicalDescriptor = m_StrayDescriptor; - +#endif
// // allocate frame bandwidth array @@ -1271,7 +1313,14 @@ if (Acknowledge) { // + // acknowledge interrupt + // This->WriteRegister16(UHCI_USBSTS, Acknowledge); + + // + // queue dpc + // + KeInsertQueueDpc(&This->m_IntDpcObject, UlongToPtr(Status), NULL); }
// @@ -1356,6 +1405,7 @@ IN PVOID SystemArgument2) { CUSBHardwareDevice *This; + ULONG Status;
// // get parameters @@ -1363,8 +1413,24 @@ This = (CUSBHardwareDevice*)DeferredContext;
DPRINT("OhciDefferedRoutine\n"); - ASSERT(FALSE); - + + // + // get status + // + Status = PtrToUlong(SystemArgument1); + if (Status & (UHCI_USBSTS_USBINT | UHCI_USBSTS_ERRINT)) + { + // + // a transfer finished, inform the queue + // + This->m_UsbQueue->TransferInterrupt(Status & UHCI_USBSTS_USBINT); + return; + } + + // + // other event + // + DPRINT1("[USBUHCI] Status %x not handled\n", Status); }
VOID
Modified: trunk/reactos/drivers/usb/usbuhci/hardware.h URL: http://svn.reactos.org/svn/reactos/trunk/reactos/drivers/usb/usbuhci/hardwar... ============================================================================== --- trunk/reactos/drivers/usb/usbuhci/hardware.h [iso-8859-1] (original) +++ trunk/reactos/drivers/usb/usbuhci/hardware.h [iso-8859-1] Wed Feb 22 17:24:43 2012 @@ -92,10 +92,11 @@ ULONG BufferPhysical; // pointer to the buffer
// Software part - ULONG PhysicalAddress; // Physical address of this descriptor + ULONG PhysicalAddress; // Physical address of this descriptor PVOID NextLogicalDescriptor; - ULONG BufferSize; // Size of the buffer - PVOID BufferLogical; // Logical pointer to the buffer + ULONG BufferSize; // Size of the buffer + PVOID BufferLogical; // Logical pointer to the buffer + PVOID UserBuffer; }UHCI_TRANSFER_DESCRIPTOR, *PUHCI_TRANSFER_DESCRIPTOR;
#define TD_NEXT_IS_QH 0x02
Modified: trunk/reactos/drivers/usb/usbuhci/interfaces.h URL: http://svn.reactos.org/svn/reactos/trunk/reactos/drivers/usb/usbuhci/interfa... ============================================================================== --- trunk/reactos/drivers/usb/usbuhci/interfaces.h [iso-8859-1] (original) +++ trunk/reactos/drivers/usb/usbuhci/interfaces.h [iso-8859-1] Wed Feb 22 17:24:43 2012 @@ -437,6 +437,22 @@ // Description: returns device speed
virtual USB_DEVICE_SPEED GetDeviceSpeed() = 0; + +//----------------------------------------------------------------------------------------- +// +// CompletionCallback +// +// Description: notifies request that the endpoint descriptor is complete + + virtual VOID CompletionCallback() = 0; + +//----------------------------------------------------------------------------------------- +// +// FreeEndpointDescriptor +// +// Description: frees the associated endpoint descriptor and its general descriptors + + virtual VOID FreeEndpointDescriptor(struct _UHCI_QUEUE_HEAD * OutDescriptor) = 0; };
@@ -505,6 +521,14 @@ // Description: aborts all pending requsts of an device
virtual NTSTATUS AbortDevicePipe(UCHAR DeviceAddress, IN struct _USB_ENDPOINT * EndpointDescriptor) = 0; + +//----------------------------------------------------------------------------------------- +// +// TransferInterrupt +// +// Description: informs the queue that a interrupt completed + + virtual VOID TransferInterrupt(UCHAR ErrorInterrupt) = 0;
};
Modified: trunk/reactos/drivers/usb/usbuhci/usb_queue.cpp URL: http://svn.reactos.org/svn/reactos/trunk/reactos/drivers/usb/usbuhci/usb_que... ============================================================================== --- trunk/reactos/drivers/usb/usbuhci/usb_queue.cpp [iso-8859-1] (original) +++ trunk/reactos/drivers/usb/usbuhci/usb_queue.cpp [iso-8859-1] Wed Feb 22 17:24:43 2012 @@ -40,10 +40,16 @@ virtual NTSTATUS CancelRequests(); virtual NTSTATUS CreateUSBRequest(IUSBRequest **OutRequest); virtual NTSTATUS AbortDevicePipe(UCHAR DeviceAddress, IN struct _USB_ENDPOINT * EndpointDescriptor); + virtual VOID TransferInterrupt(UCHAR ErrorInterrupt);
// local VOID LinkQueueHead(PUHCI_QUEUE_HEAD QueueHead, PUHCI_QUEUE_HEAD NextQueueHead); + VOID UnLinkQueueHead(PUHCI_QUEUE_HEAD PreviousQueueHead, PUHCI_QUEUE_HEAD NextQueueHead); + BOOLEAN IsQueueHeadComplete(PUHCI_QUEUE_HEAD QueueHead); + NTSTATUS AddQueueHead(PUHCI_QUEUE_HEAD NewQueueHead); + VOID QueueHeadCleanup(IN PUHCI_QUEUE_HEAD QueueHead, IN PUHCI_QUEUE_HEAD PreviousQueueHead, OUT PUHCI_QUEUE_HEAD *NextQueueHead); +
// constructor / destructor CUSBQueue(IUnknown *OuterUnknown){} @@ -53,6 +59,7 @@ LONG m_Ref; // reference count KSPIN_LOCK m_Lock; // list lock PUSBHARDWAREDEVICE m_Hardware; // hardware + };
//================================================================================================= @@ -106,25 +113,23 @@ }
NTSTATUS -CUSBQueue::AddUSBRequest( - IUSBRequest * Request) -{ - PUHCI_QUEUE_HEAD NewQueueHead, QueueHead = NULL; - NTSTATUS Status; - - DPRINT("CUSBQueue::AddUSBRequest\n"); - - // - // get queue head - // - Status = Request->GetEndpointDescriptor(&NewQueueHead); - if (!NT_SUCCESS(Status)) - { - // - // failed to create queue head - // - DPRINT1("[USBUHCI] Failed to create queue head %x\n", Status); - return Status; +CUSBQueue::AddQueueHead( + PUHCI_QUEUE_HEAD NewQueueHead) +{ + PUSBREQUEST Request; + PUHCI_QUEUE_HEAD QueueHead = NULL; + + + // + // get request + // + Request = (PUSBREQUEST)NewQueueHead->Request; + if (!Request) + { + // + // no request + // + return STATUS_INVALID_PARAMETER; }
if (Request->GetTransferType() == USB_ENDPOINT_TYPE_CONTROL) @@ -184,6 +189,40 @@ // LinkQueueHead(QueueHead, NewQueueHead); return STATUS_SUCCESS; + +} + +NTSTATUS +CUSBQueue::AddUSBRequest( + IUSBRequest * Request) +{ + PUHCI_QUEUE_HEAD NewQueueHead; + NTSTATUS Status; + + // + // get queue head + // + Status = Request->GetEndpointDescriptor(&NewQueueHead); + if (!NT_SUCCESS(Status)) + { + // + // failed to create queue head + // + DPRINT1("[USBUHCI] Failed to create queue head %x\n", Status); + return Status; + } + + // + // sanity check + // + ASSERT(PVOID(Request) == NewQueueHead->Request); + + // + // add queue head + // + DPRINT1("AddUSBRequest Request %p\n", Request); + DPRINT1("NewQueueHead %p\n", NewQueueHead); + return AddQueueHead(NewQueueHead); }
VOID @@ -198,6 +237,17 @@ QueueHead->NextLogicalDescriptor = (PVOID)NextQueueHead; }
+ +VOID +CUSBQueue::UnLinkQueueHead( + PUHCI_QUEUE_HEAD QueueHeadToRemove, + PUHCI_QUEUE_HEAD PreviousQueueHead) +{ + PreviousQueueHead->LinkPhysical = QueueHeadToRemove->LinkPhysical; + PreviousQueueHead->NextLogicalDescriptor = QueueHeadToRemove->NextLogicalDescriptor; +} + + NTSTATUS CUSBQueue::CancelRequests() { @@ -233,6 +283,218 @@ return Status; }
+BOOLEAN +CUSBQueue::IsQueueHeadComplete( + IN PUHCI_QUEUE_HEAD QueueHead) +{ + PUHCI_TRANSFER_DESCRIPTOR Descriptor; + ULONG ErrorCount; + + if (QueueHead->NextElementDescriptor == NULL) + { + // + // empty queue head + // + DPRINT1("QueueHead %p empty element physical\n", QueueHead); + return FALSE; + } + + // + // check all descriptors + // + Descriptor = (PUHCI_TRANSFER_DESCRIPTOR)QueueHead->NextElementDescriptor; + while(Descriptor) + { + if (Descriptor->Status & TD_STATUS_ACTIVE) + { + // + // descriptor is still active + // + DPRINT1("Descriptor %p is active Status %x BufferSize %lu\n", Descriptor, Descriptor->Status, Descriptor->BufferSize); + return FALSE; + } + + if (Descriptor->Status & TD_ERROR_MASK) + { + // + // error happened + // + DPRINT1("[USBUHCI] Error detected at descriptor %p Physical %x\n", Descriptor, Descriptor->PhysicalAddress); + + // + // get error count + // + ErrorCount = (Descriptor->Status >> TD_ERROR_COUNT_SHIFT) & TD_ERROR_COUNT_MASK; + if (ErrorCount == 0) + { + // + // error retry count elapsed + // + DPRINT1("[USBUHCI] ErrorBuffer %x TimeOut %x Nak %x BitStuff %x\n", + Descriptor->Status & TD_STATUS_ERROR_BUFFER, + Descriptor->Status & TD_STATUS_ERROR_TIMEOUT, + Descriptor->Status & TD_STATUS_ERROR_NAK, + Descriptor->Status & TD_STATUS_ERROR_BITSTUFF); + return TRUE; + } + else if (Descriptor->Status & TD_STATUS_ERROR_BABBLE) + { + // + // babble error + // + DPRINT1("[USBUHCI] Babble detected\n"); + return TRUE; + } + else + { + // + // stall detected + // + DPRINT1("[USBUHCI] Stall detected\n"); + } + } + + // + // move to next descriptor + // + Descriptor = (PUHCI_TRANSFER_DESCRIPTOR)Descriptor->NextLogicalDescriptor; + } + + // + // request is complete + // + return TRUE; +} + +VOID +CUSBQueue::QueueHeadCleanup( + IN PUHCI_QUEUE_HEAD QueueHead, + IN PUHCI_QUEUE_HEAD PreviousQueueHead, + OUT PUHCI_QUEUE_HEAD *NextQueueHead) +{ + PUSBREQUEST Request; + PUHCI_QUEUE_HEAD NewQueueHead; + NTSTATUS Status; + + // + // unlink queue head + // + UnLinkQueueHead(QueueHead, PreviousQueueHead); + + // + // get next queue head + // + *NextQueueHead = (PUHCI_QUEUE_HEAD)PreviousQueueHead->NextLogicalDescriptor; + ASSERT(*NextQueueHead != QueueHead); + + // + // the queue head is complete, is the transfer now completed? + // + Request = (PUSBREQUEST)QueueHead->Request; + ASSERT(Request); + + // + // free queue head + // + DPRINT1("Request %p\n", Request); + Request->FreeEndpointDescriptor(QueueHead); + + // + // check if transfer is complete + // + if (Request->IsRequestComplete()) + { + // + // the transfer is complete + // + Request->CompletionCallback(); + Request->Release(); + return; + } + + // + // grab new queue head + // + Status = Request->GetEndpointDescriptor(&NewQueueHead); + if (!NT_SUCCESS(Status)) + { + // + // failed to get new queue head + // + DPRINT1("[USBUHCI] Failed to get new queue head with %x\n", Status); + Request->CompletionCallback(); + Request->Release(); + return; + } + + // + // Link queue head + // + Status = AddQueueHead(NewQueueHead); + if (!NT_SUCCESS(Status)) + { + // + // failed to get new queue head + // + DPRINT1("[USBUHCI] Failed to add queue head with %x\n", Status); + Request->CompletionCallback(); + Request->Release(); + return; + } + +} + +VOID +CUSBQueue::TransferInterrupt( + UCHAR ErrorInterrupt) +{ + KIRQL OldLevel; + PUHCI_QUEUE_HEAD QueueHead, PreviousQueueHead = NULL; + BOOLEAN IsComplete; + + // + // acquire lock + // + KeAcquireSpinLock(&m_Lock, &OldLevel); + + // + // get queue head + // + m_Hardware->GetQueueHead(UHCI_INTERRUPT_QUEUE, &QueueHead); + + while(QueueHead) + { + // + // is queue head complete + // + DPRINT1("QueueHead %p\n", QueueHead); + IsComplete = IsQueueHeadComplete(QueueHead); + if (IsComplete) + { + // + // cleanup queue head + // + QueueHeadCleanup(QueueHead, PreviousQueueHead, &QueueHead); + continue; + } + + // + // backup previous queue head + // + PreviousQueueHead = QueueHead; + + // + // get next queue head + // + QueueHead = (PUHCI_QUEUE_HEAD)QueueHead->NextLogicalDescriptor; + } + + // + // release lock + // + KeReleaseSpinLock(&m_Lock, OldLevel); +} + NTSTATUS CreateUSBQueue( PUSBQUEUE *OutUsbQueue)
Modified: trunk/reactos/drivers/usb/usbuhci/usb_request.cpp URL: http://svn.reactos.org/svn/reactos/trunk/reactos/drivers/usb/usbuhci/usb_req... ============================================================================== --- trunk/reactos/drivers/usb/usbuhci/usb_request.cpp [iso-8859-1] (original) +++ trunk/reactos/drivers/usb/usbuhci/usb_request.cpp [iso-8859-1] Wed Feb 22 17:24:43 2012 @@ -46,7 +46,8 @@ virtual BOOLEAN IsQueueHeadComplete(struct _QUEUE_HEAD * QueueHead); virtual UCHAR GetInterval(); virtual USB_DEVICE_SPEED GetDeviceSpeed(); - + virtual VOID CompletionCallback(); + virtual VOID FreeEndpointDescriptor(struct _UHCI_QUEUE_HEAD * OutDescriptor);
// local functions ULONG InternalGetTransferType(); @@ -829,7 +830,7 @@ // // FIXME FIXME FIXME FIXME FIXME // - MaxPacketSize = 1280; + MaxPacketSize = 64; //1280;
do { @@ -857,7 +858,14 @@ // // copy buffer // - RtlCopyMemory(CurrentDescriptor->BufferLogical, TransferBuffer, CurrentBufferSize); + RtlCopyMemory(CurrentDescriptor->BufferLogical, (PVOID)((ULONG_PTR)TransferBuffer + TransferBufferOffset), CurrentBufferSize); + } + else + { + // + // store user buffer + // + CurrentDescriptor->UserBuffer = (PVOID)((ULONG_PTR)TransferBuffer + TransferBufferOffset); }
if (!FirstDescriptor) @@ -1075,7 +1083,7 @@ Status = BuildTransferDescriptorChain(MmGetMdlVirtualAddress(m_TransferBufferMDL), m_TransferBufferLength, Direction ? TD_TOKEN_IN : TD_TOKEN_OUT, - FALSE, + TRUE, &FirstDescriptor, &LastDescriptor, &ChainDescriptorLength, @@ -1131,6 +1139,173 @@ return m_DeviceSpeed; }
+VOID +CUSBRequest::FreeEndpointDescriptor( + struct _UHCI_QUEUE_HEAD * OutDescriptor) +{ + PUHCI_TRANSFER_DESCRIPTOR Descriptor, NextDescriptor; + ULONG ErrorCount; + + // + // grab first transfer descriptor + // + Descriptor = (PUHCI_TRANSFER_DESCRIPTOR)OutDescriptor->NextElementDescriptor; + while(Descriptor) + { + if (Descriptor->Status & TD_ERROR_MASK) + { + // + // error happened + // + DPRINT1("[USBUHCI] Error detected at descriptor %p Physical %x\n", Descriptor, Descriptor->PhysicalAddress); + + // + // get error count + // + ErrorCount = (Descriptor->Status >> TD_ERROR_COUNT_SHIFT) & TD_ERROR_COUNT_MASK; + if (ErrorCount == 0) + { + // + // error retry count elapsed + // + m_NtStatusCode = STATUS_UNSUCCESSFUL; + + if (Descriptor->Status & TD_STATUS_ERROR_BUFFER) + { + DPRINT1("[USBUHCI] Buffer Error detected in descriptor %p\n", Descriptor); + m_UrbStatusCode = USBD_STATUS_DATA_BUFFER_ERROR; + } + else if (Descriptor->Status & TD_STATUS_ERROR_TIMEOUT) + { + DPRINT1("[USBUHCI] Timeout detected in descriptor %p\n", Descriptor); + m_UrbStatusCode = USBD_STATUS_TIMEOUT; + } + else if (Descriptor->Status & TD_STATUS_ERROR_NAK) + { + DPRINT1("[USBUHCI] Unexpected pid detected in descriptor %p\n", Descriptor); + m_UrbStatusCode = USBD_STATUS_UNEXPECTED_PID; + } + else if (Descriptor->Status & TD_STATUS_ERROR_BITSTUFF) + { + DPRINT1("[USBUHCI] BitStuff detected in descriptor %p\n", Descriptor); + m_UrbStatusCode = USBD_STATUS_BTSTUFF; + } + } + else if (Descriptor->Status & TD_STATUS_ERROR_BABBLE) + { + // + // babble error + // + DPRINT1("[USBUHCI] Babble detected in descriptor %p\n", Descriptor); + m_UrbStatusCode = USBD_STATUS_BABBLE_DETECTED; + } + else + { + // + // stall detected + // + DPRINT1("[USBUHCI] Stall detected\n"); + m_UrbStatusCode = USBD_STATUS_STALL_PID; + } + } + else + { + // + // FIXME detect actual length + // + if (Descriptor->UserBuffer) + { + // + // copy contents back + // + RtlCopyMemory(Descriptor->UserBuffer, Descriptor->BufferLogical, Descriptor->BufferSize); + } + } + // + // move to next descriptor + // + NextDescriptor = (PUHCI_TRANSFER_DESCRIPTOR)Descriptor->NextLogicalDescriptor; + + // + // free endpoint descriptor + // + FreeDescriptor(Descriptor); + + // + // move to next + // + Descriptor = NextDescriptor; + } + + // + // now free queue head + // + m_DmaManager->Release(OutDescriptor, sizeof(UHCI_QUEUE_HEAD)); + +} + +VOID +CUSBRequest::CompletionCallback() +{ + PIO_STACK_LOCATION IoStack; + PURB Urb; + + DPRINT("CUSBRequest::CompletionCallback\n"); + + if (m_Irp) + { + // + // set irp completion status + // + m_Irp->IoStatus.Status = m_NtStatusCode; + + // + // get current irp stack location + // + IoStack = IoGetCurrentIrpStackLocation(m_Irp); + + // + // get urb + // + Urb = (PURB)IoStack->Parameters.Others.Argument1; + + // + // store urb status + // + Urb->UrbHeader.Status = m_UrbStatusCode; + + // + // Check if the MDL was created + // + if (!Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL) + { + // + // Free Mdl + // + IoFreeMdl(m_TransferBufferMDL); + } + + // + // FIXME calculate length + // + + // + // complete request + // + IoCompleteRequest(m_Irp, IO_NO_INCREMENT); + } + else + { + // + // signal completion event + // + PC_ASSERT(m_CompletionEvent); + KeSetEvent(m_CompletionEvent, 0, FALSE); + } + +} + + //----------------------------------------------------------------------------------------- NTSTATUS InternalCreateUSBRequest(