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/hardwar... ============================================================================== --- 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/interfa... ============================================================================== --- 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_que... ============================================================================== --- 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_req... ============================================================================== --- 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