Author: janderwald
Date: Fri Mar 2 14:21:44 2012
New Revision: 55957
URL:
http://svn.reactos.org/svn/reactos?rev=55957&view=rev
Log:
[USBEHCI]
- Partly implement support for interrupt transfers, WIP, untested
Modified:
trunk/reactos/drivers/usb/usbehci/hardware.h
trunk/reactos/drivers/usb/usbehci/interfaces.h
trunk/reactos/drivers/usb/usbehci/usb_queue.cpp
trunk/reactos/drivers/usb/usbehci/usb_request.cpp
Modified: trunk/reactos/drivers/usb/usbehci/hardware.h
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/drivers/usb/usbehci/hardwa…
==============================================================================
--- trunk/reactos/drivers/usb/usbehci/hardware.h [iso-8859-1] (original)
+++ trunk/reactos/drivers/usb/usbehci/hardware.h [iso-8859-1] Fri Mar 2 14:21:44 2012
@@ -222,6 +222,7 @@
ULONG PhysicalAddr;
LIST_ENTRY LinkedQueueHeads;
LIST_ENTRY TransferDescriptorListHead;
+ PVOID NextQueueHead;
PVOID Request;
} QUEUE_HEAD, *PQUEUE_HEAD;
@@ -309,4 +310,19 @@
ULONG PortChange;
}EHCI_PORT_STATUS;
-
+#define EHCI_INTERRUPT_ENTRIES_COUNT (10 + 1)
+#define EHCI_VFRAMELIST_ENTRIES_COUNT 128
+#define EHCI_FRAMELIST_ENTRIES_COUNT 1024
+
+#define MAX_AVAILABLE_BANDWIDTH 125 // Microseconds
+
+#define EHCI_QH_CAPS_MULT_SHIFT 30 // Transactions per Micro-Frame
+#define EHCI_QH_CAPS_MULT_MASK 0x03
+#define EHCI_QH_CAPS_PORT_SHIFT 23 // Hub Port (Split-Transaction)
+#define EHCI_QH_CAPS_PORT_MASK 0x7f
+#define EHCI_QH_CAPS_HUB_SHIFT 16 // Hub Address (Split-Transaction)
+#define EHCI_QH_CAPS_HUB_MASK 0x7f
+#define EHCI_QH_CAPS_SCM_SHIFT 8 // Split Completion Mask
+#define EHCI_QH_CAPS_SCM_MASK 0xff
+#define EHCI_QH_CAPS_ISM_SHIFT 0 // Interrupt Schedule Mask
+#define EHCI_QH_CAPS_ISM_MASK 0xff
Modified: trunk/reactos/drivers/usb/usbehci/interfaces.h
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/drivers/usb/usbehci/interf…
==============================================================================
--- trunk/reactos/drivers/usb/usbehci/interfaces.h [iso-8859-1] (original)
+++ trunk/reactos/drivers/usb/usbehci/interfaces.h [iso-8859-1] Fri Mar 2 14:21:44 2012
@@ -86,8 +86,11 @@
IN struct _QUEUE_HEAD * QueueHead) PURE; \
\
STDMETHOD_(BOOLEAN, IsQueueHeadComplete)( THIS_ \
- IN struct _QUEUE_HEAD * QueueHead) PURE;
-
+ IN struct _QUEUE_HEAD * QueueHead) PURE; \
+ \
+ STDMETHOD_(USB_DEVICE_SPEED, GetSpeed)( THIS) PURE; \
+ \
+ STDMETHOD_(UCHAR, GetInterval)( THIS) PURE;
#define IMP_IEHCIREQUEST \
STDMETHODIMP_(VOID) CompletionCallback( \
@@ -103,8 +106,11 @@
STDMETHODIMP_(VOID) FreeQueueHead(struct _QUEUE_HEAD * QueueHead); \
\
STDMETHODIMP_(BOOLEAN) IsQueueHeadComplete( \
- IN struct _QUEUE_HEAD * QueueHead);
-
+ IN struct _QUEUE_HEAD * QueueHead); \
+ \
+ STDMETHODIMP_(USB_DEVICE_SPEED) GetSpeed( THIS); \
+ \
+ STDMETHODIMP_(UCHAR) GetInterval( THIS);
DECLARE_INTERFACE_(IEHCIRequest, IUSBRequest)
{
Modified: trunk/reactos/drivers/usb/usbehci/usb_queue.cpp
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/drivers/usb/usbehci/usb_qu…
==============================================================================
--- trunk/reactos/drivers/usb/usbehci/usb_queue.cpp [iso-8859-1] (original)
+++ trunk/reactos/drivers/usb/usbehci/usb_queue.cpp [iso-8859-1] Fri Mar 2 14:21:44 2012
@@ -55,7 +55,6 @@
ULONG m_MaxPollingInterval;
// max polling interval
PHYSICAL_ADDRESS m_SyncFrameListAddr;
// physical address of sync frame list
PULONG m_SyncFrameList;
// virtual address of sync frame list
- PQUEUE_HEAD * m_SyncFrameListQueueHeads;
// stores the frame list of queue head
// queue head manipulation functions
VOID LinkQueueHead(PQUEUE_HEAD HeadQueueHead, PQUEUE_HEAD NewQueueHead);
@@ -66,6 +65,9 @@
// processes the async list
VOID ProcessAsyncList(IN NTSTATUS Status, OUT PULONG ShouldRingDoorBell);
+ // processes the async list
+ VOID ProcessPeriodicSchedule(IN NTSTATUS Status, OUT PULONG ShouldRingDoorBell);
+
// called for each completed queue head
VOID QueueHeadCompletion(PQUEUE_HEAD QueueHead, NTSTATUS Status);
@@ -74,6 +76,14 @@
// intializes the sync schedule
NTSTATUS InitializeSyncSchedule(IN PEHCIHARDWAREDEVICE Hardware, IN PDMAMEMORYMANAGER
MemManager);
+
+ // links interrupt queue head
+ VOID LinkInterruptQueueHead(PQUEUE_HEAD QueueHead);
+
+ // interrupt queue heads
+ PQUEUE_HEAD m_InterruptQueueHeads[EHCI_INTERRUPT_ENTRIES_COUNT];
+
+
};
//=================================================================================================
@@ -156,7 +166,7 @@
{
PHYSICAL_ADDRESS QueueHeadPhysAddr;
NTSTATUS Status;
- ULONG Index;
+ ULONG Index, Interval, IntervalIndex;
PQUEUE_HEAD QueueHead;
//
@@ -165,23 +175,10 @@
m_MaxPeriodicListEntries = 1024;
//
- // use polling scheme of 32ms
- //
- m_MaxPollingInterval = 32;
-
- //
- // allocate dummy frame list array
- //
- m_SyncFrameListQueueHeads = (PQUEUE_HEAD*)ExAllocatePool(NonPagedPool,
m_MaxPollingInterval * sizeof(PQUEUE_HEAD));
- if (!m_SyncFrameListQueueHeads)
- {
- //
- // no memory
- //
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
-
+ // use polling scheme of 512ms
+ //
+ m_MaxPollingInterval = 512;
+
//
// first allocate a page to hold the queue array
//
@@ -192,77 +189,52 @@
// failed to allocate sync frame list array
//
DPRINT1("Failed to allocate sync frame list\n");
- ExFreePool(m_SyncFrameListQueueHeads);
- //ASSERT(FALSE);
return STATUS_INSUFFICIENT_RESOURCES;
}
- //
- // now allocate queue head descriptors for the polling interval
- //
- for(Index = 0; Index < m_MaxPeriodicListEntries; Index++)
- {
- //
- // check if is inside our polling interrupt frequency window
- //
- if (Index < m_MaxPollingInterval)
+ for(Index = 0; Index < EHCI_INTERRUPT_ENTRIES_COUNT; Index++)
+ {
+ //
+ // allocate queue head
+ //
+ Status = MemManager->Allocate(sizeof(QUEUE_HEAD), (PVOID*)&QueueHead,
&QueueHeadPhysAddr);
+
+ //
+ // initialize queue head
+ //
+ QueueHead->HorizontalLinkPointer = TERMINATE_POINTER;
+ QueueHead->AlternateNextPointer = TERMINATE_POINTER;
+ QueueHead->NextPointer = TERMINATE_POINTER;
+ QueueHead->EndPointCharacteristics.MaximumPacketLength = 64;
+ QueueHead->EndPointCharacteristics.NakCountReload = 0x3;
+ QueueHead->EndPointCharacteristics.EndPointSpeed = QH_ENDPOINT_HIGHSPEED;
+ QueueHead->EndPointCapabilities.NumberOfTransactionPerFrame = 0x01;
+ QueueHead->PhysicalAddr = QueueHeadPhysAddr.LowPart;
+ QueueHead->Token.Bits.Halted = TRUE; //FIXME
+ m_InterruptQueueHeads[Index]= QueueHead;
+
+ if (Index > 0)
{
- //
- // allocate queue head
- //
- Status = MemManager->Allocate(sizeof(QUEUE_HEAD), (PVOID*)&QueueHead,
&QueueHeadPhysAddr);
-
- //
- // initialize queue head
- //
- QueueHead->HorizontalLinkPointer = TERMINATE_POINTER;
- QueueHead->AlternateNextPointer = TERMINATE_POINTER;
- QueueHead->NextPointer = TERMINATE_POINTER;
-
- //
- // 1 for non high speed, 0 for high speed device
- //
- QueueHead->EndPointCharacteristics.ControlEndPointFlag = 0;
- QueueHead->EndPointCharacteristics.HeadOfReclamation = FALSE;
- QueueHead->EndPointCharacteristics.MaximumPacketLength = 64;
-
- //
- // Set NakCountReload to max value possible
- //
- QueueHead->EndPointCharacteristics.NakCountReload = 0xF;
-
- //
- // Get the Initial Data Toggle from the QEDT
- //
- QueueHead->EndPointCharacteristics.QEDTDataToggleControl = FALSE;
-
- //
- // FIXME: check if High Speed Device
- //
- QueueHead->EndPointCharacteristics.EndPointSpeed = QH_ENDPOINT_HIGHSPEED;
- QueueHead->EndPointCapabilities.NumberOfTransactionPerFrame = 0x03;
- QueueHead->Token.DWord = 0;
- QueueHead->Token.Bits.InterruptOnComplete = FALSE;
- QueueHead->PhysicalAddr = QueueHeadPhysAddr.LowPart;
-
-
- //
- // store in queue head array
- //
- m_SyncFrameListQueueHeads[Index] = QueueHead;
+ // link all to the first queue head
+ QueueHead->HorizontalLinkPointer =
m_InterruptQueueHeads[0]->PhysicalAddr | QH_TYPE_QH;
+ QueueHead->NextQueueHead = m_InterruptQueueHeads[0];
}
- else
+ }
+
+ //
+ // build interrupt tree
+ //
+ Interval = EHCI_FRAMELIST_ENTRIES_COUNT;
+ IntervalIndex = EHCI_INTERRUPT_ENTRIES_COUNT - 1;
+ while (Interval > 1)
+ {
+ for (Index = Interval / 2; Index < EHCI_FRAMELIST_ENTRIES_COUNT; Index +=
Interval)
{
- //
- // get cached entry
- //
- QueueHead = m_SyncFrameListQueueHeads[m_MaxPeriodicListEntries %
m_MaxPollingInterval];
+ DPRINT("Index %lu IntervalIndex %lu\n", Index, IntervalIndex);
+ m_SyncFrameList[Index] =
m_InterruptQueueHeads[IntervalIndex]->PhysicalAddr | QH_TYPE_QH;
}
-
- //
- // store entry
- //
- m_SyncFrameList[Index] = (QueueHead->PhysicalAddr | 0x2);
+ IntervalIndex--;
+ Interval /= 2;
}
//
@@ -293,21 +265,17 @@
// get internal req
Request = PEHCIREQUEST(Req);
- //
// get request type
- //
Type = Request->GetTransferType();
- //
// check if supported
- //
switch(Type)
{
case USB_ENDPOINT_TYPE_ISOCHRONOUS:
- case USB_ENDPOINT_TYPE_INTERRUPT:
/* NOT IMPLEMENTED IN QUEUE */
Status = STATUS_NOT_SUPPORTED;
break;
+ case USB_ENDPOINT_TYPE_INTERRUPT:
case USB_ENDPOINT_TYPE_BULK:
case USB_ENDPOINT_TYPE_CONTROL:
Status = STATUS_SUCCESS;
@@ -318,53 +286,45 @@
Status = STATUS_NOT_SUPPORTED;
}
- //
// check for success
- //
if (!NT_SUCCESS(Status))
{
- //
// request not supported, please try later
- //
return Status;
}
+ // get queue head
+ Status = Request->GetQueueHead(&QueueHead);
+
+ // check for success
+ if (!NT_SUCCESS(Status))
+ {
+ // failed to get queue head
+ return Status;
+ }
+
+ // acquire lock
+ KeAcquireSpinLock(m_Lock, &OldLevel);
+
if (Type == USB_ENDPOINT_TYPE_BULK || Type == USB_ENDPOINT_TYPE_CONTROL)
{
- //
- // get queue head
- //
- Status = Request->GetQueueHead(&QueueHead);
-
- //
- // check for success
- //
- if (!NT_SUCCESS(Status))
- {
- //
- // failed to get queue head
- //
- return Status;
- }
-
- DPRINT("Request %p QueueHead %p inserted into AsyncQueue\n", Request,
QueueHead);
-
- //
- // Add it to the pending list
- //
- KeAcquireSpinLock(m_Lock, &OldLevel);
+ // Add to list
LinkQueueHead(AsyncListQueueHead, QueueHead);
- KeReleaseSpinLock(m_Lock, OldLevel);
-
- }
-
-
- //
+ }
+ else if (Type == USB_ENDPOINT_TYPE_INTERRUPT)
+ {
+ // get interval
+ LinkInterruptQueueHead(QueueHead);
+ }
+
+ // release lock
+ KeReleaseSpinLock(m_Lock, OldLevel);
+
+
// add extra reference which is released when the request is completed
- //
Request->AddRef();
-
+ // done
return STATUS_SUCCESS;
}
@@ -385,6 +345,79 @@
}
return Status;
+}
+
+VOID
+CUSBQueue::LinkInterruptQueueHead(
+ PQUEUE_HEAD QueueHead)
+{
+ PEHCIREQUEST Request;
+ UCHAR Interval, IntervalIndex;
+ USB_DEVICE_SPEED DeviceSpeed;
+ PQUEUE_HEAD InterruptQueueHead;
+
+ // get internal req
+ Request = PEHCIREQUEST(QueueHead->Request);
+ ASSERT(Request);
+
+ // get interval
+ Interval = Request->GetInterval();
+
+ // get device speed
+ DeviceSpeed = Request->GetSpeed();
+ if (DeviceSpeed == UsbHighSpeed)
+ {
+ // interrupt queue head can be scheduled on each possible micro frame
+ QueueHead->EndPointCapabilities.InterruptScheduleMask = 0xFF;
+ }
+ else
+ {
+ // As we do not yet support FSTNs to correctly reference low/full
+ // speed interrupt transfers, we simply put them into the 1 interval
+ // queue. This way we ensure that we reach them on every micro frame
+ // and can do the corresponding start/complete split transactions.
+ // ToDo: use FSTNs to correctly link non high speed interrupt transfers
+ Interval = 1;
+
+ // For now we also force start splits to be in micro frame 0 and
+ // complete splits to be in micro frame 2, 3 and 4.
+ QueueHead->EndPointCapabilities.InterruptScheduleMask = 0x01;
+ QueueHead->EndPointCapabilities.SplitCompletionMask = 0x1C;
+ }
+
+ // sanitize interrupt interval
+ Interval = max(1, Interval);
+
+ if (Interval == 1)
+ IntervalIndex = 1;
+ else if (Interval == 2)
+ IntervalIndex = 2;
+ else if (Interval <= 4)
+ IntervalIndex = 3;
+ else if (Interval <= 8)
+ IntervalIndex = 4;
+ else if (Interval <= 16)
+ IntervalIndex = 5;
+ else if (Interval <= 32)
+ IntervalIndex = 6;
+ else if (Interval <= 64)
+ IntervalIndex = 7;
+ else if (Interval <= 128)
+ IntervalIndex = 8;
+ else if (Interval <= 256)
+ IntervalIndex = 9;
+ else
+ IntervalIndex = 10;
+
+ // get interrupt queue head
+ InterruptQueueHead = m_InterruptQueueHeads[IntervalIndex];
+
+ // link queue head
+ QueueHead->HorizontalLinkPointer = InterruptQueueHead->HorizontalLinkPointer;
+ QueueHead->NextQueueHead = InterruptQueueHead->NextQueueHead;
+
+ InterruptQueueHead->HorizontalLinkPointer = QueueHead->PhysicalAddr |
QH_TYPE_QH;
+ InterruptQueueHead->NextQueueHead = QueueHead;
}
//
@@ -583,6 +616,16 @@
InsertTailList(&m_CompletedRequestAsyncList,
&CurrentQH->LinkedQueueHeads);
}
+
+VOID
+CUSBQueue::ProcessPeriodicSchedule(
+ IN NTSTATUS Status,
+ OUT PULONG ShouldRingDoorBell)
+{
+
+
+}
+
VOID
CUSBQueue::ProcessAsyncList(
IN NTSTATUS Status,
@@ -665,18 +708,18 @@
IN NTSTATUS Status,
OUT PULONG ShouldRingDoorBell)
{
-
DPRINT("CUSBQueue::InterruptCallback\n");
+
+ //
+ // process periodic schedule
+ //
+ ProcessPeriodicSchedule(Status, ShouldRingDoorBell);
//
// iterate asynchronous list
//
*ShouldRingDoorBell = FALSE;
ProcessAsyncList(Status, ShouldRingDoorBell);
-
- //
- // TODO: implement periodic schedule processing
- //
}
VOID
Modified: trunk/reactos/drivers/usb/usbehci/usb_request.cpp
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/drivers/usb/usbehci/usb_re…
==============================================================================
--- trunk/reactos/drivers/usb/usbehci/usb_request.cpp [iso-8859-1] (original)
+++ trunk/reactos/drivers/usb/usbehci/usb_request.cpp [iso-8859-1] Fri Mar 2 14:21:44
2012
@@ -44,7 +44,7 @@
ULONG InternalGetTransferType();
UCHAR InternalGetPidDirection();
NTSTATUS BuildControlTransferQueueHead(PQUEUE_HEAD * OutHead);
- NTSTATUS BuildBulkTransferQueueHead(PQUEUE_HEAD * OutHead);
+ NTSTATUS BuildBulkInterruptTransferQueueHead(PQUEUE_HEAD * OutHead);
NTSTATUS STDMETHODCALLTYPE CreateDescriptor(PQUEUE_TRANSFER_DESCRIPTOR
*OutDescriptor);
NTSTATUS CreateQueueHead(PQUEUE_HEAD *OutQueueHead);
UCHAR STDMETHODCALLTYPE GetDeviceAddress();
@@ -129,7 +129,11 @@
NTSTATUS m_NtStatusCode;
ULONG m_UrbStatusCode;
+ // buffer base address
PVOID m_Base;
+
+ // device speed
+ USB_DEVICE_SPEED m_Speed;
};
@@ -168,6 +172,7 @@
m_TransferBufferLength = TransferBufferLength;
m_TransferBufferMDL = TransferBuffer;
m_DeviceAddress = Device->GetDeviceAddress();
+ m_Speed = Device->GetSpeed();
m_EndpointDescriptor = EndpointDescriptor;
m_TotalBytesTransferred = 0;
@@ -217,6 +222,7 @@
m_DmaManager = DmaManager;
m_TotalBytesTransferred = 0;
+ m_Speed = Device->GetSpeed();
//
// get current irp stack location
@@ -442,12 +448,9 @@
case USB_ENDPOINT_TYPE_CONTROL:
Status = BuildControlTransferQueueHead(OutHead);
break;
+ case USB_ENDPOINT_TYPE_INTERRUPT:
case USB_ENDPOINT_TYPE_BULK:
- Status = BuildBulkTransferQueueHead(OutHead);
- break;
- case USB_ENDPOINT_TYPE_INTERRUPT:
- DPRINT1("USB_ENDPOINT_TYPE_INTERRUPT not implemented\n");
- Status = STATUS_NOT_IMPLEMENTED;
+ Status = BuildBulkInterruptTransferQueueHead(OutHead);
break;
case USB_ENDPOINT_TYPE_ISOCHRONOUS:
DPRINT1("USB_ENDPOINT_TYPE_ISOCHRONOUS not implemented\n");
@@ -1059,7 +1062,7 @@
//----------------------------------------------------------------------------------------
NTSTATUS
-CUSBRequest::BuildBulkTransferQueueHead(
+CUSBRequest::BuildBulkInterruptTransferQueueHead(
PQUEUE_HEAD * OutHead)
{
NTSTATUS Status;
@@ -1738,6 +1741,21 @@
return m_TransferBufferLength;
}
+USB_DEVICE_SPEED
+CUSBRequest::GetSpeed()
+{
+ return m_Speed;
+}
+
+UCHAR
+CUSBRequest::GetInterval()
+{
+ if (!m_EndpointDescriptor)
+ return 0;
+
+ return m_EndpointDescriptor->EndPointDescriptor.bInterval;
+}
+
//-----------------------------------------------------------------------------------------
NTSTATUS
NTAPI