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/hardware.h?rev=55957&r1=55956&r2=55957&view=diff
==============================================================================
--- 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/interfaces.h?rev=55957&r1=55956&r2=55957&view=diff
==============================================================================
--- 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_queue.cpp?rev=55957&r1=55956&r2=55957&view=diff
==============================================================================
--- 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_request.cpp?rev=55957&r1=55956&r2=55957&view=diff
==============================================================================
--- 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


Reply via email to