[USBEHCI_NEW]

- Return the physical address of the async queue head in CUSBHardwareDevice::GetAsyncListRegister
- Remove superflous entries from queue head structure, they are processed within IUSBRequest class
- Remove USBHI_GetPortHackFlags, this function has been deprecated
- Add interface functions for IUSBRequest / IUSBQueue
- Callback into IUSBQueue when a hardware interrupt arrives
- Implement callback function to check for completed queue heads / free completed queue head depending on the Async Advance interrupt bit

svn path=/branches/usb-bringup/; revision=51466
This commit is contained in:
Johannes Anderwald 2011-04-27 16:23:42 +00:00
parent 346a95197f
commit 591cffee7c
6 changed files with 386 additions and 55 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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