reactos/reactos/drivers/usb/usbehci/usb_request.cpp
Thomas Faber 1bb6977402 [USBEHCI][USBOHCI]
- Touch the virtual address of DMA buffers before calling MmGetPhysicalAddress on them. This ensures page directories are correctly set up for the current process context, and fixes random DMA operation failures ("MM:MmGetPhysicalAddressFailed"). This is not a hack -- using MmGetPhysicalAddress for DMA in the first place is.
CORE-9224 #resolve

svn path=/trunk/; revision=74408
2017-04-26 10:53:32 +00:00

1852 lines
51 KiB
C++

/*
* PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface
* LICENSE: GPL - See COPYING in the top level directory
* FILE: drivers/usb/usbehci/usb_request.cpp
* PURPOSE: USB EHCI device driver.
* PROGRAMMERS:
* Michael Martin (michael.martin@reactos.org)
* Johannes Anderwald (johannes.anderwald@reactos.org)
*/
#include "usbehci.h"
#define NDEBUG
#include <debug.h>
class CUSBRequest : public IEHCIRequest
{
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;
}
// IUSBRequest interface functions
IMP_IUSBREQUEST
// IEHCI Request interface functions
IMP_IEHCIREQUEST
// local functions
ULONG InternalGetTransferType();
UCHAR InternalGetPidDirection();
NTSTATUS BuildControlTransferQueueHead(PQUEUE_HEAD * OutHead);
NTSTATUS BuildBulkInterruptTransferQueueHead(PQUEUE_HEAD * OutHead);
NTSTATUS STDMETHODCALLTYPE CreateDescriptor(PQUEUE_TRANSFER_DESCRIPTOR *OutDescriptor);
NTSTATUS CreateQueueHead(PQUEUE_HEAD *OutQueueHead);
UCHAR STDMETHODCALLTYPE GetDeviceAddress();
NTSTATUS BuildSetupPacket();
NTSTATUS BuildSetupPacketFromURB();
ULONG InternalCalculateTransferLength();
NTSTATUS STDMETHODCALLTYPE BuildTransferDescriptorChain(IN PQUEUE_HEAD QueueHead, IN PVOID TransferBuffer, IN ULONG TransferBufferLength, IN UCHAR PidCode, IN UCHAR InitialDataToggle, OUT PQUEUE_TRANSFER_DESCRIPTOR * OutFirstDescriptor, OUT PQUEUE_TRANSFER_DESCRIPTOR * OutLastDescriptor, OUT PUCHAR OutDataToggle, OUT PULONG OutTransferBufferOffset);
VOID STDMETHODCALLTYPE InitDescriptor(IN PQUEUE_TRANSFER_DESCRIPTOR CurrentDescriptor, IN PVOID TransferBuffer, IN ULONG TransferBufferLength, IN UCHAR PidCode, IN UCHAR DataToggle, OUT PULONG OutDescriptorLength);
VOID DumpQueueHead(IN PQUEUE_HEAD QueueHead);
// constructor / destructor
CUSBRequest(IUnknown *OuterUnknown);
virtual ~CUSBRequest();
protected:
LONG m_Ref;
//
// memory manager for allocating setup packet / queue head / transfer descriptors
//
PDMAMEMORYMANAGER m_DmaManager;
//
// caller provided irp packet containing URB request
//
PIRP m_Irp;
//
// transfer buffer length
//
ULONG m_TransferBufferLength;
//
// current transfer length
//
ULONG m_TransferBufferLengthCompleted;
//
// Total Transfer Length
//
ULONG m_TotalBytesTransferred;
//
// transfer buffer MDL
//
PMDL m_TransferBufferMDL;
//
// caller provided setup packet
//
PUSB_DEFAULT_PIPE_SETUP_PACKET m_SetupPacket;
//
// completion event for callers who initialized request with setup packet
//
PKEVENT m_CompletionEvent;
//
// device address for callers who initialized it with device address
//
UCHAR m_DeviceAddress;
//
// store end point address
//
PUSB_ENDPOINT m_EndpointDescriptor;
//
// DMA queue head
//
PQUEUE_HEAD m_QueueHead;
//
// allocated setup packet from the DMA pool
//
PUSB_DEFAULT_PIPE_SETUP_PACKET m_DescriptorPacket;
PHYSICAL_ADDRESS m_DescriptorSetupPacket;
//
// stores the result of the operation
//
NTSTATUS m_NtStatusCode;
ULONG m_UrbStatusCode;
// buffer base address
PVOID m_Base;
// device speed
USB_DEVICE_SPEED m_Speed;
};
//----------------------------------------------------------------------------------------
CUSBRequest::CUSBRequest(IUnknown *OuterUnknown) :
m_CompletionEvent(NULL)
{
UNREFERENCED_PARAMETER(OuterUnknown);
}
//----------------------------------------------------------------------------------------
CUSBRequest::~CUSBRequest()
{
if (m_CompletionEvent != NULL)
{
ExFreePoolWithTag(m_CompletionEvent, TAG_USBEHCI);
}
}
//----------------------------------------------------------------------------------------
NTSTATUS
STDMETHODCALLTYPE
CUSBRequest::QueryInterface(
IN REFIID refiid,
OUT PVOID* Output)
{
return STATUS_UNSUCCESSFUL;
}
//----------------------------------------------------------------------------------------
NTSTATUS
STDMETHODCALLTYPE
CUSBRequest::InitializeWithSetupPacket(
IN PDMAMEMORYMANAGER DmaManager,
IN PUSB_DEFAULT_PIPE_SETUP_PACKET SetupPacket,
IN PUSBDEVICE Device,
IN OPTIONAL PUSB_ENDPOINT EndpointDescriptor,
IN OUT ULONG TransferBufferLength,
IN OUT PMDL TransferBuffer)
{
//
// sanity checks
//
PC_ASSERT(DmaManager);
PC_ASSERT(SetupPacket);
//
// initialize packet
//
m_DmaManager = DmaManager;
m_SetupPacket = SetupPacket;
m_TransferBufferLength = TransferBufferLength;
m_TransferBufferMDL = TransferBuffer;
m_DeviceAddress = Device->GetDeviceAddress();
m_Speed = Device->GetSpeed();
m_EndpointDescriptor = EndpointDescriptor;
m_TotalBytesTransferred = 0;
//
// Set Length Completed to 0
//
m_TransferBufferLengthCompleted = 0;
//
// allocate completion event
//
m_CompletionEvent = (PKEVENT)ExAllocatePoolWithTag(NonPagedPool, sizeof(KEVENT), TAG_USBEHCI);
if (!m_CompletionEvent)
{
//
// failed to allocate completion event
//
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// initialize completion event
//
KeInitializeEvent(m_CompletionEvent, NotificationEvent, FALSE);
//
// done
//
return STATUS_SUCCESS;
}
//----------------------------------------------------------------------------------------
NTSTATUS
STDMETHODCALLTYPE
CUSBRequest::InitializeWithIrp(
IN PDMAMEMORYMANAGER DmaManager,
IN PUSBDEVICE Device,
IN OUT PIRP Irp)
{
PIO_STACK_LOCATION IoStack;
PURB Urb;
//
// sanity checks
//
PC_ASSERT(DmaManager);
PC_ASSERT(Irp);
m_DmaManager = DmaManager;
m_TotalBytesTransferred = 0;
m_Speed = Device->GetSpeed();
//
// get current irp stack location
//
IoStack = IoGetCurrentIrpStackLocation(Irp);
//
// sanity check
//
PC_ASSERT(IoStack->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL);
PC_ASSERT(IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_INTERNAL_USB_SUBMIT_URB);
PC_ASSERT(IoStack->Parameters.Others.Argument1 != 0);
//
// get urb
//
Urb = (PURB)IoStack->Parameters.Others.Argument1;
//
// store irp
//
m_Irp = Irp;
//
// check function type
//
switch (Urb->UrbHeader.Function)
{
//
// luckily those request have the same structure layout
//
case URB_FUNCTION_CLASS_INTERFACE:
case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE:
case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER:
{
//
// bulk interrupt transfer
//
if (Urb->UrbBulkOrInterruptTransfer.TransferBufferLength)
{
//
// Check if there is a MDL
//
if (!Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL)
{
//
// sanity check
//
PC_ASSERT(Urb->UrbBulkOrInterruptTransfer.TransferBuffer);
//
// Create one using TransferBuffer
//
DPRINT("Creating Mdl from Urb Buffer %p Length %lu\n", Urb->UrbBulkOrInterruptTransfer.TransferBuffer, Urb->UrbBulkOrInterruptTransfer.TransferBufferLength);
m_TransferBufferMDL = IoAllocateMdl(Urb->UrbBulkOrInterruptTransfer.TransferBuffer,
Urb->UrbBulkOrInterruptTransfer.TransferBufferLength,
FALSE,
FALSE,
NULL);
if (!m_TransferBufferMDL)
{
//
// failed to allocate mdl
//
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// build mdl for non paged pool
// FIXME: Does hub driver already do this when passing MDL?
//
MmBuildMdlForNonPagedPool(m_TransferBufferMDL);
//
// Keep that ehci created the MDL and needs to free it.
//
}
else
{
m_TransferBufferMDL = Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL;
}
//
// save buffer length
//
m_TransferBufferLength = Urb->UrbBulkOrInterruptTransfer.TransferBufferLength;
//
// Set Length Completed to 0
//
m_TransferBufferLengthCompleted = 0;
//
// get endpoint descriptor
//
m_EndpointDescriptor = (PUSB_ENDPOINT)Urb->UrbBulkOrInterruptTransfer.PipeHandle;
}
break;
}
default:
DPRINT1("URB Function: not supported %x\n", Urb->UrbHeader.Function);
//ASSERT(FALSE);
}
//
// done
//
return STATUS_SUCCESS;
}
//----------------------------------------------------------------------------------------
VOID
STDMETHODCALLTYPE
CUSBRequest::CompletionCallback(
IN NTSTATUS NtStatusCode,
IN ULONG UrbStatusCode,
IN struct _QUEUE_HEAD *QueueHead)
{
PIO_STACK_LOCATION IoStack;
PURB Urb;
//
// FIXME: support linked queue heads
//
//
// store completion code
//
m_NtStatusCode = NtStatusCode;
m_UrbStatusCode = UrbStatusCode;
if (m_Irp)
{
//
// set irp completion status
//
m_Irp->IoStatus.Status = NtStatusCode;
//
// get current irp stack location
//
IoStack = IoGetCurrentIrpStackLocation(m_Irp);
//
// get urb
//
Urb = (PURB)IoStack->Parameters.Others.Argument1;
//
// store urb status
//
Urb->UrbHeader.Status = UrbStatusCode;
//
// Check if the MDL was created
//
if (!Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL)
{
//
// Free Mdl
//
IoFreeMdl(m_TransferBufferMDL);
}
//
// check if the request was successful
//
if (!NT_SUCCESS(NtStatusCode))
{
//
// set returned length to zero in case of error
//
Urb->UrbHeader.Length = 0;
}
else
{
//
// calculate transfer length
//
Urb->UrbBulkOrInterruptTransfer.TransferBufferLength = InternalCalculateTransferLength();
}
DPRINT("Request %p Completing Irp %p NtStatusCode %x UrbStatusCode %x Transferred Length %lu\n", this, m_Irp, NtStatusCode, UrbStatusCode, Urb->UrbBulkOrInterruptTransfer.TransferBufferLength);
//
// FIXME: check if the transfer was split
// if yes dont complete irp yet
//
IoCompleteRequest(m_Irp, IO_NO_INCREMENT);
}
else
{
//
// signal completion event
//
PC_ASSERT(m_CompletionEvent);
KeSetEvent(m_CompletionEvent, 0, FALSE);
}
}
//----------------------------------------------------------------------------------------
NTSTATUS
STDMETHODCALLTYPE
CUSBRequest::GetQueueHead(
struct _QUEUE_HEAD ** OutHead)
{
ULONG TransferType;
NTSTATUS Status;
//
// first get transfer type
//
TransferType = InternalGetTransferType();
//
// build request depending on type
//
switch(TransferType)
{
case USB_ENDPOINT_TYPE_CONTROL:
Status = BuildControlTransferQueueHead(OutHead);
break;
case USB_ENDPOINT_TYPE_INTERRUPT:
case USB_ENDPOINT_TYPE_BULK:
Status = BuildBulkInterruptTransferQueueHead(OutHead);
break;
case USB_ENDPOINT_TYPE_ISOCHRONOUS:
DPRINT1("USB_ENDPOINT_TYPE_ISOCHRONOUS not implemented\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
default:
PC_ASSERT(FALSE);
Status = STATUS_NOT_IMPLEMENTED;
break;
}
if (NT_SUCCESS(Status))
{
//
// store queue head
//
m_QueueHead = *OutHead;
//
// store request object
//
(*OutHead)->Request = PVOID(this);
}
//
// done
//
return Status;
}
//----------------------------------------------------------------------------------------
BOOLEAN
STDMETHODCALLTYPE
CUSBRequest::IsRequestComplete()
{
//
// FIXME: check if request was split
//
//
// Check if the transfer was completed, only valid for Bulk Transfers
//
if ((m_TransferBufferLengthCompleted < m_TransferBufferLength)
&& (GetTransferType() == USB_ENDPOINT_TYPE_BULK))
{
//
// Transfer not completed
//
return FALSE;
}
return TRUE;
}
//----------------------------------------------------------------------------------------
ULONG
STDMETHODCALLTYPE
CUSBRequest::GetTransferType()
{
//
// call internal implementation
//
return InternalGetTransferType();
}
//----------------------------------------------------------------------------------------
ULONG
CUSBRequest::InternalGetTransferType()
{
ULONG TransferType;
//
// check if an irp is provided
//
if (m_Irp)
{
ASSERT(m_EndpointDescriptor);
//
// end point is defined in the low byte of bmAttributes
//
TransferType = (m_EndpointDescriptor->EndPointDescriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK);
}
else
{
//
// initialized with setup packet, must be a control transfer
//
TransferType = USB_ENDPOINT_TYPE_CONTROL;
ASSERT(m_EndpointDescriptor == NULL);
}
//
// done
//
return TransferType;
}
UCHAR
CUSBRequest::InternalGetPidDirection()
{
if (m_EndpointDescriptor)
{
//
// end point direction is highest bit in bEndpointAddress
//
return (m_EndpointDescriptor->EndPointDescriptor.bEndpointAddress & USB_ENDPOINT_DIRECTION_MASK) >> 7;
}
else
{
//
// request arrives on the control pipe, extract direction from setup packet
//
ASSERT(m_DescriptorPacket);
return (m_DescriptorPacket->bmRequestType.B >> 7);
}
}
VOID
STDMETHODCALLTYPE
CUSBRequest::InitDescriptor(
IN PQUEUE_TRANSFER_DESCRIPTOR CurrentDescriptor,
IN PVOID TransferBuffer,
IN ULONG TransferBufferLength,
IN UCHAR PidCode,
IN UCHAR DataToggle,
OUT PULONG OutDescriptorLength)
{
ULONG Index, Length = 0, PageOffset, BufferLength;
PHYSICAL_ADDRESS Address;
//
// init transfer descriptor
//
CurrentDescriptor->Token.Bits.PIDCode = PidCode;
CurrentDescriptor->Token.Bits.TotalBytesToTransfer = 0;
CurrentDescriptor->Token.Bits.DataToggle = DataToggle;
//
// sanity check
//
ASSERT(TransferBufferLength);
//
// store buffers
//
Index = 0;
do
{
//
// get address (HACK)
//
*(volatile char *)TransferBuffer;
Address = MmGetPhysicalAddress(TransferBuffer);
//
// use physical address
//
CurrentDescriptor->BufferPointer[Index] = Address.LowPart;
CurrentDescriptor->ExtendedBufferPointer[Index] = Address.HighPart;
//
// Get the offset from page size
//
PageOffset = BYTE_OFFSET(CurrentDescriptor->BufferPointer[Index]);
if (PageOffset != 0)
{
//
// move to next page
//
TransferBuffer = (PVOID)ROUND_TO_PAGES(TransferBuffer);
}
else
{
//
// move to next page
//
TransferBuffer = (PVOID)((ULONG_PTR)TransferBuffer + PAGE_SIZE);
}
//
// calculate buffer length
//
BufferLength = min(TransferBufferLength, PAGE_SIZE - PageOffset);
//
// increment transfer bytes
//
CurrentDescriptor->Token.Bits.TotalBytesToTransfer += BufferLength;
CurrentDescriptor->TotalBytesToTransfer += BufferLength;
Length += BufferLength;
DPRINT("Index %lu TransferBufferLength %lu PageOffset %x BufferLength %lu Buffer Phy %p TransferBuffer %p\n", Index, TransferBufferLength, PageOffset, BufferLength, CurrentDescriptor->BufferPointer[Index], TransferBuffer);
//
// decrement available byte count
//
TransferBufferLength -= BufferLength;
if (TransferBufferLength == 0)
{
//
// end reached
//
break;
}
//
// sanity check
//
if (Index > 1)
{
//
// no equal buffers
//
ASSERT(CurrentDescriptor->BufferPointer[Index] != CurrentDescriptor->BufferPointer[Index-1]);
}
//
// next descriptor index
//
Index++;
}while(Index < 5);
//
// store result
//
*OutDescriptorLength = Length;
}
NTSTATUS
STDMETHODCALLTYPE
CUSBRequest::BuildTransferDescriptorChain(
IN PQUEUE_HEAD QueueHead,
IN PVOID TransferBuffer,
IN ULONG TransferBufferLength,
IN UCHAR PidCode,
IN UCHAR InitialDataToggle,
OUT PQUEUE_TRANSFER_DESCRIPTOR * OutFirstDescriptor,
OUT PQUEUE_TRANSFER_DESCRIPTOR * OutLastDescriptor,
OUT PUCHAR OutDataToggle,
OUT PULONG OutTransferBufferOffset)
{
PQUEUE_TRANSFER_DESCRIPTOR FirstDescriptor = NULL, CurrentDescriptor, LastDescriptor = NULL;
NTSTATUS Status;
ULONG DescriptorLength, TransferBufferOffset = 0;
ULONG MaxPacketSize = 0, TransferSize;
//
// is there an endpoint descriptor
//
if (m_EndpointDescriptor)
{
//
// use endpoint packet size
//
MaxPacketSize = m_EndpointDescriptor->EndPointDescriptor.wMaxPacketSize;
}
do
{
//
// allocate transfer descriptor
//
Status = CreateDescriptor(&CurrentDescriptor);
if (!NT_SUCCESS(Status))
{
//
// failed to allocate transfer descriptor
//
return STATUS_INSUFFICIENT_RESOURCES;
}
if (MaxPacketSize)
{
//
// transfer size is minimum available buffer or endpoint size
//
TransferSize = min(TransferBufferLength - TransferBufferOffset, MaxPacketSize);
}
else
{
//
// use available buffer
//
TransferSize = TransferBufferLength - TransferBufferOffset;
}
//
// now init the descriptor
//
InitDescriptor(CurrentDescriptor,
(PVOID)((ULONG_PTR)TransferBuffer + TransferBufferOffset),
TransferSize,
PidCode,
InitialDataToggle,
&DescriptorLength);
//
// insert into queue head
//
InsertTailList(&QueueHead->TransferDescriptorListHead, &CurrentDescriptor->DescriptorEntry);
//
// adjust offset
//
TransferBufferOffset += DescriptorLength;
if (LastDescriptor)
{
//
// link to current descriptor
//
LastDescriptor->NextPointer = CurrentDescriptor->PhysicalAddr;
LastDescriptor = CurrentDescriptor;
}
else
{
//
// first descriptor in chain
//
LastDescriptor = FirstDescriptor = CurrentDescriptor;
}
//
// flip data toggle
//
InitialDataToggle = !InitialDataToggle;
if(TransferBufferLength == TransferBufferOffset)
{
//
// end reached
//
break;
}
}while(TRUE);
if (OutFirstDescriptor)
{
//
// store first descriptor
//
*OutFirstDescriptor = FirstDescriptor;
}
if (OutLastDescriptor)
{
//
// store last descriptor
//
*OutLastDescriptor = CurrentDescriptor;
}
if (OutDataToggle)
{
//
// store result data toggle
//
*OutDataToggle = InitialDataToggle;
}
if (OutTransferBufferOffset)
{
//
// store offset
//
*OutTransferBufferOffset = TransferBufferOffset;
}
//
// done
//
return STATUS_SUCCESS;
}
//----------------------------------------------------------------------------------------
NTSTATUS
CUSBRequest::BuildControlTransferQueueHead(
PQUEUE_HEAD * OutHead)
{
NTSTATUS Status;
ULONG DescriptorChainLength;
PQUEUE_HEAD QueueHead;
PQUEUE_TRANSFER_DESCRIPTOR SetupDescriptor, StatusDescriptor, FirstDescriptor, LastDescriptor;
//
// first allocate the queue head
//
Status = CreateQueueHead(&QueueHead);
if (!NT_SUCCESS(Status))
{
//
// failed to allocate queue head
//
DPRINT1("[EHCI] Failed to create queue head\n");
return Status;
}
//
// sanity check
//
PC_ASSERT(QueueHead);
//
// create setup packet
//
Status = BuildSetupPacket();
if (!NT_SUCCESS(Status))
{
// failed to create setup packet
DPRINT1("[EHCI] Failed to create setup packet\n");
// release queue head
m_DmaManager->Release(QueueHead, sizeof(QUEUE_HEAD));
return Status;
}
//
// create setup descriptor
//
Status = CreateDescriptor(&SetupDescriptor);
if (!NT_SUCCESS(Status))
{
// failed to create setup transfer descriptor
DPRINT1("[EHCI] Failed to create setup descriptor\n");
if (m_DescriptorPacket)
{
// release packet descriptor
m_DmaManager->Release(m_DescriptorPacket, sizeof(USB_DEFAULT_PIPE_SETUP_PACKET));
}
// release queue head
m_DmaManager->Release(QueueHead, sizeof(QUEUE_HEAD));
return Status;
}
//
// create status descriptor
//
Status = CreateDescriptor(&StatusDescriptor);
if (!NT_SUCCESS(Status))
{
// failed to create status transfer descriptor
DPRINT1("[EHCI] Failed to create status descriptor\n");
// release setup transfer descriptor
m_DmaManager->Release(SetupDescriptor, sizeof(QUEUE_TRANSFER_DESCRIPTOR));
if (m_DescriptorPacket)
{
// release packet descriptor
m_DmaManager->Release(m_DescriptorPacket, sizeof(USB_DEFAULT_PIPE_SETUP_PACKET));
}
// release queue head
m_DmaManager->Release(QueueHead, sizeof(QUEUE_HEAD));
return Status;
}
//
// now initialize the queue head
//
QueueHead->EndPointCharacteristics.DeviceAddress = GetDeviceAddress();
ASSERT(m_EndpointDescriptor == NULL);
//
// init setup descriptor
//
SetupDescriptor->Token.Bits.PIDCode = PID_CODE_SETUP_TOKEN;
SetupDescriptor->Token.Bits.TotalBytesToTransfer = sizeof(USB_DEFAULT_PIPE_SETUP_PACKET);
SetupDescriptor->Token.Bits.DataToggle = FALSE;
SetupDescriptor->BufferPointer[0] = m_DescriptorSetupPacket.LowPart;
SetupDescriptor->ExtendedBufferPointer[0] = m_DescriptorSetupPacket.HighPart;
InsertTailList(&QueueHead->TransferDescriptorListHead, &SetupDescriptor->DescriptorEntry);
//
// init status descriptor
//
StatusDescriptor->Token.Bits.TotalBytesToTransfer = 0;
StatusDescriptor->Token.Bits.DataToggle = TRUE;
StatusDescriptor->Token.Bits.InterruptOnComplete = TRUE;
//
// is there data
//
if (m_TransferBufferLength)
{
Status = BuildTransferDescriptorChain(QueueHead,
MmGetMdlVirtualAddress(m_TransferBufferMDL),
m_TransferBufferLength,
InternalGetPidDirection(),
TRUE,
&FirstDescriptor,
&LastDescriptor,
NULL,
&DescriptorChainLength);
if (!NT_SUCCESS(Status))
{
// failed to create descriptor chain
DPRINT1("[EHCI] Failed to create descriptor chain\n");
// release status transfer descriptor
m_DmaManager->Release(StatusDescriptor, sizeof(QUEUE_TRANSFER_DESCRIPTOR));
// release setup transfer descriptor
m_DmaManager->Release(SetupDescriptor, sizeof(QUEUE_TRANSFER_DESCRIPTOR));
if (m_DescriptorPacket)
{
// release packet descriptor
m_DmaManager->Release(m_DescriptorPacket, sizeof(USB_DEFAULT_PIPE_SETUP_PACKET));
}
// release queue head
m_DmaManager->Release(QueueHead, sizeof(QUEUE_HEAD));
return Status;
}
if (m_TransferBufferLength != DescriptorChainLength)
{
DPRINT1("DescriptorChainLength %x\n", DescriptorChainLength);
DPRINT1("m_TransferBufferLength %x\n", m_TransferBufferLength);
ASSERT(FALSE);
}
//
// now link the descriptors
//
SetupDescriptor->NextPointer = FirstDescriptor->PhysicalAddr;
SetupDescriptor->AlternateNextPointer = FirstDescriptor->PhysicalAddr;
LastDescriptor->NextPointer = StatusDescriptor->PhysicalAddr;
LastDescriptor->AlternateNextPointer = StatusDescriptor->PhysicalAddr;
//
// pid code is flipped for ops with data stage
//
StatusDescriptor->Token.Bits.PIDCode = !InternalGetPidDirection();
}
else
{
//
// direct link
//
SetupDescriptor->NextPointer = StatusDescriptor->PhysicalAddr;
SetupDescriptor->AlternateNextPointer = StatusDescriptor->PhysicalAddr;
//
// retrieve result of operation
//
StatusDescriptor->Token.Bits.PIDCode = PID_CODE_IN_TOKEN;
}
//
// insert status descriptor
//
InsertTailList(&QueueHead->TransferDescriptorListHead, &StatusDescriptor->DescriptorEntry);
//
// link transfer descriptors to queue head
//
QueueHead->NextPointer = SetupDescriptor->PhysicalAddr;
//
// store result
//
*OutHead = QueueHead;
//
// displays the current request
//
//DumpQueueHead(QueueHead);
DPRINT("BuildControlTransferQueueHead done\n");
//
// done
//
return STATUS_SUCCESS;
}
VOID
CUSBRequest::DumpQueueHead(
IN PQUEUE_HEAD QueueHead)
{
PLIST_ENTRY Entry;
PQUEUE_TRANSFER_DESCRIPTOR Descriptor;
ULONG Index = 0;
DPRINT1("QueueHead %p Addr %x\n", QueueHead, QueueHead->PhysicalAddr);
DPRINT1("QueueHead AlternateNextPointer %x\n", QueueHead->AlternateNextPointer);
DPRINT1("QueueHead NextPointer %x\n", QueueHead->NextPointer);
DPRINT1("QueueHead HubAddr %x\n", QueueHead->EndPointCharacteristics.ControlEndPointFlag);
DPRINT1("QueueHead DeviceAddress %x\n", QueueHead->EndPointCharacteristics.DeviceAddress);
DPRINT1("QueueHead EndPointNumber %x\n", QueueHead->EndPointCharacteristics.EndPointNumber);
DPRINT1("QueueHead EndPointSpeed %x\n", QueueHead->EndPointCharacteristics.EndPointSpeed);
DPRINT1("QueueHead HeadOfReclamation %x\n", QueueHead->EndPointCharacteristics.HeadOfReclamation);
DPRINT1("QueueHead InactiveOnNextTransaction %x\n", QueueHead->EndPointCharacteristics.InactiveOnNextTransaction);
DPRINT1("QueueHead MaximumPacketLength %x\n", QueueHead->EndPointCharacteristics.MaximumPacketLength);
DPRINT1("QueueHead NakCountReload %x\n", QueueHead->EndPointCharacteristics.NakCountReload);
DPRINT1("QueueHead QEDTDataToggleControl %x\n", QueueHead->EndPointCharacteristics.QEDTDataToggleControl);
DPRINT1("QueueHead HubAddr %x\n", QueueHead->EndPointCapabilities.HubAddr);
DPRINT1("QueueHead InterruptScheduleMask %x\n", QueueHead->EndPointCapabilities.InterruptScheduleMask);
DPRINT1("QueueHead NumberOfTransactionPerFrame %x\n", QueueHead->EndPointCapabilities.NumberOfTransactionPerFrame);
DPRINT1("QueueHead PortNumber %x\n", QueueHead->EndPointCapabilities.PortNumber);
DPRINT1("QueueHead SplitCompletionMask %x\n", QueueHead->EndPointCapabilities.SplitCompletionMask);
Entry = QueueHead->TransferDescriptorListHead.Flink;
while(Entry != &QueueHead->TransferDescriptorListHead)
{
//
// get transfer descriptor
//
Descriptor = (PQUEUE_TRANSFER_DESCRIPTOR)CONTAINING_RECORD(Entry, QUEUE_TRANSFER_DESCRIPTOR, DescriptorEntry);
DPRINT1("TransferDescriptor %lu Addr %x\n", Index, Descriptor->PhysicalAddr);
DPRINT1("TransferDescriptor %lu Next %x\n", Index, Descriptor->NextPointer);
DPRINT1("TransferDescriptor %lu AlternateNextPointer %x\n", Index, Descriptor->AlternateNextPointer);
DPRINT1("TransferDescriptor %lu Active %lu\n", Index, Descriptor->Token.Bits.Active);
DPRINT1("TransferDescriptor %lu BabbleDetected %lu\n", Index, Descriptor->Token.Bits.BabbleDetected);
DPRINT1("TransferDescriptor %lu CurrentPage %lu\n", Index, Descriptor->Token.Bits.CurrentPage);
DPRINT1("TransferDescriptor %lu DataBufferError %lu\n", Index, Descriptor->Token.Bits.DataBufferError);
DPRINT1("TransferDescriptor %lu DataToggle %lu\n", Index, Descriptor->Token.Bits.DataToggle);
DPRINT1("TransferDescriptor %lu ErrorCounter %lu\n", Index, Descriptor->Token.Bits.ErrorCounter);
DPRINT1("TransferDescriptor %lu Halted %lu\n", Index, Descriptor->Token.Bits.Halted);
DPRINT1("TransferDescriptor %lu InterruptOnComplete %x\n", Index, Descriptor->Token.Bits.InterruptOnComplete);
DPRINT1("TransferDescriptor %lu MissedMicroFrame %lu\n", Index, Descriptor->Token.Bits.MissedMicroFrame);
DPRINT1("TransferDescriptor %lu PIDCode %lu\n", Index, Descriptor->Token.Bits.PIDCode);
DPRINT1("TransferDescriptor %lu PingState %lu\n", Index, Descriptor->Token.Bits.PingState);
DPRINT1("TransferDescriptor %lu SplitTransactionState %lu\n", Index, Descriptor->Token.Bits.SplitTransactionState);
DPRINT1("TransferDescriptor %lu TotalBytesToTransfer %lu\n", Index, Descriptor->Token.Bits.TotalBytesToTransfer);
DPRINT1("TransferDescriptor %lu TransactionError %lu\n", Index, Descriptor->Token.Bits.TransactionError);
DPRINT1("TransferDescriptor %lu Buffer Pointer 0 %x\n", Index, Descriptor->BufferPointer[0]);
DPRINT1("TransferDescriptor %lu Buffer Pointer 1 %x\n", Index, Descriptor->BufferPointer[1]);
DPRINT1("TransferDescriptor %lu Buffer Pointer 2 %x\n", Index, Descriptor->BufferPointer[2]);
DPRINT1("TransferDescriptor %lu Buffer Pointer 3 %x\n", Index, Descriptor->BufferPointer[3]);
DPRINT1("TransferDescriptor %lu Buffer Pointer 4 %x\n", Index, Descriptor->BufferPointer[4]);
Entry = Entry->Flink;
Index++;
}
}
//----------------------------------------------------------------------------------------
NTSTATUS
CUSBRequest::BuildBulkInterruptTransferQueueHead(
PQUEUE_HEAD * OutHead)
{
NTSTATUS Status;
PQUEUE_HEAD QueueHead;
PVOID Base;
ULONG ChainDescriptorLength;
PQUEUE_TRANSFER_DESCRIPTOR FirstDescriptor, LastDescriptor;
//
// Allocate the queue head
//
Status = CreateQueueHead(&QueueHead);
if (!NT_SUCCESS(Status))
{
//
// failed to allocate queue head
//
DPRINT1("[EHCI] Failed to create queue head\n");
return Status;
}
//
// sanity checks
//
PC_ASSERT(QueueHead);
PC_ASSERT(m_TransferBufferLength);
if (!m_Base)
{
//
// get virtual base of mdl
//
m_Base = MmGetSystemAddressForMdlSafe(m_TransferBufferMDL, NormalPagePriority);
}
//
// Increase the size of last transfer, 0 in case this is the first
//
Base = (PVOID)((ULONG_PTR)m_Base + m_TransferBufferLengthCompleted);
PC_ASSERT(m_EndpointDescriptor);
PC_ASSERT(Base);
//
// sanity check
//
ASSERT(m_EndpointDescriptor);
//
// use 4 * PAGE_SIZE at max for each new request
//
ULONG MaxTransferLength = min(4 * PAGE_SIZE, m_TransferBufferLength - m_TransferBufferLengthCompleted);
//
// build bulk transfer descriptor chain
//
Status = BuildTransferDescriptorChain(QueueHead,
Base,
MaxTransferLength,
InternalGetPidDirection(),
m_EndpointDescriptor->DataToggle,
&FirstDescriptor,
&LastDescriptor,
&m_EndpointDescriptor->DataToggle,
&ChainDescriptorLength);
if (!NT_SUCCESS(Status))
{
//
// failed to build transfer descriptor chain
//
DPRINT1("[EHCI] Failed to create descriptor chain\n");
m_DmaManager->Release(QueueHead, sizeof(QUEUE_HEAD));
return Status;
}
//
// move to next offset
//
m_TransferBufferLengthCompleted += ChainDescriptorLength;
//
// init queue head
//
QueueHead->EndPointCharacteristics.DeviceAddress = GetDeviceAddress();
QueueHead->EndPointCharacteristics.EndPointNumber = m_EndpointDescriptor->EndPointDescriptor.bEndpointAddress & 0x0F;
QueueHead->EndPointCharacteristics.MaximumPacketLength = m_EndpointDescriptor->EndPointDescriptor.wMaxPacketSize;
QueueHead->NextPointer = FirstDescriptor->PhysicalAddr;
QueueHead->CurrentLinkPointer = FirstDescriptor->PhysicalAddr;
QueueHead->AlternateNextPointer = TERMINATE_POINTER;
ASSERT(QueueHead->EndPointCharacteristics.DeviceAddress);
ASSERT(QueueHead->EndPointCharacteristics.EndPointNumber);
ASSERT(QueueHead->EndPointCharacteristics.MaximumPacketLength);
ASSERT(QueueHead->NextPointer);
//
// interrupt on last descriptor
//
LastDescriptor->Token.Bits.InterruptOnComplete = TRUE;
//
// store result
//
*OutHead = QueueHead;
//
// dump status
//
//DumpQueueHead(QueueHead);
//
// done
//
return STATUS_SUCCESS;
}
//----------------------------------------------------------------------------------------
NTSTATUS
STDMETHODCALLTYPE
CUSBRequest::CreateDescriptor(
PQUEUE_TRANSFER_DESCRIPTOR *OutDescriptor)
{
PQUEUE_TRANSFER_DESCRIPTOR Descriptor;
NTSTATUS Status;
PHYSICAL_ADDRESS TransferDescriptorPhysicalAddress;
//
// allocate descriptor
//
Status = m_DmaManager->Allocate(sizeof(QUEUE_TRANSFER_DESCRIPTOR), (PVOID*)&Descriptor, &TransferDescriptorPhysicalAddress);
if (!NT_SUCCESS(Status))
{
//
// failed to allocate transfer descriptor
//
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// initialize transfer descriptor
//
Descriptor->NextPointer = TERMINATE_POINTER;
Descriptor->AlternateNextPointer = TERMINATE_POINTER;
Descriptor->Token.Bits.DataToggle = TRUE;
Descriptor->Token.Bits.ErrorCounter = 0x03;
Descriptor->Token.Bits.Active = TRUE;
Descriptor->PhysicalAddr = TransferDescriptorPhysicalAddress.LowPart;
//
// store result
//
*OutDescriptor = Descriptor;
//
// done
//
return Status;
}
//----------------------------------------------------------------------------------------
NTSTATUS
CUSBRequest::CreateQueueHead(
PQUEUE_HEAD *OutQueueHead)
{
PQUEUE_HEAD QueueHead;
PHYSICAL_ADDRESS QueueHeadPhysicalAddress;
NTSTATUS Status;
//
// allocate queue head
//
Status = m_DmaManager->Allocate(sizeof(QUEUE_HEAD), (PVOID*)&QueueHead, &QueueHeadPhysicalAddress);
if (!NT_SUCCESS(Status))
{
//
// failed to allocate queue head
//
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// initialize queue head
//
QueueHead->HorizontalLinkPointer = TERMINATE_POINTER;
QueueHead->AlternateNextPointer = TERMINATE_POINTER;
QueueHead->NextPointer = TERMINATE_POINTER;
InitializeListHead(&QueueHead->TransferDescriptorListHead);
//
// 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 = 0x3;
//
// Get the Initial Data Toggle from the QEDT
//
QueueHead->EndPointCharacteristics.QEDTDataToggleControl = TRUE;
//
// FIXME: check if High Speed Device
//
QueueHead->EndPointCharacteristics.EndPointSpeed = QH_ENDPOINT_HIGHSPEED;
QueueHead->EndPointCapabilities.NumberOfTransactionPerFrame = 0x01;
QueueHead->Token.DWord = 0;
QueueHead->Token.Bits.InterruptOnComplete = FALSE;
//
// store address
//
QueueHead->PhysicalAddr = QueueHeadPhysicalAddress.LowPart;
//
// output queue head
//
*OutQueueHead = QueueHead;
//
// done
//
return STATUS_SUCCESS;
}
//----------------------------------------------------------------------------------------
UCHAR
STDMETHODCALLTYPE
CUSBRequest::GetDeviceAddress()
{
PIO_STACK_LOCATION IoStack;
PURB Urb;
PUSBDEVICE UsbDevice;
//
// check if there is an irp provided
//
if (!m_Irp)
{
//
// used provided address
//
return m_DeviceAddress;
}
//
// get current stack location
//
IoStack = IoGetCurrentIrpStackLocation(m_Irp);
//
// get contained urb
//
Urb = (PURB)IoStack->Parameters.Others.Argument1;
//
// check if there is a pipe handle provided
//
if (Urb->UrbHeader.UsbdDeviceHandle)
{
//
// there is a device handle provided
//
UsbDevice = (PUSBDEVICE)Urb->UrbHeader.UsbdDeviceHandle;
//
// return device address
//
return UsbDevice->GetDeviceAddress();
}
//
// no device handle provided, it is the host root bus
//
return 0;
}
//----------------------------------------------------------------------------------------
NTSTATUS
CUSBRequest::BuildSetupPacket()
{
NTSTATUS Status;
//
// allocate common buffer setup packet
//
Status = m_DmaManager->Allocate(sizeof(USB_DEFAULT_PIPE_SETUP_PACKET), (PVOID*)&m_DescriptorPacket, &m_DescriptorSetupPacket);
if (!NT_SUCCESS(Status))
{
//
// no memory
//
return Status;
}
if (m_SetupPacket)
{
//
// copy setup packet
//
RtlCopyMemory(m_DescriptorPacket, m_SetupPacket, sizeof(USB_DEFAULT_PIPE_SETUP_PACKET));
}
else
{
//
// build setup packet from urb
//
Status = BuildSetupPacketFromURB();
}
//
// done
//
return Status;
}
NTSTATUS
CUSBRequest::BuildSetupPacketFromURB()
{
PIO_STACK_LOCATION IoStack;
PURB Urb;
NTSTATUS Status = STATUS_NOT_IMPLEMENTED;
//
// sanity checks
//
PC_ASSERT(m_Irp);
PC_ASSERT(m_DescriptorPacket);
//
// get stack location
//
IoStack = IoGetCurrentIrpStackLocation(m_Irp);
//
// get urb
//
Urb = (PURB)IoStack->Parameters.Others.Argument1;
//
// zero descriptor packet
//
RtlZeroMemory(m_DescriptorPacket, sizeof(USB_DEFAULT_PIPE_SETUP_PACKET));
switch (Urb->UrbHeader.Function)
{
/* CLEAR FEATURE */
case URB_FUNCTION_CLEAR_FEATURE_TO_DEVICE:
case URB_FUNCTION_CLEAR_FEATURE_TO_INTERFACE:
case URB_FUNCTION_CLEAR_FEATURE_TO_ENDPOINT:
UNIMPLEMENTED;
break;
/* GET CONFIG */
case URB_FUNCTION_GET_CONFIGURATION:
m_DescriptorPacket->bRequest = USB_REQUEST_GET_CONFIGURATION;
m_DescriptorPacket->bmRequestType.B = 0x80;
m_DescriptorPacket->wLength = 1;
break;
/* GET DESCRIPTOR */
case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE:
m_DescriptorPacket->bRequest = USB_REQUEST_GET_DESCRIPTOR;
m_DescriptorPacket->wValue.LowByte = Urb->UrbControlDescriptorRequest.Index;
m_DescriptorPacket->wValue.HiByte = Urb->UrbControlDescriptorRequest.DescriptorType;
m_DescriptorPacket->wIndex.W = Urb->UrbControlDescriptorRequest.LanguageId;
m_DescriptorPacket->wLength = (USHORT)Urb->UrbControlDescriptorRequest.TransferBufferLength;
m_DescriptorPacket->bmRequestType.B = 0x80;
break;
/* GET INTERFACE */
case URB_FUNCTION_GET_INTERFACE:
m_DescriptorPacket->bRequest = USB_REQUEST_GET_CONFIGURATION;
m_DescriptorPacket->wIndex.W = Urb->UrbControlGetStatusRequest.Index;
m_DescriptorPacket->bmRequestType.B = 0x80;
m_DescriptorPacket->wLength = 1;
break;
/* GET STATUS */
case URB_FUNCTION_GET_STATUS_FROM_DEVICE:
m_DescriptorPacket->bRequest = USB_REQUEST_GET_STATUS;
ASSERT(Urb->UrbControlGetStatusRequest.Index == 0);
m_DescriptorPacket->wIndex.W = Urb->UrbControlGetStatusRequest.Index;
m_DescriptorPacket->bmRequestType.B = 0x80;
m_DescriptorPacket->wLength = 2;
break;
case URB_FUNCTION_GET_STATUS_FROM_INTERFACE:
m_DescriptorPacket->bRequest = USB_REQUEST_GET_STATUS;
ASSERT(Urb->UrbControlGetStatusRequest.Index != 0);
m_DescriptorPacket->wIndex.W = Urb->UrbControlGetStatusRequest.Index;
m_DescriptorPacket->bmRequestType.B = 0x81;
m_DescriptorPacket->wLength = 2;
break;
case URB_FUNCTION_GET_STATUS_FROM_ENDPOINT:
m_DescriptorPacket->bRequest = USB_REQUEST_GET_STATUS;
ASSERT(Urb->UrbControlGetStatusRequest.Index != 0);
m_DescriptorPacket->wIndex.W = Urb->UrbControlGetStatusRequest.Index;
m_DescriptorPacket->bmRequestType.B = 0x82;
m_DescriptorPacket->wLength = 2;
break;
/* SET ADDRESS */
/* SET CONFIG */
case URB_FUNCTION_SELECT_CONFIGURATION:
m_DescriptorPacket->bRequest = USB_REQUEST_SET_CONFIGURATION;
m_DescriptorPacket->wValue.W = Urb->UrbSelectConfiguration.ConfigurationDescriptor->bConfigurationValue;
m_DescriptorPacket->wIndex.W = 0;
m_DescriptorPacket->wLength = 0;
m_DescriptorPacket->bmRequestType.B = 0x00;
break;
/* SET DESCRIPTOR */
case URB_FUNCTION_SET_DESCRIPTOR_TO_DEVICE:
case URB_FUNCTION_SET_DESCRIPTOR_TO_INTERFACE:
case URB_FUNCTION_SET_DESCRIPTOR_TO_ENDPOINT:
UNIMPLEMENTED;
break;
/* SET FEATURE */
case URB_FUNCTION_SET_FEATURE_TO_DEVICE:
m_DescriptorPacket->bRequest = USB_REQUEST_SET_FEATURE;
ASSERT(Urb->UrbControlGetStatusRequest.Index == 0);
m_DescriptorPacket->wIndex.W = Urb->UrbControlGetStatusRequest.Index;
m_DescriptorPacket->bmRequestType.B = 0x80;
break;
case URB_FUNCTION_SET_FEATURE_TO_INTERFACE:
m_DescriptorPacket->bRequest = USB_REQUEST_SET_FEATURE;
ASSERT(Urb->UrbControlGetStatusRequest.Index == 0);
m_DescriptorPacket->wIndex.W = Urb->UrbControlGetStatusRequest.Index;
m_DescriptorPacket->bmRequestType.B = 0x81;
break;
case URB_FUNCTION_SET_FEATURE_TO_ENDPOINT:
m_DescriptorPacket->bRequest = USB_REQUEST_SET_FEATURE;
ASSERT(Urb->UrbControlGetStatusRequest.Index == 0);
m_DescriptorPacket->wIndex.W = Urb->UrbControlGetStatusRequest.Index;
m_DescriptorPacket->bmRequestType.B = 0x82;
break;
/* SET INTERFACE*/
case URB_FUNCTION_SELECT_INTERFACE:
m_DescriptorPacket->bRequest = USB_REQUEST_SET_INTERFACE;
m_DescriptorPacket->wValue.W = Urb->UrbSelectInterface.Interface.AlternateSetting;
m_DescriptorPacket->wIndex.W = Urb->UrbSelectInterface.Interface.InterfaceNumber;
m_DescriptorPacket->wLength = 0;
m_DescriptorPacket->bmRequestType.B = 0x01;
break;
/* SYNC FRAME */
case URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL:
UNIMPLEMENTED;
break;
default:
UNIMPLEMENTED;
break;
}
return Status;
}
//----------------------------------------------------------------------------------------
VOID
STDMETHODCALLTYPE
CUSBRequest::GetResultStatus(
OUT OPTIONAL NTSTATUS * NtStatusCode,
OUT OPTIONAL PULONG UrbStatusCode)
{
//
// sanity check
//
PC_ASSERT(m_CompletionEvent);
//
// wait for the operation to complete
//
KeWaitForSingleObject(m_CompletionEvent, Executive, KernelMode, FALSE, NULL);
//
// copy status
//
if (NtStatusCode)
{
*NtStatusCode = m_NtStatusCode;
}
//
// copy urb status
//
if (UrbStatusCode)
{
*UrbStatusCode = m_UrbStatusCode;
}
}
//-----------------------------------------------------------------------------------------
BOOLEAN
STDMETHODCALLTYPE
CUSBRequest::ShouldReleaseRequestAfterCompletion()
{
if (m_Irp)
{
//
// the request is completed, release it
//
return TRUE;
}
else
{
//
// created with an setup packet, don't release
//
return FALSE;
}
}
//-----------------------------------------------------------------------------------------
VOID
STDMETHODCALLTYPE
CUSBRequest::FreeQueueHead(
IN struct _QUEUE_HEAD * QueueHead)
{
PLIST_ENTRY Entry;
PQUEUE_TRANSFER_DESCRIPTOR Descriptor;
//
// sanity checks
//
ASSERT(m_DmaManager);
ASSERT(QueueHead);
ASSERT(!IsListEmpty(&QueueHead->TransferDescriptorListHead));
do
{
//
// get transfer descriptors
//
Entry = RemoveHeadList(&QueueHead->TransferDescriptorListHead);
ASSERT(Entry);
//
// obtain descriptor from entry
//
Descriptor = (PQUEUE_TRANSFER_DESCRIPTOR)CONTAINING_RECORD(Entry, QUEUE_TRANSFER_DESCRIPTOR, DescriptorEntry);
ASSERT(Descriptor);
//
// add transfer count
//
m_TotalBytesTransferred += (Descriptor->TotalBytesToTransfer - Descriptor->Token.Bits.TotalBytesToTransfer);
DPRINT("TotalBytes Transferred in Descriptor %p Phys Addr %x TotalBytesSoftware %lu Length %lu\n", Descriptor, Descriptor->PhysicalAddr, Descriptor->TotalBytesToTransfer, Descriptor->TotalBytesToTransfer - Descriptor->Token.Bits.TotalBytesToTransfer);
//
// release transfer descriptors
//
m_DmaManager->Release(Descriptor, sizeof(QUEUE_TRANSFER_DESCRIPTOR));
}while(!IsListEmpty(&QueueHead->TransferDescriptorListHead));
if (m_DescriptorPacket)
{
//
// release packet descriptor
//
m_DmaManager->Release(m_DescriptorPacket, sizeof(USB_DEFAULT_PIPE_SETUP_PACKET));
}
//
// release queue head
//
m_DmaManager->Release(QueueHead, sizeof(QUEUE_HEAD));
//
// nullify pointers
//
m_QueueHead = 0;
m_DescriptorPacket = 0;
}
//-----------------------------------------------------------------------------------------
BOOLEAN
STDMETHODCALLTYPE
CUSBRequest::IsQueueHeadComplete(
struct _QUEUE_HEAD * QueueHead)
{
PLIST_ENTRY Entry;
PQUEUE_TRANSFER_DESCRIPTOR Descriptor;
//
// first check - is the queue head currently active
//
if (QueueHead->Token.Bits.Active)
{
//
// queue head is active (currently processed)
//
return FALSE;
}
if (QueueHead->Token.Bits.Halted)
{
//
// error occured
//
DPRINT1("Found halted queue head %p\n", QueueHead);
DumpQueueHead(QueueHead);
//ASSERT(FALSE);
return TRUE;
}
//
// loop list and see if there are any active descriptors
//
Entry = QueueHead->TransferDescriptorListHead.Flink;
while(Entry != &QueueHead->TransferDescriptorListHead)
{
//
// obtain descriptor from entry
//
Descriptor = (PQUEUE_TRANSFER_DESCRIPTOR)CONTAINING_RECORD(Entry, QUEUE_TRANSFER_DESCRIPTOR, DescriptorEntry);
ASSERT(Descriptor);
if (Descriptor->Token.Bits.Active)
{
//
// descriptor is still active
//
return FALSE;
}
//
// move to next entry
//
Entry = Entry->Flink;
}
DPRINT("QueueHead %p Addr %x is complete\n", QueueHead, QueueHead->PhysicalAddr);
//
// no active descriptors found, queue head is finished
//
return TRUE;
}
//-----------------------------------------------------------------------------------------
ULONG
CUSBRequest::InternalCalculateTransferLength()
{
if (!m_Irp)
{
//
// FIXME: get length for control request
//
return m_TransferBufferLength;
}
//
// sanity check
//
ASSERT(m_EndpointDescriptor);
if (USB_ENDPOINT_DIRECTION_IN(m_EndpointDescriptor->EndPointDescriptor.bEndpointAddress))
{
//
// bulk in request
// HACK: Properly determine transfer length
//
return m_TransferBufferLength;//m_TotalBytesTransferred;
}
//
// bulk out transfer
//
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
InternalCreateUSBRequest(
PUSBREQUEST *OutRequest)
{
PUSBREQUEST This;
//
// allocate requests
//
This = new(NonPagedPool, TAG_USBEHCI) CUSBRequest(0);
if (!This)
{
//
// failed to allocate
//
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// add reference count
//
This->AddRef();
//
// return result
//
*OutRequest = (PUSBREQUEST)This;
//
// done
//
return STATUS_SUCCESS;
}