[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

svn path=/trunk/; revision=55809
This commit is contained in:
Johannes Anderwald 2012-02-22 17:24:43 +00:00
parent e9cca27577
commit 6528183f15
5 changed files with 550 additions and 22 deletions

View file

@ -686,7 +686,7 @@ CUSBHardwareDevice::InitializeController()
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 @@ CUSBHardwareDevice::InitializeController()
}
}
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 @@ CUSBHardwareDevice::InitializeController()
DPRINT1("[USBUHCI] Failed to allocate queue head %x Index %x\n", Status, Index);
return Status;
}
#if 0
//
// init stray descriptor
//
@ -722,7 +764,7 @@ CUSBHardwareDevice::InitializeController()
//
m_QueueHead[4]->LinkPhysical = m_StrayDescriptor->PhysicalAddress;
m_QueueHead[4]->NextLogicalDescriptor = m_StrayDescriptor;
#endif
//
// allocate frame bandwidth array
@ -1270,8 +1312,15 @@ InterruptServiceRoutine(
//
if (Acknowledge)
{
//
// acknowledge interrupt
//
This->WriteRegister16(UHCI_USBSTS, Acknowledge);
//
// queue dpc
//
KeInsertQueueDpc(&This->m_IntDpcObject, UlongToPtr(Status), NULL);
}
//
@ -1356,6 +1405,7 @@ OhciDefferedRoutine(
IN PVOID SystemArgument2)
{
CUSBHardwareDevice *This;
ULONG Status;
//
// get parameters
@ -1363,8 +1413,24 @@ OhciDefferedRoutine(
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

View file

@ -92,10 +92,11 @@ typedef struct _UHCI_TRANSFER_DESCRIPTOR
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

View file

@ -437,6 +437,22 @@ DECLARE_INTERFACE_(IUSBRequest, IUnknown)
// 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;
};
@ -506,6 +522,14 @@ DECLARE_INTERFACE_(IUSBQueue, IUnknown)
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;
};
typedef IUSBQueue *PUSBQUEUE;

View file

@ -40,10 +40,16 @@ public:
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 @@ protected:
LONG m_Ref; // reference count
KSPIN_LOCK m_Lock; // list lock
PUSBHARDWAREDEVICE m_Hardware; // hardware
};
//=================================================================================================
@ -106,25 +113,23 @@ CUSBQueue::GetPendingRequestCount()
}
NTSTATUS
CUSBQueue::AddUSBRequest(
IUSBRequest * Request)
CUSBQueue::AddQueueHead(
PUHCI_QUEUE_HEAD NewQueueHead)
{
PUHCI_QUEUE_HEAD NewQueueHead, QueueHead = NULL;
NTSTATUS Status;
PUSBREQUEST Request;
PUHCI_QUEUE_HEAD QueueHead = NULL;
DPRINT("CUSBQueue::AddUSBRequest\n");
//
// get queue head
// get request
//
Status = Request->GetEndpointDescriptor(&NewQueueHead);
if (!NT_SUCCESS(Status))
Request = (PUSBREQUEST)NewQueueHead->Request;
if (!Request)
{
//
// failed to create queue head
// no request
//
DPRINT1("[USBUHCI] Failed to create queue head %x\n", Status);
return Status;
return STATUS_INVALID_PARAMETER;
}
if (Request->GetTransferType() == USB_ENDPOINT_TYPE_CONTROL)
@ -184,6 +189,40 @@ CUSBQueue::AddUSBRequest(
//
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 @@ CUSBQueue::LinkQueueHead(
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 @@ CUSBQueue::CreateUSBRequest(
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)

View file

@ -46,7 +46,8 @@ public:
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 @@ CUSBRequest::BuildTransferDescriptorChain(
//
// FIXME FIXME FIXME FIXME FIXME
//
MaxPacketSize = 1280;
MaxPacketSize = 64; //1280;
do
{
@ -857,7 +858,14 @@ CUSBRequest::BuildTransferDescriptorChain(
//
// 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 @@ CUSBRequest::BuildControlTransferDescriptor(
Status = BuildTransferDescriptorChain(MmGetMdlVirtualAddress(m_TransferBufferMDL),
m_TransferBufferLength,
Direction ? TD_TOKEN_IN : TD_TOKEN_OUT,
FALSE,
TRUE,
&FirstDescriptor,
&LastDescriptor,
&ChainDescriptorLength,
@ -1131,6 +1139,173 @@ CUSBRequest::GetDeviceSpeed()
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(