mirror of
https://github.com/reactos/reactos.git
synced 2024-11-02 12:53:33 +00:00
1023 lines
26 KiB
C++
1023 lines
26 KiB
C++
/*
|
|
* PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface
|
|
* LICENSE: GPL - See COPYING in the top level directory
|
|
* FILE: drivers/usb/usbohci/usb_queue.cpp
|
|
* PURPOSE: USB OHCI device driver.
|
|
* PROGRAMMERS:
|
|
* Michael Martin (michael.martin@reactos.org)
|
|
* Johannes Anderwald (johannes.anderwald@reactos.org)
|
|
*/
|
|
|
|
#include "usbohci.h"
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
class CUSBQueue : public IOHCIQueue
|
|
{
|
|
public:
|
|
STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface);
|
|
|
|
STDMETHODIMP_(ULONG) AddRef()
|
|
{
|
|
InterlockedIncrement(&m_Ref);
|
|
return m_Ref;
|
|
}
|
|
STDMETHODIMP_(ULONG) Release()
|
|
{
|
|
InterlockedDecrement(&m_Ref);
|
|
|
|
if (!m_Ref)
|
|
{
|
|
delete this;
|
|
return 0;
|
|
}
|
|
return m_Ref;
|
|
}
|
|
|
|
// com
|
|
IMP_IUSBQUEUE
|
|
IMP_IUSBOHCIQUEUE
|
|
|
|
// local functions
|
|
BOOLEAN IsTransferDescriptorInEndpoint(IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor, IN ULONG TransferDescriptorLogicalAddress);
|
|
BOOLEAN IsTransferDescriptorInIsoEndpoint(IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor, IN ULONG TransferDescriptorLogicalAddress);
|
|
NTSTATUS FindTransferDescriptorInEndpoint(IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor, IN ULONG TransferDescriptorLogicalAddress, OUT POHCI_ENDPOINT_DESCRIPTOR *OutEndpointDescriptor, OUT POHCI_ENDPOINT_DESCRIPTOR *OutPreviousEndpointDescriptor);
|
|
NTSTATUS FindTransferDescriptorInInterruptHeadEndpoints(IN ULONG TransferDescriptorLogicalAddress, OUT POHCI_ENDPOINT_DESCRIPTOR *OutEndpointDescriptor, OUT POHCI_ENDPOINT_DESCRIPTOR *OutPreviousEndpointDescriptor);
|
|
NTSTATUS FindTransferDescriptorInIsochronousHeadEndpoints(IN ULONG TransferDescriptorLogicalAddress, OUT POHCI_ENDPOINT_DESCRIPTOR *OutEndpointDescriptor, OUT POHCI_ENDPOINT_DESCRIPTOR *OutPreviousEndpointDescriptor);
|
|
|
|
VOID CleanupEndpointDescriptor(POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor, POHCI_ENDPOINT_DESCRIPTOR PreviousEndpointDescriptor);
|
|
POHCI_ENDPOINT_DESCRIPTOR FindInterruptEndpointDescriptor(UCHAR InterruptInterval);
|
|
VOID PrintEndpointList(POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor);
|
|
VOID LinkEndpoint(POHCI_ENDPOINT_DESCRIPTOR HeadEndpointDescriptor, POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor);
|
|
VOID AddEndpointDescriptor(IN POHCI_ENDPOINT_DESCRIPTOR Descriptor);
|
|
|
|
|
|
// constructor / destructor
|
|
CUSBQueue(IUnknown *OuterUnknown){}
|
|
virtual ~CUSBQueue(){}
|
|
|
|
protected:
|
|
LONG m_Ref; // reference count
|
|
KSPIN_LOCK m_Lock; // list lock
|
|
POHCIHARDWAREDEVICE m_Hardware; // hardware
|
|
POHCI_ENDPOINT_DESCRIPTOR m_BulkHeadEndpointDescriptor; // bulk head descriptor
|
|
POHCI_ENDPOINT_DESCRIPTOR m_ControlHeadEndpointDescriptor; // control head descriptor
|
|
POHCI_ENDPOINT_DESCRIPTOR m_IsoHeadEndpointDescriptor; // isochronous head descriptor
|
|
POHCI_ENDPOINT_DESCRIPTOR * m_InterruptEndpoints;
|
|
LIST_ENTRY m_PendingRequestList; // pending request list
|
|
};
|
|
|
|
//=================================================================================================
|
|
// COM
|
|
//
|
|
NTSTATUS
|
|
STDMETHODCALLTYPE
|
|
CUSBQueue::QueryInterface(
|
|
IN REFIID refiid,
|
|
OUT PVOID* Output)
|
|
{
|
|
if (IsEqualGUIDAligned(refiid, IID_IUnknown))
|
|
{
|
|
*Output = PVOID(PUNKNOWN(this));
|
|
PUNKNOWN(*Output)->AddRef();
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
NTSTATUS
|
|
STDMETHODCALLTYPE
|
|
CUSBQueue::Initialize(
|
|
IN PUSBHARDWAREDEVICE Hardware,
|
|
IN PDMA_ADAPTER AdapterObject,
|
|
IN PDMAMEMORYMANAGER MemManager,
|
|
IN OPTIONAL PKSPIN_LOCK Lock)
|
|
{
|
|
if (!Hardware)
|
|
{
|
|
// WTF
|
|
DPRINT1("[USBOHCI] Failed to initialize queue\n");
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// store hardware
|
|
//
|
|
m_Hardware = POHCIHARDWAREDEVICE(Hardware);
|
|
ASSERT(m_Hardware);
|
|
|
|
//
|
|
// get bulk endpoint descriptor
|
|
//
|
|
m_Hardware->GetBulkHeadEndpointDescriptor(&m_BulkHeadEndpointDescriptor);
|
|
|
|
//
|
|
// get control endpoint descriptor
|
|
//
|
|
m_Hardware->GetControlHeadEndpointDescriptor(&m_ControlHeadEndpointDescriptor);
|
|
|
|
//
|
|
// get isochronous endpoint
|
|
//
|
|
m_Hardware->GetIsochronousHeadEndpointDescriptor(&m_IsoHeadEndpointDescriptor);
|
|
|
|
//
|
|
// get interrupt endpoints
|
|
//
|
|
m_Hardware->GetInterruptEndpointDescriptors(&m_InterruptEndpoints);
|
|
|
|
//
|
|
// initialize spinlock
|
|
//
|
|
KeInitializeSpinLock(&m_Lock);
|
|
|
|
//
|
|
// init list
|
|
//
|
|
InitializeListHead(&m_PendingRequestList);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
CUSBQueue::LinkEndpoint(
|
|
POHCI_ENDPOINT_DESCRIPTOR HeadEndpointDescriptor,
|
|
POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor)
|
|
{
|
|
POHCI_ENDPOINT_DESCRIPTOR CurrentEndpointDescriptor = HeadEndpointDescriptor;
|
|
|
|
//
|
|
// get last descriptor in queue
|
|
//
|
|
while(CurrentEndpointDescriptor->NextDescriptor)
|
|
{
|
|
//
|
|
// move to last descriptor
|
|
//
|
|
CurrentEndpointDescriptor = (POHCI_ENDPOINT_DESCRIPTOR)CurrentEndpointDescriptor->NextDescriptor;
|
|
}
|
|
|
|
//
|
|
// link endpoints
|
|
//
|
|
CurrentEndpointDescriptor->NextPhysicalEndpoint = EndpointDescriptor->PhysicalAddress.LowPart;
|
|
CurrentEndpointDescriptor->NextDescriptor = EndpointDescriptor;
|
|
|
|
}
|
|
|
|
VOID
|
|
CUSBQueue::AddEndpointDescriptor(
|
|
IN POHCI_ENDPOINT_DESCRIPTOR Descriptor)
|
|
{
|
|
IOHCIRequest *Request;
|
|
ULONG Type;
|
|
POHCI_ENDPOINT_DESCRIPTOR HeadDescriptor;
|
|
POHCI_ISO_TD CurrentDescriptor;
|
|
ULONG FrameNumber;
|
|
USHORT Frame;
|
|
|
|
|
|
//
|
|
// sanity check
|
|
//
|
|
ASSERT(Descriptor->Request);
|
|
Request = (IOHCIRequest*)Descriptor->Request;
|
|
|
|
//
|
|
// get request type
|
|
//
|
|
Type = Request->GetTransferType();
|
|
|
|
//
|
|
// check type
|
|
//
|
|
if (Type == USB_ENDPOINT_TYPE_BULK)
|
|
{
|
|
//
|
|
// get head descriptor
|
|
//
|
|
HeadDescriptor = m_BulkHeadEndpointDescriptor;
|
|
}
|
|
else if (Type == USB_ENDPOINT_TYPE_CONTROL)
|
|
{
|
|
//
|
|
// get head descriptor
|
|
//
|
|
HeadDescriptor = m_ControlHeadEndpointDescriptor;
|
|
}
|
|
else if (Type == USB_ENDPOINT_TYPE_INTERRUPT)
|
|
{
|
|
//
|
|
// get head descriptor
|
|
//
|
|
HeadDescriptor = FindInterruptEndpointDescriptor(Request->GetInterval());
|
|
ASSERT(HeadDescriptor);
|
|
}
|
|
else if (Type == USB_ENDPOINT_TYPE_ISOCHRONOUS)
|
|
{
|
|
//
|
|
// get head descriptor
|
|
//
|
|
HeadDescriptor = m_IsoHeadEndpointDescriptor;
|
|
|
|
//
|
|
// get current frame number
|
|
//
|
|
m_Hardware->GetCurrentFrameNumber(&FrameNumber);
|
|
|
|
//
|
|
// FIXME: increment frame number
|
|
//
|
|
FrameNumber += 300;
|
|
|
|
//
|
|
// apply frame number to iso transfer descriptors
|
|
//
|
|
CurrentDescriptor = (POHCI_ISO_TD)Descriptor->HeadLogicalDescriptor;
|
|
|
|
DPRINT("ISO: NextFrameNumber %x\n", FrameNumber);
|
|
Frame = (FrameNumber & 0xFFFF);
|
|
|
|
while(CurrentDescriptor)
|
|
{
|
|
//
|
|
// set current frame number
|
|
//
|
|
CurrentDescriptor->Flags |= OHCI_ITD_SET_STARTING_FRAME(Frame);
|
|
|
|
//
|
|
// move to next frame number
|
|
//
|
|
Frame += OHCI_ITD_GET_FRAME_COUNT(CurrentDescriptor->Flags);
|
|
|
|
//
|
|
// move to next descriptor
|
|
//
|
|
CurrentDescriptor = CurrentDescriptor->NextLogicalDescriptor;
|
|
}
|
|
|
|
//
|
|
// get current frame number
|
|
//
|
|
m_Hardware->GetCurrentFrameNumber(&FrameNumber);
|
|
|
|
DPRINT("Hardware 1ms %p Iso %p\n",m_InterruptEndpoints[0], m_IsoHeadEndpointDescriptor);
|
|
ASSERT(m_InterruptEndpoints[0]->NextPhysicalEndpoint == m_IsoHeadEndpointDescriptor->PhysicalAddress.LowPart);
|
|
|
|
PrintEndpointList(m_IsoHeadEndpointDescriptor);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// bad request type
|
|
//
|
|
ASSERT(FALSE);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// set descriptor active
|
|
//
|
|
Descriptor->Flags &= ~OHCI_ENDPOINT_SKIP;
|
|
|
|
//
|
|
// insert endpoint at end
|
|
//
|
|
LinkEndpoint(HeadDescriptor, Descriptor);
|
|
|
|
if (Type == USB_ENDPOINT_TYPE_CONTROL || Type == USB_ENDPOINT_TYPE_BULK)
|
|
{
|
|
//
|
|
// notify hardware of our request
|
|
//
|
|
m_Hardware->HeadEndpointDescriptorModified(Type);
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
STDMETHODCALLTYPE
|
|
CUSBQueue::AddUSBRequest(
|
|
IUSBRequest * Req)
|
|
{
|
|
NTSTATUS Status;
|
|
IN POHCI_ENDPOINT_DESCRIPTOR Descriptor;
|
|
POHCIREQUEST Request;
|
|
|
|
DPRINT("CUSBQueue::AddUSBRequest\n");
|
|
|
|
// get request
|
|
Request = POHCIREQUEST(Req);
|
|
|
|
|
|
//
|
|
// sanity check
|
|
//
|
|
ASSERT(Request != NULL);
|
|
|
|
//
|
|
// add extra reference which is released when the request is completed
|
|
//
|
|
Request->AddRef();
|
|
|
|
//
|
|
// get transfer descriptors
|
|
//
|
|
Status = Request->GetEndpointDescriptor(&Descriptor);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// failed to get transfer descriptor
|
|
//
|
|
DPRINT1("CUSBQueue::AddUSBRequest GetEndpointDescriptor failed with %x\n", Status);
|
|
|
|
//
|
|
// release reference
|
|
//
|
|
Request->Release();
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// add the request
|
|
//
|
|
AddEndpointDescriptor(Descriptor);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
STDMETHODCALLTYPE
|
|
CUSBQueue::CreateUSBRequest(
|
|
IUSBRequest **OutRequest)
|
|
{
|
|
PUSBREQUEST UsbRequest;
|
|
NTSTATUS Status;
|
|
|
|
*OutRequest = NULL;
|
|
Status = InternalCreateUSBRequest(&UsbRequest);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
*OutRequest = UsbRequest;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
CUSBQueue::FindTransferDescriptorInEndpoint(
|
|
IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor,
|
|
IN ULONG TransferDescriptorLogicalAddress,
|
|
OUT POHCI_ENDPOINT_DESCRIPTOR *OutEndpointDescriptor,
|
|
OUT POHCI_ENDPOINT_DESCRIPTOR *OutPreviousEndpointDescriptor)
|
|
{
|
|
POHCI_ENDPOINT_DESCRIPTOR LastDescriptor = EndpointDescriptor;
|
|
|
|
|
|
//
|
|
// skip first endpoint head
|
|
//
|
|
EndpointDescriptor = (POHCI_ENDPOINT_DESCRIPTOR)EndpointDescriptor->NextDescriptor;
|
|
|
|
while(EndpointDescriptor)
|
|
{
|
|
//
|
|
// check if the transfer descriptor is inside the list
|
|
//
|
|
if ((EndpointDescriptor->HeadPhysicalDescriptor & OHCI_ENDPOINT_HEAD_MASK) == EndpointDescriptor->TailPhysicalDescriptor || (EndpointDescriptor->HeadPhysicalDescriptor & OHCI_ENDPOINT_HALTED))
|
|
{
|
|
//
|
|
// found endpoint
|
|
//
|
|
*OutEndpointDescriptor = EndpointDescriptor;
|
|
*OutPreviousEndpointDescriptor = LastDescriptor;
|
|
|
|
//
|
|
// done
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// store last endpoint
|
|
//
|
|
LastDescriptor = EndpointDescriptor;
|
|
|
|
//
|
|
// move to next
|
|
//
|
|
EndpointDescriptor = (POHCI_ENDPOINT_DESCRIPTOR)EndpointDescriptor->NextDescriptor;
|
|
}
|
|
|
|
//
|
|
// failed to endpoint
|
|
//
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
NTSTATUS
|
|
CUSBQueue::FindTransferDescriptorInInterruptHeadEndpoints(IN ULONG TransferDescriptorLogicalAddress, OUT POHCI_ENDPOINT_DESCRIPTOR *OutEndpointDescriptor, OUT POHCI_ENDPOINT_DESCRIPTOR *OutPreviousEndpointDescriptor)
|
|
{
|
|
ULONG Index;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// search descriptor in endpoint list
|
|
//
|
|
for(Index = 0; Index < OHCI_STATIC_ENDPOINT_COUNT; Index++)
|
|
{
|
|
//
|
|
// is it in current endpoint
|
|
//
|
|
Status = FindTransferDescriptorInEndpoint(m_InterruptEndpoints[Index], TransferDescriptorLogicalAddress, OutEndpointDescriptor, OutPreviousEndpointDescriptor);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// found transfer descriptor
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// not found
|
|
//
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
NTSTATUS
|
|
CUSBQueue::FindTransferDescriptorInIsochronousHeadEndpoints(
|
|
IN ULONG TransferDescriptorLogicalAddress,
|
|
OUT POHCI_ENDPOINT_DESCRIPTOR *OutEndpointDescriptor,
|
|
OUT POHCI_ENDPOINT_DESCRIPTOR *OutPreviousEndpointDescriptor)
|
|
{
|
|
POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor;
|
|
POHCI_ENDPOINT_DESCRIPTOR LastDescriptor = m_IsoHeadEndpointDescriptor;
|
|
|
|
|
|
//
|
|
// skip first endpoint head
|
|
//
|
|
EndpointDescriptor = (POHCI_ENDPOINT_DESCRIPTOR)m_IsoHeadEndpointDescriptor->NextDescriptor;
|
|
|
|
while(EndpointDescriptor)
|
|
{
|
|
//
|
|
// check if the transfer descriptor is inside the list
|
|
//
|
|
if (IsTransferDescriptorInIsoEndpoint(EndpointDescriptor, TransferDescriptorLogicalAddress))
|
|
{
|
|
//
|
|
// found endpoint
|
|
//
|
|
*OutEndpointDescriptor = EndpointDescriptor;
|
|
*OutPreviousEndpointDescriptor = LastDescriptor;
|
|
|
|
//
|
|
// done
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// store last endpoint
|
|
//
|
|
LastDescriptor = EndpointDescriptor;
|
|
|
|
//
|
|
// move to next
|
|
//
|
|
EndpointDescriptor = (POHCI_ENDPOINT_DESCRIPTOR)EndpointDescriptor->NextDescriptor;
|
|
}
|
|
|
|
//
|
|
// failed to endpoint
|
|
//
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
BOOLEAN
|
|
CUSBQueue::IsTransferDescriptorInIsoEndpoint(
|
|
IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor,
|
|
IN ULONG TransferDescriptorLogicalAddress)
|
|
{
|
|
POHCI_ISO_TD Descriptor;
|
|
|
|
//
|
|
// get first general transfer descriptor
|
|
//
|
|
Descriptor = (POHCI_ISO_TD)EndpointDescriptor->HeadLogicalDescriptor;
|
|
|
|
//
|
|
// sanity check
|
|
//
|
|
ASSERT(Descriptor);
|
|
|
|
do
|
|
{
|
|
if (Descriptor->PhysicalAddress.LowPart == TransferDescriptorLogicalAddress)
|
|
{
|
|
//
|
|
// found descriptor
|
|
//
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// move to next
|
|
//
|
|
Descriptor = (POHCI_ISO_TD)Descriptor->NextLogicalDescriptor;
|
|
}while(Descriptor);
|
|
|
|
//
|
|
// no descriptor found
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
CUSBQueue::IsTransferDescriptorInEndpoint(
|
|
IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor,
|
|
IN ULONG TransferDescriptorLogicalAddress)
|
|
{
|
|
POHCI_GENERAL_TD Descriptor;
|
|
|
|
//
|
|
// get first general transfer descriptor
|
|
//
|
|
Descriptor = (POHCI_GENERAL_TD)EndpointDescriptor->HeadLogicalDescriptor;
|
|
|
|
//
|
|
// sanity check
|
|
//
|
|
ASSERT(Descriptor);
|
|
|
|
do
|
|
{
|
|
if (Descriptor->PhysicalAddress.LowPart == TransferDescriptorLogicalAddress)
|
|
{
|
|
//
|
|
// found descriptor
|
|
//
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// move to next
|
|
//
|
|
Descriptor = (POHCI_GENERAL_TD)Descriptor->NextLogicalDescriptor;
|
|
}while(Descriptor);
|
|
|
|
|
|
//
|
|
// no descriptor found
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
VOID
|
|
CUSBQueue::CleanupEndpointDescriptor(
|
|
POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor,
|
|
POHCI_ENDPOINT_DESCRIPTOR PreviousEndpointDescriptor)
|
|
{
|
|
POHCIREQUEST Request;
|
|
POHCI_ENDPOINT_DESCRIPTOR NewEndpointDescriptor;
|
|
USBD_STATUS UrbStatus;
|
|
KIRQL OldLevel;
|
|
|
|
//
|
|
// FIXME: verify unlinking process
|
|
//
|
|
PreviousEndpointDescriptor->NextDescriptor = EndpointDescriptor->NextDescriptor;
|
|
PreviousEndpointDescriptor->NextPhysicalEndpoint = EndpointDescriptor->NextPhysicalEndpoint;
|
|
|
|
//
|
|
// get corresponding request
|
|
//
|
|
Request = POHCIREQUEST(EndpointDescriptor->Request);
|
|
ASSERT(Request);
|
|
|
|
//
|
|
// check for errors
|
|
//
|
|
if (EndpointDescriptor->HeadPhysicalDescriptor & OHCI_ENDPOINT_HALTED)
|
|
{
|
|
//
|
|
// the real error will processed by IUSBRequest
|
|
//
|
|
UrbStatus = USBD_STATUS_STALL_PID;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// well done ;)
|
|
//
|
|
UrbStatus = USBD_STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Check if the transfer was completed and if UrbStatus is ok
|
|
//
|
|
if ((Request->IsRequestComplete() == FALSE) && (UrbStatus == USBD_STATUS_SUCCESS))
|
|
{
|
|
//
|
|
// request is incomplete, get new queue head
|
|
//
|
|
if (Request->GetEndpointDescriptor(&NewEndpointDescriptor) == STATUS_SUCCESS)
|
|
{
|
|
//
|
|
// notify of completion
|
|
//
|
|
Request->FreeEndpointDescriptor(EndpointDescriptor);
|
|
|
|
//
|
|
// first acquire request lock
|
|
//
|
|
KeAcquireSpinLock(&m_Lock, &OldLevel);
|
|
|
|
//
|
|
// add to pending list
|
|
//
|
|
InsertTailList(&m_PendingRequestList, &NewEndpointDescriptor->DescriptorListEntry);
|
|
|
|
//
|
|
// release queue head
|
|
//
|
|
KeReleaseSpinLock(&m_Lock, OldLevel);
|
|
|
|
//
|
|
// Done for now
|
|
//
|
|
return;
|
|
}
|
|
DPRINT1("Unable to create a new QueueHead\n");
|
|
//ASSERT(FALSE);
|
|
|
|
//
|
|
// Else there was a problem
|
|
// FIXME: Find better return
|
|
UrbStatus = USBD_STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
if (UrbStatus != USBD_STATUS_SUCCESS)
|
|
{
|
|
DPRINT1("URB failed with status 0x%x\n", UrbStatus);
|
|
//PC_ASSERT(FALSE);
|
|
}
|
|
|
|
//
|
|
// free endpoint descriptor
|
|
//
|
|
Request->FreeEndpointDescriptor(EndpointDescriptor);
|
|
|
|
//
|
|
// notify of completion
|
|
//
|
|
Request->CompletionCallback();
|
|
|
|
|
|
//
|
|
// release request
|
|
//
|
|
Request->Release();
|
|
}
|
|
|
|
VOID
|
|
CUSBQueue::PrintEndpointList(
|
|
POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor)
|
|
{
|
|
DPRINT1("CUSBQueue::PrintEndpointList HeadEndpoint %p Logical %x\n", EndpointDescriptor, EndpointDescriptor->PhysicalAddress.LowPart);
|
|
|
|
//
|
|
// get first general transfer descriptor
|
|
//
|
|
EndpointDescriptor = (POHCI_ENDPOINT_DESCRIPTOR)EndpointDescriptor->NextDescriptor;
|
|
|
|
while(EndpointDescriptor)
|
|
{
|
|
DPRINT1(" CUSBQueue::PrintEndpointList Endpoint %p Logical %x\n", EndpointDescriptor, EndpointDescriptor->PhysicalAddress.LowPart);
|
|
|
|
//
|
|
// move to next
|
|
//
|
|
EndpointDescriptor = (POHCI_ENDPOINT_DESCRIPTOR)EndpointDescriptor->NextDescriptor;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
STDMETHODCALLTYPE
|
|
CUSBQueue::TransferDescriptorCompletionCallback(
|
|
ULONG TransferDescriptorLogicalAddress)
|
|
{
|
|
POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor, PreviousEndpointDescriptor;
|
|
PLIST_ENTRY Entry;
|
|
NTSTATUS Status;
|
|
|
|
DPRINT("CUSBQueue::TransferDescriptorCompletionCallback transfer descriptor %x\n", TransferDescriptorLogicalAddress);
|
|
|
|
do
|
|
{
|
|
//
|
|
// find transfer descriptor in control list
|
|
//
|
|
Status = FindTransferDescriptorInEndpoint(m_ControlHeadEndpointDescriptor, TransferDescriptorLogicalAddress, &EndpointDescriptor, &PreviousEndpointDescriptor);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// cleanup endpoint
|
|
//
|
|
CleanupEndpointDescriptor(EndpointDescriptor, PreviousEndpointDescriptor);
|
|
|
|
//
|
|
// done
|
|
//
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// find transfer descriptor in bulk list
|
|
//
|
|
Status = FindTransferDescriptorInEndpoint(m_BulkHeadEndpointDescriptor, TransferDescriptorLogicalAddress, &EndpointDescriptor, &PreviousEndpointDescriptor);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// cleanup endpoint
|
|
//
|
|
CleanupEndpointDescriptor(EndpointDescriptor, PreviousEndpointDescriptor);
|
|
|
|
//
|
|
// done
|
|
//
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// find transfer descriptor in interrupt list
|
|
//
|
|
Status = FindTransferDescriptorInInterruptHeadEndpoints(TransferDescriptorLogicalAddress, &EndpointDescriptor, &PreviousEndpointDescriptor);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// cleanup endpoint
|
|
//
|
|
CleanupEndpointDescriptor(EndpointDescriptor, PreviousEndpointDescriptor);
|
|
|
|
//
|
|
// done
|
|
//
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// last try: find the descriptor in isochronous list
|
|
//
|
|
Status = FindTransferDescriptorInIsochronousHeadEndpoints(TransferDescriptorLogicalAddress, &EndpointDescriptor, &PreviousEndpointDescriptor);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// cleanup endpoint
|
|
//
|
|
DPRINT("ISO endpoint complete\n");
|
|
//ASSERT(FALSE);
|
|
CleanupEndpointDescriptor(EndpointDescriptor, PreviousEndpointDescriptor);
|
|
|
|
//
|
|
// done
|
|
//
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// no more completed descriptors found
|
|
//
|
|
break;
|
|
|
|
}while(TRUE);
|
|
|
|
|
|
//
|
|
// acquire spin lock
|
|
//
|
|
KeAcquireSpinLockAtDpcLevel(&m_Lock);
|
|
|
|
//
|
|
// is there a pending list item
|
|
//
|
|
if (!IsListEmpty(&m_PendingRequestList))
|
|
{
|
|
//
|
|
// get list entry
|
|
//
|
|
Entry = RemoveHeadList(&m_PendingRequestList);
|
|
|
|
//
|
|
// get entry
|
|
//
|
|
EndpointDescriptor = (POHCI_ENDPOINT_DESCRIPTOR)CONTAINING_RECORD(Entry, OHCI_ENDPOINT_DESCRIPTOR, DescriptorListEntry);
|
|
|
|
//
|
|
// add entry
|
|
//
|
|
AddEndpointDescriptor(EndpointDescriptor);
|
|
}
|
|
|
|
//
|
|
// release lock
|
|
//
|
|
KeReleaseSpinLockFromDpcLevel(&m_Lock);
|
|
}
|
|
|
|
POHCI_ENDPOINT_DESCRIPTOR
|
|
CUSBQueue::FindInterruptEndpointDescriptor(
|
|
UCHAR InterruptInterval)
|
|
{
|
|
ULONG Index = 0;
|
|
ULONG Power = 1;
|
|
|
|
//
|
|
// sanity check
|
|
//
|
|
ASSERT(InterruptInterval <= OHCI_BIGGEST_INTERVAL);
|
|
|
|
//
|
|
// find interrupt index
|
|
//
|
|
while (Power <= OHCI_BIGGEST_INTERVAL / 2)
|
|
{
|
|
//
|
|
// is current interval greater
|
|
//
|
|
if (Power * 2 > InterruptInterval)
|
|
break;
|
|
|
|
//
|
|
// increment power
|
|
//
|
|
Power *= 2;
|
|
|
|
//
|
|
// move to next interrupt
|
|
//
|
|
Index++;
|
|
}
|
|
|
|
DPRINT("InterruptInterval %lu Selected InterruptIndex %lu Chosen Interval %lu\n", InterruptInterval, Index, Power);
|
|
|
|
//
|
|
// return endpoint
|
|
//
|
|
return m_InterruptEndpoints[Index];
|
|
}
|
|
|
|
NTSTATUS
|
|
STDMETHODCALLTYPE
|
|
CUSBQueue::AbortDevicePipe(
|
|
IN UCHAR DeviceAddress,
|
|
IN PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor)
|
|
{
|
|
POHCI_ENDPOINT_DESCRIPTOR HeadDescriptor, CurrentDescriptor, PreviousDescriptor, TempDescriptor;
|
|
ULONG Type;
|
|
POHCI_GENERAL_TD TransferDescriptor;
|
|
|
|
//
|
|
// get type
|
|
//
|
|
Type = (EndpointDescriptor->bmAttributes & USB_ENDPOINT_TYPE_MASK);
|
|
|
|
//
|
|
// check type
|
|
//
|
|
if (Type == USB_ENDPOINT_TYPE_BULK)
|
|
{
|
|
//
|
|
// get head descriptor
|
|
//
|
|
HeadDescriptor = m_BulkHeadEndpointDescriptor;
|
|
}
|
|
else if (Type == USB_ENDPOINT_TYPE_CONTROL)
|
|
{
|
|
//
|
|
// get head descriptor
|
|
//
|
|
HeadDescriptor = m_ControlHeadEndpointDescriptor;
|
|
}
|
|
else if (Type == USB_ENDPOINT_TYPE_INTERRUPT)
|
|
{
|
|
//
|
|
// get head descriptor
|
|
//
|
|
HeadDescriptor = FindInterruptEndpointDescriptor(EndpointDescriptor->bInterval);
|
|
ASSERT(HeadDescriptor);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// IMPLEMENT me
|
|
//
|
|
ASSERT(Type == USB_ENDPOINT_TYPE_ISOCHRONOUS);
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
//
|
|
// FIXME should disable list processing
|
|
//
|
|
|
|
//
|
|
// now remove all endpoints
|
|
//
|
|
ASSERT(HeadDescriptor);
|
|
CurrentDescriptor = (POHCI_ENDPOINT_DESCRIPTOR)HeadDescriptor->NextDescriptor;
|
|
PreviousDescriptor = (POHCI_ENDPOINT_DESCRIPTOR)HeadDescriptor;
|
|
|
|
while(CurrentDescriptor)
|
|
{
|
|
if ((CurrentDescriptor->HeadPhysicalDescriptor & OHCI_ENDPOINT_HEAD_MASK) == CurrentDescriptor->TailPhysicalDescriptor || (CurrentDescriptor->HeadPhysicalDescriptor & OHCI_ENDPOINT_HALTED))
|
|
{
|
|
//
|
|
// cleanup endpoint
|
|
//
|
|
TempDescriptor = (POHCI_ENDPOINT_DESCRIPTOR)CurrentDescriptor->NextDescriptor;
|
|
CleanupEndpointDescriptor(CurrentDescriptor, PreviousDescriptor);
|
|
|
|
//
|
|
// use next descriptor
|
|
//
|
|
CurrentDescriptor = TempDescriptor;
|
|
}
|
|
|
|
if (!CurrentDescriptor)
|
|
break;
|
|
|
|
if (CurrentDescriptor->HeadPhysicalDescriptor)
|
|
{
|
|
TransferDescriptor = (POHCI_GENERAL_TD)CurrentDescriptor->HeadLogicalDescriptor;
|
|
ASSERT(TransferDescriptor);
|
|
|
|
if ((OHCI_ENDPOINT_GET_ENDPOINT_NUMBER(TransferDescriptor->Flags) == (EndpointDescriptor->bEndpointAddress & 0xF)) &&
|
|
(OHCI_ENDPOINT_GET_DEVICE_ADDRESS(TransferDescriptor->Flags) == DeviceAddress))
|
|
{
|
|
//
|
|
// cleanup endpoint
|
|
//
|
|
TempDescriptor = (POHCI_ENDPOINT_DESCRIPTOR)CurrentDescriptor->NextDescriptor;
|
|
CleanupEndpointDescriptor(CurrentDescriptor, PreviousDescriptor);
|
|
//
|
|
// use next descriptor
|
|
//
|
|
CurrentDescriptor = TempDescriptor;
|
|
}
|
|
}
|
|
|
|
if (!CurrentDescriptor)
|
|
break;
|
|
|
|
PreviousDescriptor = CurrentDescriptor;
|
|
CurrentDescriptor = (POHCI_ENDPOINT_DESCRIPTOR)CurrentDescriptor->NextDescriptor;
|
|
}
|
|
|
|
//
|
|
// done
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
CreateUSBQueue(
|
|
PUSBQUEUE *OutUsbQueue)
|
|
{
|
|
PUSBQUEUE This;
|
|
|
|
//
|
|
// allocate controller
|
|
//
|
|
This = new(NonPagedPool, TAG_USBOHCI) CUSBQueue(0);
|
|
if (!This)
|
|
{
|
|
//
|
|
// failed to allocate
|
|
//
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// add reference count
|
|
//
|
|
This->AddRef();
|
|
|
|
//
|
|
// return result
|
|
//
|
|
*OutUsbQueue = (PUSBQUEUE)This;
|
|
|
|
//
|
|
// done
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|