2011-05-22 19:36:13 +00:00
|
|
|
/*
|
|
|
|
* 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"
|
|
|
|
#include "hardware.h"
|
|
|
|
|
|
|
|
class CUSBQueue : public IUSBQueue
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2011-05-24 17:57:00 +00:00
|
|
|
// com
|
2011-05-22 19:36:13 +00:00
|
|
|
virtual NTSTATUS Initialize(IN PUSBHARDWAREDEVICE Hardware, PDMA_ADAPTER AdapterObject, IN PDMAMEMORYMANAGER MemManager, IN OPTIONAL PKSPIN_LOCK Lock);
|
|
|
|
virtual ULONG GetPendingRequestCount();
|
|
|
|
virtual NTSTATUS AddUSBRequest(IUSBRequest * Request);
|
|
|
|
virtual NTSTATUS CancelRequests();
|
|
|
|
virtual NTSTATUS CreateUSBRequest(IUSBRequest **OutRequest);
|
2011-05-24 17:57:00 +00:00
|
|
|
virtual VOID TransferDescriptorCompletionCallback(ULONG TransferDescriptorLogicalAddress);
|
|
|
|
|
|
|
|
// local functions
|
|
|
|
BOOLEAN IsTransferDescriptorInEndpoint(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);
|
2011-05-22 19:36:13 +00:00
|
|
|
|
|
|
|
// constructor / destructor
|
|
|
|
CUSBQueue(IUnknown *OuterUnknown){}
|
|
|
|
virtual ~CUSBQueue(){}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
LONG m_Ref; // reference count
|
|
|
|
KSPIN_LOCK m_Lock; // list lock
|
2011-05-23 17:42:52 +00:00
|
|
|
PUSBHARDWAREDEVICE m_Hardware; // hardware
|
|
|
|
POHCI_ENDPOINT_DESCRIPTOR m_BulkHeadEndpointDescriptor; // bulk head descriptor
|
|
|
|
POHCI_ENDPOINT_DESCRIPTOR m_ControlHeadEndpointDescriptor; // control head descriptor
|
2011-05-22 19:36:13 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
//=================================================================================================
|
|
|
|
// 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
|
|
|
|
CUSBQueue::Initialize(
|
|
|
|
IN PUSBHARDWAREDEVICE Hardware,
|
|
|
|
IN PDMA_ADAPTER AdapterObject,
|
|
|
|
IN PDMAMEMORYMANAGER MemManager,
|
|
|
|
IN OPTIONAL PKSPIN_LOCK Lock)
|
|
|
|
{
|
2011-05-23 17:42:52 +00:00
|
|
|
//
|
|
|
|
// get bulk endpoint descriptor
|
|
|
|
//
|
|
|
|
Hardware->GetBulkHeadEndpointDescriptor(&m_BulkHeadEndpointDescriptor);
|
|
|
|
|
|
|
|
//
|
|
|
|
// get control endpoint descriptor
|
|
|
|
//
|
|
|
|
Hardware->GetControlHeadEndpointDescriptor(&m_ControlHeadEndpointDescriptor);
|
|
|
|
|
|
|
|
//
|
|
|
|
// initialize spinlock
|
|
|
|
//
|
|
|
|
KeInitializeSpinLock(&m_Lock);
|
|
|
|
|
|
|
|
//
|
|
|
|
// store hardware
|
|
|
|
//
|
|
|
|
m_Hardware = Hardware;
|
|
|
|
|
2011-05-22 19:36:13 +00:00
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
ULONG
|
|
|
|
CUSBQueue::GetPendingRequestCount()
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Loop through the pending list and iterrate one for each QueueHead that
|
|
|
|
// has a IRP to complete.
|
|
|
|
//
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
CUSBQueue::AddUSBRequest(
|
|
|
|
IUSBRequest * Request)
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
|
|
ULONG Type;
|
|
|
|
KIRQL OldLevel;
|
2011-05-23 17:42:52 +00:00
|
|
|
POHCI_ENDPOINT_DESCRIPTOR HeadDescriptor;
|
|
|
|
POHCI_ENDPOINT_DESCRIPTOR Descriptor;
|
|
|
|
|
|
|
|
DPRINT1("CUSBQueue::AddUSBRequest\n");
|
2011-05-22 19:36:13 +00:00
|
|
|
|
|
|
|
//
|
|
|
|
// sanity check
|
|
|
|
//
|
|
|
|
ASSERT(Request != NULL);
|
|
|
|
|
|
|
|
//
|
|
|
|
// 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_BULK:
|
|
|
|
case USB_ENDPOINT_TYPE_CONTROL:
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* BUG */
|
|
|
|
PC_ASSERT(FALSE);
|
|
|
|
Status = STATUS_NOT_SUPPORTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// check for success
|
|
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// request not supported, please try later
|
|
|
|
//
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// add extra reference which is released when the request is completed
|
|
|
|
//
|
|
|
|
Request->AddRef();
|
|
|
|
|
2011-05-23 17:42:52 +00:00
|
|
|
//
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// link endpoints
|
|
|
|
//
|
|
|
|
Descriptor->NextPhysicalEndpoint = HeadDescriptor->NextPhysicalEndpoint;
|
|
|
|
Descriptor->NextDescriptor = HeadDescriptor->NextDescriptor;
|
|
|
|
|
|
|
|
HeadDescriptor->NextPhysicalEndpoint = Descriptor->PhysicalAddress.LowPart;
|
|
|
|
HeadDescriptor->NextDescriptor = Descriptor;
|
|
|
|
|
|
|
|
//
|
|
|
|
// set descriptor active
|
|
|
|
//
|
|
|
|
Descriptor->Flags &= ~OHCI_ENDPOINT_SKIP;
|
2011-05-24 17:57:00 +00:00
|
|
|
//HeadDescriptor->Flags &= ~OHCI_ENDPOINT_SKIP;
|
2011-05-23 17:42:52 +00:00
|
|
|
|
|
|
|
//
|
|
|
|
// notify hardware of our request
|
|
|
|
//
|
|
|
|
m_Hardware->HeadEndpointDescriptorModified(Type);
|
|
|
|
|
2011-05-24 17:57:00 +00:00
|
|
|
DPRINT1("Request %x %x added to queue\n", Descriptor, Descriptor->PhysicalAddress);
|
2011-05-23 17:42:52 +00:00
|
|
|
|
2011-05-22 19:36:13 +00:00
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
CUSBQueue::CancelRequests()
|
|
|
|
{
|
|
|
|
UNIMPLEMENTED
|
|
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
CUSBQueue::CreateUSBRequest(
|
|
|
|
IUSBRequest **OutRequest)
|
|
|
|
{
|
|
|
|
PUSBREQUEST UsbRequest;
|
|
|
|
NTSTATUS Status;
|
|
|
|
|
|
|
|
*OutRequest = NULL;
|
|
|
|
Status = InternalCreateUSBRequest(&UsbRequest);
|
|
|
|
|
|
|
|
if (NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
*OutRequest = UsbRequest;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
2011-05-24 17:57:00 +00:00
|
|
|
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 (IsTransferDescriptorInEndpoint(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::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::TransferDescriptorCompletionCallback(
|
|
|
|
ULONG TransferDescriptorLogicalAddress)
|
|
|
|
{
|
|
|
|
POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor, PreviousEndpointDescriptor;
|
|
|
|
NTSTATUS Status;
|
|
|
|
PUSBREQUEST Request;
|
|
|
|
|
|
|
|
//
|
|
|
|
// find transfer descriptor in control list
|
|
|
|
//
|
|
|
|
Status = FindTransferDescriptorInEndpoint(m_ControlHeadEndpointDescriptor, TransferDescriptorLogicalAddress, &EndpointDescriptor, &PreviousEndpointDescriptor);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// FIXME: make sure this is ok
|
|
|
|
// unlink descriptor
|
|
|
|
//
|
|
|
|
PreviousEndpointDescriptor->NextDescriptor = EndpointDescriptor->NextDescriptor;
|
|
|
|
PreviousEndpointDescriptor->NextPhysicalEndpoint = EndpointDescriptor->NextPhysicalEndpoint;
|
|
|
|
|
|
|
|
//
|
|
|
|
// get corresponding request
|
|
|
|
//
|
|
|
|
Request = PUSBREQUEST(EndpointDescriptor->Request);
|
|
|
|
|
|
|
|
//
|
|
|
|
// notify of completion
|
|
|
|
//
|
|
|
|
Request->CompletionCallback(EndpointDescriptor);
|
|
|
|
|
|
|
|
//
|
|
|
|
// FIXME: check if complete
|
|
|
|
//
|
|
|
|
|
|
|
|
//
|
|
|
|
// release request
|
|
|
|
//
|
|
|
|
Request->Release();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2011-05-22 19:36:13 +00:00
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
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;
|
|
|
|
}
|