diff --git a/drivers/usb/usbehci_new/hardware.cpp b/drivers/usb/usbehci_new/hardware.cpp index 6d57f0ae4cf..aa9628433d1 100644 --- a/drivers/usb/usbehci_new/hardware.cpp +++ b/drivers/usb/usbehci_new/hardware.cpp @@ -87,30 +87,39 @@ public: virtual ~CUSBHardwareDevice(){} protected: - LONG m_Ref; - PDRIVER_OBJECT m_DriverObject; - PDEVICE_OBJECT m_PhysicalDeviceObject; - PDEVICE_OBJECT m_FunctionalDeviceObject; - PDEVICE_OBJECT m_NextDeviceObject; - KSPIN_LOCK m_Lock; - PKINTERRUPT m_Interrupt; - KDPC m_IntDpcObject; - PVOID VirtualBase; - PHYSICAL_ADDRESS PhysicalAddress; - PULONG m_Base; - PDMA_ADAPTER m_Adapter; - ULONG m_MapRegisters; - EHCI_CAPS m_Capabilities; - USHORT m_VendorID; - USHORT m_DeviceID; - PQUEUE_HEAD AsyncQueueHead; - PUSBQUEUE m_UsbQueue; - PDMAMEMORYMANAGER m_MemoryManager; - HD_INIT_CALLBACK* m_SCECallBack; - PVOID m_SCEContext; + LONG m_Ref; // reference count + PDRIVER_OBJECT m_DriverObject; // driver object + PDEVICE_OBJECT m_PhysicalDeviceObject; // pdo + PDEVICE_OBJECT m_FunctionalDeviceObject; // fdo (hcd controller) + PDEVICE_OBJECT m_NextDeviceObject; // lower device object + KSPIN_LOCK m_Lock; // hardware lock + PKINTERRUPT m_Interrupt; // interrupt object + KDPC m_IntDpcObject; // dpc object for deferred isr processing + PVOID VirtualBase; // virtual base for memory manager + PHYSICAL_ADDRESS PhysicalAddress; // physical base for memory manager + PULONG m_Base; // EHCI operational port base registers + PDMA_ADAPTER m_Adapter; // dma adapter object + ULONG m_MapRegisters; // map registers count + EHCI_CAPS m_Capabilities; // EHCI caps + USHORT m_VendorID; // vendor id + USHORT m_DeviceID; // device id + PQUEUE_HEAD AsyncQueueHead; // async queue head terminator + PUSBQUEUE m_UsbQueue; // usb request queue + PDMAMEMORYMANAGER m_MemoryManager; // memory manager + HD_INIT_CALLBACK* m_SCECallBack; // status change callback routine + PVOID m_SCEContext; // status change callback routine context + BOOLEAN m_DoorBellRingInProgress; // door bell ring in progress + + // set command VOID SetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd); + + // get command VOID GetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd); + + // read register ULONG EHCI_READ_REGISTER_ULONG(ULONG Offset); + + // write register VOID EHCI_WRITE_REGISTER_ULONG(ULONG Offset, ULONG Value); }; @@ -315,6 +324,7 @@ CUSBHardwareDevice::PnpStart( m_Capabilities.HCCParamsLong = READ_REGISTER_ULONG((PULONG)((ULONG)ResourceBase + 8)); DPRINT1("Controller has %d Ports\n", m_Capabilities.HCSParams.PortCount); + DPRINT1("Controller EHCI Version %x\n", m_Capabilities.HCIVersion); if (m_Capabilities.HCSParams.PortRouteRules) { for (Count = 0; Count < m_Capabilities.HCSParams.PortCount; Count++) @@ -850,7 +860,7 @@ CUSBHardwareDevice::SetPeriodicListRegister( ULONG CUSBHardwareDevice::GetAsyncListRegister() { - return PhysicalAddress.LowPart; + return AsyncQueueHead->PhysicalAddr; } ULONG CUSBHardwareDevice::GetPeriodicListRegister() @@ -946,11 +956,86 @@ EhciDefferedRoutine( IN PVOID SystemArgument2) { CUSBHardwareDevice *This; - ULONG CStatus, PortStatus, PortCount, i; + ULONG CStatus, PortStatus, PortCount, i, ShouldRingDoorBell; + NTSTATUS Status = STATUS_SUCCESS; + EHCI_USBCMD_CONTENT UsbCmd; This = (CUSBHardwareDevice*) SystemArgument1; CStatus = (ULONG) SystemArgument2; + + // + // check for completion of async schedule + // + if (CStatus & (EHCI_STS_RECL| EHCI_STS_INT | EHCI_ERROR_INT)) + { + if (CStatus & EHCI_ERROR_INT) + { + // + // controller reported error + // + Status = STATUS_UNSUCCESSFUL; + } + + // + // check if there is a door bell ring in progress + // + if (This->m_DoorBellRingInProgress == FALSE) + { + // + // inform IUSBQueue of a completed queue head + // + This->m_UsbQueue->InterruptCallback(Status, &ShouldRingDoorBell); + + // + // was a queue head completed? + // + if (ShouldRingDoorBell) + { + // + // set door ring bell in progress status flag + // + This->m_DoorBellRingInProgress = TRUE; + + // + // get command register + // + This->GetCommandRegister(&UsbCmd); + + // + // set door rang bell bit + // + UsbCmd.DoorBell = TRUE; + + // + // update command status + // + This->SetCommandRegister(&UsbCmd); + } + } + } + + // + // check if the controller has acknowledged the door bell + // + if (CStatus & EHCI_STS_IAA) + { + // + // controller has acknowledged, assert we rang the bell + // + PC_ASSERT(This->m_DoorBellRingInProgress == TRUE); + + // + // now notify IUSBQueue that it can free completed requests + // + This->m_UsbQueue->CompleteAsyncRequests(); + + // + // door ring bell completed + // + This->m_DoorBellRingInProgress = FALSE; + } + This->GetDeviceDetails(NULL, NULL, &PortCount, NULL); if (CStatus & EHCI_STS_PCD) { diff --git a/drivers/usb/usbehci_new/hardware.h b/drivers/usb/usbehci_new/hardware.h index 321e9a92522..1c97fb3ddb4 100644 --- a/drivers/usb/usbehci_new/hardware.h +++ b/drivers/usb/usbehci_new/hardware.h @@ -190,10 +190,6 @@ typedef struct _QUEUE_HEAD //Software ULONG PhysicalAddr; LIST_ENTRY LinkedQueueHeads; - PQUEUE_TRANSFER_DESCRIPTOR TransferDescriptor; - PIRP IrpToComplete; - PMDL Mdl; - PKEVENT Event; PVOID Request; } QUEUE_HEAD, *PQUEUE_HEAD; diff --git a/drivers/usb/usbehci_new/hub_controller.cpp b/drivers/usb/usbehci_new/hub_controller.cpp index 9e57952d202..619097d8cfe 100644 --- a/drivers/usb/usbehci_new/hub_controller.cpp +++ b/drivers/usb/usbehci_new/hub_controller.cpp @@ -1986,16 +1986,6 @@ USBHI_RestoreUsbDevice( return STATUS_NOT_IMPLEMENTED; } -NTSTATUS -USB_BUSIFFN -USBHI_GetPortHackFlags( - PVOID BusContext, - PULONG Flags) -{ - UNIMPLEMENTED - return STATUS_NOT_IMPLEMENTED; -} - NTSTATUS USB_BUSIFFN USBHI_QueryDeviceInformation( @@ -2565,7 +2555,6 @@ CHubController::HandleQueryInterface( InterfaceHub->GetUsbDescriptors = USBHI_GetUsbDescriptors; InterfaceHub->RemoveUsbDevice = USBHI_RemoveUsbDevice; InterfaceHub->RestoreUsbDevice = USBHI_RestoreUsbDevice; - InterfaceHub->GetPortHackFlags = USBHI_GetPortHackFlags; InterfaceHub->QueryDeviceInformation = USBHI_QueryDeviceInformation; } diff --git a/drivers/usb/usbehci_new/interfaces.h b/drivers/usb/usbehci_new/interfaces.h index c85dab68d41..ee1bc6541a4 100644 --- a/drivers/usb/usbehci_new/interfaces.h +++ b/drivers/usb/usbehci_new/interfaces.h @@ -482,8 +482,27 @@ DECLARE_INTERFACE_(IUSBRequest, IUnknown) // Description: frees the queue head with the associated transfer descriptors virtual VOID FreeQueueHead(struct _QUEUE_HEAD * QueueHead) = 0; + +//--------------------------------------------------------------------------------------- +// +// GetTransferBuffer +// +// Description: this function returns the transfer buffer mdl and length +// Used by IUSBQueue for mapping buffer contents with DMA + + virtual VOID GetTransferBuffer(OUT PMDL * OutMDL, + OUT PULONG TransferLength) = 0; + +//-------------------------------------------------------------------------------------- +// +// IsQueueHeadComplete +// +// Description: returns true when the queue head which was passed as a parameter has been completed + + virtual BOOLEAN IsQueueHeadComplete(struct _QUEUE_HEAD * QueueHead) = 0; }; + typedef IUSBRequest *PUSBREQUEST; //========================================================================================= @@ -539,6 +558,24 @@ DECLARE_INTERFACE_(IUSBQueue, IUnknown) // Description: creates an usb request virtual NTSTATUS CreateUSBRequest(IUSBRequest **OutRequest) = 0; + +//-------------------------------------------------------------------------------------- +// +// InterruptCallback +// +// Description: callback when the periodic / asynchronous queue has been completed / queue head been completed + + virtual VOID InterruptCallback(IN NTSTATUS Status, OUT PULONG ShouldRingDoorBell) = 0; + +//-------------------------------------------------------------------------------------- +// +// CompleteAsyncRequests +// +// Description: once a request has been completed it is moved to pending queue. Since a queue head should only be freed +// after a door bell ring, this needs some synchronization. +// This function gets called by IUSBHardware after it the Interrupt on Async Advance bit has been set + + virtual VOID CompleteAsyncRequests() = 0; }; typedef IUSBQueue *PUSBQUEUE; diff --git a/drivers/usb/usbehci_new/usb_queue.cpp b/drivers/usb/usbehci_new/usb_queue.cpp index 7bae9c680cc..e88c6b347cc 100644 --- a/drivers/usb/usbehci_new/usb_queue.cpp +++ b/drivers/usb/usbehci_new/usb_queue.cpp @@ -33,12 +33,14 @@ public: return m_Ref; } - NTSTATUS Initialize(IN PUSBHARDWAREDEVICE Hardware, PDMA_ADAPTER AdapterObject, IN OPTIONAL PKSPIN_LOCK Lock); - ULONG GetPendingRequestCount(); - NTSTATUS AddUSBRequest(PURB Urb); - NTSTATUS AddUSBRequest(IUSBRequest * Request); - NTSTATUS CancelRequests(); - NTSTATUS CreateUSBRequest(IUSBRequest **OutRequest); + virtual NTSTATUS Initialize(IN PUSBHARDWAREDEVICE Hardware, PDMA_ADAPTER AdapterObject, IN OPTIONAL PKSPIN_LOCK Lock); + virtual ULONG GetPendingRequestCount(); + virtual NTSTATUS AddUSBRequest(PURB Urb); + virtual NTSTATUS AddUSBRequest(IUSBRequest * Request); + virtual NTSTATUS CancelRequests(); + virtual NTSTATUS CreateUSBRequest(IUSBRequest **OutRequest); + virtual VOID InterruptCallback(IN NTSTATUS Status, OUT PULONG ShouldRingDoorBell); + virtual VOID CompleteAsyncRequests(); // constructor / destructor CUSBQueue(IUnknown *OuterUnknown){} @@ -50,6 +52,7 @@ protected: PDMA_ADAPTER m_Adapter; PQUEUE_HEAD AsyncListQueueHead; PQUEUE_HEAD PendingListQueueHead; + LIST_ENTRY m_CompletedRequestAsyncList; // queue head manipulation functions VOID LinkQueueHead(PQUEUE_HEAD HeadQueueHead, PQUEUE_HEAD NewQueueHead); @@ -57,8 +60,11 @@ protected: VOID LinkQueueHeadChain(PQUEUE_HEAD HeadQueueHead, PQUEUE_HEAD NewQueueHead); PQUEUE_HEAD UnlinkQueueHeadChain(PQUEUE_HEAD HeadQueueHead, ULONG Count); + // processes the async list + VOID ProcessAsyncList(IN NTSTATUS Status, OUT PULONG ShouldRingDoorBell); + // called for each completed queue head - NTSTATUS QueueHeadCompletion(PQUEUE_HEAD QueueHead, NTSTATUS Status); + VOID QueueHeadCompletion(PQUEUE_HEAD QueueHead, NTSTATUS Status); // called when the completion queue is cleaned up VOID QueueHeadCleanup(PQUEUE_HEAD QueueHead); @@ -120,6 +126,17 @@ CUSBQueue::Initialize( // InitializeListHead(&PendingListQueueHead->LinkedQueueHeads); + // + // fake the queue head as the first queue head + // + PendingListQueueHead->PhysicalAddr = ((ULONG_PTR)AsyncListQueueHead | QH_TYPE_QH); + + // + // Initialize completed async list head + // + InitializeListHead(&m_CompletedRequestAsyncList); + + return Status; } @@ -234,13 +251,44 @@ CUSBQueue::UnlinkQueueHead( PQUEUE_HEAD PreviousQH, NextQH; PLIST_ENTRY Entry; + // + // sanity check: there must be at least one queue head with halted bit set + // + PC_ASSERT(QueueHead->Token.Bits.Halted == 0); + + // + // get previous link + // Entry = QueueHead->LinkedQueueHeads.Blink; + + // + // get queue head structure + // PreviousQH = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads); + + // + // get next link + // Entry = QueueHead->LinkedQueueHeads.Flink; + + // + // get queue head structure + // NextQH = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads); + + // + // sanity check + // ASSERT(QueueHead->HorizontalLinkPointer == (NextQH->PhysicalAddr | QH_TYPE_QH)); + + // + // remove queue head from linked list + // PreviousQH->HorizontalLinkPointer = NextQH->PhysicalAddr | QH_TYPE_QH; + // + // remove software link + // RemoveEntryList(&QueueHead->LinkedQueueHeads); } @@ -326,7 +374,7 @@ CUSBQueue::UnlinkQueueHeadChain( return FirstQueueHead; } -NTSTATUS +VOID CUSBQueue::QueueHeadCompletion( PQUEUE_HEAD CurrentQH, NTSTATUS Status) @@ -415,14 +463,94 @@ CUSBQueue::QueueHeadCompletion( else { // - // FIXME: put queue head into completed queue head list + // 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 = PendingListQueueHead->LinkedQueueHeads.Flink; + + while(Entry != &PendingListQueueHead->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 + // + if (Request->IsQueueHeadComplete(QueueHead)) + { + // + // current queue head is complete + // + QueueHeadCompletion(QueueHead, Status); + + // + // ring door bell is going to be necessary + // + *ShouldRingDoorBell = TRUE; + } } // - // done + // release lock + // + KeReleaseSpinLock(&m_Lock, OldLevel); + +} + + +VOID +CUSBQueue::InterruptCallback( + IN NTSTATUS Status, + OUT PULONG ShouldRingDoorBell) +{ + // + // iterate asynchronous list + // + *ShouldRingDoorBell = FALSE; + ProcessAsyncList(Status, ShouldRingDoorBell); + + // + // TODO: implement periodic schedule processing // - return STATUS_SUCCESS; } VOID @@ -474,6 +602,47 @@ CUSBQueue::QueueHeadCleanup( // } +VOID +CUSBQueue::CompleteAsyncRequests() +{ + KIRQL OldLevel; + PLIST_ENTRY Entry; + PQUEUE_HEAD CurrentQH; + + // + // first acquire request lock + // + KeAcquireSpinLock(&m_Lock, &OldLevel); + + // + // the list should not be empty + // + PC_ASSERT(!IsListEmpty(&m_CompletedRequestAsyncList)); + + while(!IsListEmpty(&m_CompletedRequestAsyncList)) + { + // + // remove first entry + // + Entry = RemoveHeadList(&m_CompletedRequestAsyncList); + + // + // get queue head structure + // + CurrentQH = (PQUEUE_HEAD)CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads); + + // + // complete request now + // + QueueHeadCleanup(CurrentQH); + } + + // + // release lock + // + KeReleaseSpinLock(&m_Lock, OldLevel); +} + NTSTATUS CreateUSBQueue( PUSBQUEUE *OutUsbQueue) diff --git a/drivers/usb/usbehci_new/usb_request.cpp b/drivers/usb/usbehci_new/usb_request.cpp index a02b7e7fbad..f196d3e835d 100644 --- a/drivers/usb/usbehci_new/usb_request.cpp +++ b/drivers/usb/usbehci_new/usb_request.cpp @@ -47,6 +47,9 @@ public: virtual BOOLEAN IsRequestInitialized(); virtual BOOLEAN ShouldReleaseRequestAfterCompletion(); virtual VOID FreeQueueHead(struct _QUEUE_HEAD * QueueHead); + virtual VOID GetTransferBuffer(OUT PMDL * OutMDL, OUT PULONG TransferLength); + virtual BOOLEAN IsQueueHeadComplete(struct _QUEUE_HEAD * QueueHead); + // local functions ULONG InternalGetTransferType(); @@ -640,12 +643,6 @@ CUSBRequest::BuildControlTransferQueueHead( m_TransferDescriptors[1]->Token.Bits.InterruptOnComplete = TRUE; } - // - // Control Transfers have only one in or out buffer if one at all. Put in the QueueHead the - // same as BulkTransfers. USBQueue will use the Mdl to fill in the BufferPointers - // - QueueHead->Mdl = m_TransferBufferMDL; - // // link setup packet into buffer - Physical Address!!! // @@ -1149,6 +1146,64 @@ CUSBRequest::FreeQueueHead( } } +//----------------------------------------------------------------------------------------- +BOOLEAN +CUSBRequest::IsQueueHeadComplete( + struct _QUEUE_HEAD * QueueHead) +{ + ULONG Index; + + // + // first check - is the queue head currently active + // + if (QueueHead->Token.Bits.Active) + { + // + // queue head is active (currently processed) + // + return FALSE; + } + + // + // FIXME: support chained queue heads + // + for(Index = 0; Index < 3; Index++) + { + // + // check transfer descriptors for completion + // + if (m_TransferDescriptors[Index]) + { + // + // check for serious error + // + PC_ASSERT(m_TransferDescriptors[Index]->Token.Bits.Halted == 0); + + // + // the transfer descriptor should be in the same state as the queue head + // + PC_ASSERT(m_TransferDescriptors[Index]->Token.Bits.Active == 0); + } + } + + return TRUE; +} + +//----------------------------------------------------------------------------------------- +VOID +CUSBRequest::GetTransferBuffer( + OUT PMDL * OutMDL, + OUT PULONG TransferLength) +{ + // sanity checks + PC_ASSERT(OutMDL); + PC_ASSERT(TransferLength); + + *OutMDL = m_TransferBufferMDL; + *TransferLength = m_TransferBufferLength; +} + +//----------------------------------------------------------------------------------------- NTSTATUS InternalCreateUSBRequest( PUSBREQUEST *OutRequest)