reactos/drivers/usb/usbehci_new/usb_request.cpp
Johannes Anderwald bca978808b [USBEHCI_NEW]
- Silence usb bulk control traces

svn path=/branches/usb-bringup/; revision=51507
2011-04-30 18:46:10 +00:00

1620 lines
43 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)
*/
#define INITGUID
#include "usbehci.h"
#include "hardware.h"
class CUSBRequest : public IUSBRequest
{
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
virtual NTSTATUS InitializeWithSetupPacket(IN PDMAMEMORYMANAGER DmaManager, IN PUSB_DEFAULT_PIPE_SETUP_PACKET SetupPacket, IN UCHAR DeviceAddress, IN OPTIONAL PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor, IN OUT ULONG TransferBufferLength, IN OUT PMDL TransferBuffer);
virtual NTSTATUS InitializeWithIrp(IN PDMAMEMORYMANAGER DmaManager, IN OUT PIRP Irp);
virtual VOID CompletionCallback(IN NTSTATUS NtStatusCode, IN ULONG UrbStatusCode, IN struct _QUEUE_HEAD *QueueHead);
virtual VOID CancelCallback(IN NTSTATUS NtStatusCode, IN struct _QUEUE_HEAD *QueueHead);
virtual NTSTATUS GetQueueHead(struct _QUEUE_HEAD ** OutHead);
virtual BOOLEAN IsRequestComplete();
virtual ULONG GetTransferType();
virtual VOID GetResultStatus(OUT OPTIONAL NTSTATUS *NtStatusCode, OUT OPTIONAL PULONG UrbStatusCode);
virtual BOOLEAN IsRequestInitialized();
virtual BOOLEAN ShouldReleaseRequestAfterCompletion();
virtual VOID FreeQueueHead(struct _QUEUE_HEAD * QueueHead);
virtual VOID GetTransferBuffer(OUT PMDL * OutMDL, OUT PULONG TransferLength);
virtual BOOLEAN IsQueueHeadComplete(struct _QUEUE_HEAD * QueueHead);
// local functions
ULONG InternalGetTransferType();
UCHAR InternalGetPidDirection();
NTSTATUS BuildControlTransferQueueHead(PQUEUE_HEAD * OutHead);
NTSTATUS BuildBulkTransferQueueHead(PQUEUE_HEAD * OutHead);
NTSTATUS CreateDescriptor(PQUEUE_TRANSFER_DESCRIPTOR *OutDescriptor);
NTSTATUS CreateQueueHead(PQUEUE_HEAD *OutQueueHead);
UCHAR GetDeviceAddress();
NTSTATUS BuildSetupPacket();
NTSTATUS BuildSetupPacketFromURB();
ULONG InternalCalculateTransferLength();
// 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;
//
// 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_DESCRIPTOR m_EndpointDescriptor;
//
// DMA queue head
//
PQUEUE_HEAD m_QueueHead;
//
// DMA transfer descriptors linked to the queue head
//
PQUEUE_TRANSFER_DESCRIPTOR m_TransferDescriptors[3];
//
// 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;
};
//----------------------------------------------------------------------------------------
NTSTATUS
STDMETHODCALLTYPE
CUSBRequest::QueryInterface(
IN REFIID refiid,
OUT PVOID* Output)
{
return STATUS_UNSUCCESSFUL;
}
//----------------------------------------------------------------------------------------
NTSTATUS
CUSBRequest::InitializeWithSetupPacket(
IN PDMAMEMORYMANAGER DmaManager,
IN PUSB_DEFAULT_PIPE_SETUP_PACKET SetupPacket,
IN UCHAR DeviceAddress,
IN OPTIONAL PUSB_ENDPOINT_DESCRIPTOR 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 = DeviceAddress;
m_EndpointDescriptor = EndpointDescriptor;
//
// 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
CUSBRequest::InitializeWithIrp(
IN PDMAMEMORYMANAGER DmaManager,
IN OUT PIRP Irp)
{
PIO_STACK_LOCATION IoStack;
PURB Urb;
//
// sanity checks
//
PC_ASSERT(DmaManager);
PC_ASSERT(Irp);
m_DmaManager = DmaManager;
//
// 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);
}
else
{
m_TransferBufferMDL = Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL;
}
//
// save buffer length
//
m_TransferBufferLength = Urb->UrbBulkOrInterruptTransfer.TransferBufferLength;
//
// get endpoint descriptor
//
m_EndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR)Urb->UrbBulkOrInterruptTransfer.PipeHandle;
}
break;
}
default:
DPRINT1("URB Function: not supported %x\n", Urb->UrbHeader.Function);
PC_ASSERT(FALSE);
}
//
// done
//
return STATUS_SUCCESS;
}
//----------------------------------------------------------------------------------------
VOID
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 successfull
//
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);
}
}
//----------------------------------------------------------------------------------------
VOID
CUSBRequest::CancelCallback(
IN NTSTATUS NtStatusCode,
IN struct _QUEUE_HEAD *QueueHead)
{
PIO_STACK_LOCATION IoStack;
PURB Urb;
//
// FIXME: support linked queue heads
//
//
// store cancelleation code
//
m_NtStatusCode = NtStatusCode;
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 = USBD_STATUS_CANCELED;
Urb->UrbHeader.Length = 0;
//
// 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
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_BULK:
Status = BuildBulkTransferQueueHead(OutHead);
break;
case USB_ENDPOINT_TYPE_INTERRUPT:
DPRINT1("USB_ENDPOINT_TYPE_INTERRUPT not implemented\n");
Status = STATUS_NOT_IMPLEMENTED;
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
CUSBRequest::IsRequestComplete()
{
//
// FIXME: check if request was split
//
return TRUE;
}
//----------------------------------------------------------------------------------------
ULONG
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->bmAttributes & USB_ENDPOINT_TYPE_MASK);
}
else
{
//
// initialized with setup packet, must be a control transfer
//
TransferType = USB_ENDPOINT_TYPE_CONTROL;
}
//
// done
//
return TransferType;
}
UCHAR
CUSBRequest::InternalGetPidDirection()
{
ASSERT(m_Irp);
ASSERT(m_EndpointDescriptor);
//
// end point is defined in the low byte of bEndpointAddress
//
return (m_EndpointDescriptor->bEndpointAddress & USB_ENDPOINT_DIRECTION_MASK) >> 7;
}
//----------------------------------------------------------------------------------------
NTSTATUS
CUSBRequest::BuildControlTransferQueueHead(
PQUEUE_HEAD * OutHead)
{
NTSTATUS Status;
ULONG NumTransferDescriptors, Index;
PQUEUE_HEAD QueueHead;
//
// first allocate the queue head
//
Status = CreateQueueHead(&QueueHead);
if (!NT_SUCCESS(Status))
{
//
// failed to allocate queue head
//
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// sanity check
//
PC_ASSERT(QueueHead);
//
// create setup packet
//
Status = BuildSetupPacket();
if (!NT_SUCCESS(Status))
{
//
// failed to allocate setup packet
//
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// calculate num of transfer descriptors
//
NumTransferDescriptors = m_TransferBufferMDL != 0 ? 3 : 2;
//
// allocate transfer descriptors
//
for(Index = 0; Index < NumTransferDescriptors; Index++)
{
//
// allocate transfer descriptor
//
Status = CreateDescriptor(&m_TransferDescriptors[Index]);
if (!NT_SUCCESS(Status))
{
//
// failed to allocate transfer descriptor
//
return Status;
}
}
//
// now initialize the queue head
//
QueueHead->EndPointCharacteristics.DeviceAddress = GetDeviceAddress();
if (m_EndpointDescriptor)
{
//
// set endpoint address and max packet length
//
QueueHead->EndPointCharacteristics.EndPointNumber = m_EndpointDescriptor->bEndpointAddress & 0x0F;
QueueHead->EndPointCharacteristics.MaximumPacketLength = m_EndpointDescriptor->wMaxPacketSize;
}
QueueHead->Token.Bits.DataToggle = TRUE;
//
// setup descriptors
//
m_TransferDescriptors[0]->Token.Bits.PIDCode = PID_CODE_SETUP_TOKEN;
m_TransferDescriptors[0]->Token.Bits.TotalBytesToTransfer = sizeof(USB_DEFAULT_PIPE_SETUP_PACKET);
m_TransferDescriptors[0]->Token.Bits.DataToggle = FALSE;
if (m_TransferBufferMDL)
{
//
// setup in descriptor
//
m_TransferDescriptors[1]->Token.Bits.PIDCode = PID_CODE_IN_TOKEN;
m_TransferDescriptors[1]->Token.Bits.TotalBytesToTransfer = m_TransferBufferLength;
//
// FIXME: check if the request spawns over a page -> fill other members
//
PC_ASSERT(m_TransferBufferLength <= PAGE_SIZE);
m_TransferDescriptors[1]->BufferPointer[0] = MmGetPhysicalAddress(MmGetMdlVirtualAddress(m_TransferBufferMDL)).LowPart;
//
// setup out descriptor
//
m_TransferDescriptors[2]->Token.Bits.PIDCode = PID_CODE_OUT_TOKEN;
m_TransferDescriptors[2]->Token.Bits.TotalBytesToTransfer = 0;
//
// link descriptors
//
m_TransferDescriptors[0]->NextPointer = m_TransferDescriptors[1]->PhysicalAddr;
//
// special case, setup alternative next descriptor in case of error
// HAIKU links to dead descriptor
//
m_TransferDescriptors[0]->AlternateNextPointer = m_TransferDescriptors[2]->PhysicalAddr;
m_TransferDescriptors[1]->NextPointer = m_TransferDescriptors[2]->PhysicalAddr;
m_TransferDescriptors[1]->AlternateNextPointer = m_TransferDescriptors[2]->PhysicalAddr;
//
// interrupt on completion
//
m_TransferDescriptors[2]->Token.Bits.InterruptOnComplete = TRUE;
}
else
{
//
// no buffer, setup in descriptor
//
m_TransferDescriptors[1]->Token.Bits.PIDCode = PID_CODE_IN_TOKEN;
m_TransferDescriptors[1]->Token.Bits.TotalBytesToTransfer = 0;
//
// link descriptors
//
m_TransferDescriptors[0]->NextPointer = m_TransferDescriptors[1]->PhysicalAddr;
m_TransferDescriptors[0]->AlternateNextPointer = m_TransferDescriptors[1]->PhysicalAddr;
//
// interrupt on completion
//
m_TransferDescriptors[1]->Token.Bits.InterruptOnComplete = TRUE;
}
//
// link setup packet into buffer - Physical Address!!!
//
m_TransferDescriptors[0]->BufferPointer[0] = (ULONG)PtrToUlong(m_DescriptorSetupPacket.LowPart);
//
// link transfer descriptors to queue head
//
QueueHead->NextPointer = m_TransferDescriptors[0]->PhysicalAddr;
//
// store result
//
*OutHead = QueueHead;
//
// done
//
return STATUS_SUCCESS;
}
//----------------------------------------------------------------------------------------
NTSTATUS
CUSBRequest::BuildBulkTransferQueueHead(
PQUEUE_HEAD * OutHead)
{
NTSTATUS Status;
PQUEUE_HEAD QueueHead;
ULONG TransferDescriptorCount, Index;
ULONG BytesAvailable, BufferIndex;
PVOID Base;
ULONG PageOffset;
//
// Allocate the queue head
//
Status = CreateQueueHead(&QueueHead);
if (!NT_SUCCESS(Status))
{
//
// failed to allocate queue heads
//
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// sanity checks
//
PC_ASSERT(QueueHead);
//
// FIXME: support more than one descriptor
//
PC_ASSERT(m_TransferBufferLength < PAGE_SIZE * 5);
PC_ASSERT(m_TransferBufferLength);
TransferDescriptorCount = 1;
//
// get virtual base of mdl
//
Base = MmGetSystemAddressForMdlSafe(m_TransferBufferMDL, NormalPagePriority);
BytesAvailable = m_TransferBufferLength;
PC_ASSERT(m_EndpointDescriptor);
PC_ASSERT(Base);
DPRINT("EndPointAddress %x\n", m_EndpointDescriptor->bEndpointAddress);
DPRINT("EndPointDirection %x\n", USB_ENDPOINT_DIRECTION_IN(m_EndpointDescriptor->bEndpointAddress));
DPRINT("Request %p Base Address %p TransferBytesLength %lu MDL %p\n", this, Base, BytesAvailable, m_TransferBufferMDL);
DPRINT("InternalGetPidDirection() %d EndPointAddress %x\n", InternalGetPidDirection(), m_EndpointDescriptor->bEndpointAddress & 0x0F);
DPRINT("Irp %p QueueHead %p\n", m_Irp, QueueHead);
//PC_ASSERT(InternalGetPidDirection() == USB_ENDPOINT_DIRECTION_IN(m_EndpointDescriptor->bEndpointAddress));
//
// Allocated transfer descriptors
//
for (Index = 0; Index < TransferDescriptorCount; Index++)
{
Status = CreateDescriptor(&m_TransferDescriptors[Index]);
if (!NT_SUCCESS(Status))
{
//
// Failed to allocate transfer descriptors
//
//
// Free QueueHead
//
FreeQueueHead(QueueHead);
//
// Free Descriptors
// FIXME: Implement FreeDescriptors
//
return Status;
}
//
// sanity check
//
PC_ASSERT(BytesAvailable);
//
// now setup transfer buffers
//
for(BufferIndex = 0; BufferIndex < 5; BufferIndex++)
{
//
// setup buffer
//
if (BufferIndex == 0)
{
//
// use physical address
//
m_TransferDescriptors[Index]->BufferPointer[0] = MmGetPhysicalAddress(Base).LowPart;
//
// get offset within page
//
PageOffset = BYTE_OFFSET(m_TransferDescriptors[Index]->BufferPointer[0]);
//
// check if request fills another page
//
if (PageOffset + BytesAvailable > PAGE_SIZE)
{
//
// move to next page
//
Base = (PVOID)ROUND_TO_PAGES(Base);
//
// increment transfer bytes
//
m_TransferDescriptors[Index]->Token.Bits.TotalBytesToTransfer = PAGE_SIZE - PageOffset;
//
// decrement available byte count
//
BytesAvailable -= m_TransferDescriptors[Index]->Token.Bits.TotalBytesToTransfer;
DPRINT("TransferDescriptor %p BufferPointer %p BufferIndex %lu TotalBytes %lu Remaining %lu\n", m_TransferDescriptors[Index], m_TransferDescriptors[Index]->BufferPointer[BufferIndex],
BufferIndex, m_TransferDescriptors[Index]->Token.Bits.TotalBytesToTransfer, BytesAvailable);
}
else
{
//
// request ends on the first buffer page
//
m_TransferDescriptors[Index]->Token.Bits.TotalBytesToTransfer = BytesAvailable;
BytesAvailable = 0;
DPRINT("TransferDescriptor %p BufferPointer %p BufferIndex %lu TotalBytes %lu Remaining %lu\n", m_TransferDescriptors[Index], m_TransferDescriptors[Index]->BufferPointer[BufferIndex],
BufferIndex, m_TransferDescriptors[Index]->Token.Bits.TotalBytesToTransfer, BytesAvailable);
break;
}
}
else
{
//
// the following pages always start on byte zero of each page
//
PC_ASSERT(((ULONG_PTR)Base & (PAGE_SIZE-1)) == 0);
if (BytesAvailable >= PAGE_SIZE)
{
//
// store address
//
m_TransferDescriptors[Index]->BufferPointer[BufferIndex] = MmGetPhysicalAddress(Base).LowPart;
//
// move to next page
//
Base = (PVOID)((ULONG_PTR)Base + PAGE_SIZE);
//
// increment transfer descriptor bytes
//
m_TransferDescriptors[Index]->Token.Bits.TotalBytesToTransfer += PAGE_SIZE;
//
// decrement available byte count
//
BytesAvailable -= PAGE_SIZE;
DPRINT("TransferDescriptor %p BufferPointer %p BufferIndex %lu TotalBytes %lu Remaining %lu\n", m_TransferDescriptors[Index], m_TransferDescriptors[Index]->BufferPointer[BufferIndex],
BufferIndex, m_TransferDescriptors[Index]->Token.Bits.TotalBytesToTransfer, BytesAvailable);
}
else
{
PC_ASSERT(BytesAvailable);
//
// store address
//
m_TransferDescriptors[Index]->BufferPointer[BufferIndex] = MmGetPhysicalAddress(Base).LowPart;
//
// increment transfer descriptor bytes
//
m_TransferDescriptors[Index]->Token.Bits.TotalBytesToTransfer += BytesAvailable;
//
// decrement available byte count
//
BytesAvailable -= BytesAvailable;
//
// done
//
DPRINT("TransferDescriptor %p BufferPointer %p BufferIndex %lu TotalBytes %lu Remaining %lu\n", m_TransferDescriptors[Index], m_TransferDescriptors[Index]->BufferPointer[BufferIndex],
BufferIndex, m_TransferDescriptors[Index]->Token.Bits.TotalBytesToTransfer, BytesAvailable);
break;
}
}
}
//
// store transfer bytes of descriptor
//
m_TransferDescriptors[Index]->TotalBytesToTransfer = m_TransferDescriptors[Index]->Token.Bits.TotalBytesToTransfer;
//
// Go ahead and link descriptors
//
if (Index > 0)
{
m_TransferDescriptors[Index - 1]->NextPointer = m_TransferDescriptors[Index]->PhysicalAddr;
}
//
// setup direction
//
m_TransferDescriptors[Index]->Token.Bits.PIDCode = InternalGetPidDirection();
//
// FIXME: performance penality?
//
m_TransferDescriptors[Index]->Token.Bits.InterruptOnComplete = TRUE;
//
// FIXME need dead queue transfer descriptor?
//
}
//
// all bytes should have been consumed
//
PC_ASSERT(BytesAvailable == 0);
//
// Initialize the QueueHead
//
QueueHead->EndPointCharacteristics.DeviceAddress = GetDeviceAddress();
if (m_EndpointDescriptor)
{
//
// Set endpoint address and max packet length
//
QueueHead->EndPointCharacteristics.EndPointNumber = m_EndpointDescriptor->bEndpointAddress & 0x0F;
QueueHead->EndPointCharacteristics.MaximumPacketLength = m_EndpointDescriptor->wMaxPacketSize;
}
QueueHead->Token.Bits.DataToggle = TRUE;
//
// link descriptor with queue head
//
QueueHead->NextPointer = m_TransferDescriptors[0]->PhysicalAddr;
//
// store result
//
*OutHead = QueueHead;
//
// done
//
return STATUS_SUCCESS;
}
//----------------------------------------------------------------------------------------
NTSTATUS
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;
//
// 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;
//
// FIXME check if that is really needed
//
QueueHead->PhysicalAddr = QueueHeadPhysicalAddress.LowPart;
//
// output queue head
//
*OutQueueHead = QueueHead;
//
// done
//
return STATUS_SUCCESS;
}
//----------------------------------------------------------------------------------------
UCHAR
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 = 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
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
CUSBRequest::IsRequestInitialized()
{
if (m_Irp || m_SetupPacket)
{
//
// request is initialized
//
return TRUE;
}
//
// request is not initialized
//
return FALSE;
}
//-----------------------------------------------------------------------------------------
BOOLEAN
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
CUSBRequest::FreeQueueHead(
IN struct _QUEUE_HEAD * QueueHead)
{
//
// FIXME: support chained queue heads
//
PC_ASSERT(QueueHead == m_QueueHead);
//
// release queue head
//
m_DmaManager->Release(QueueHead, sizeof(QUEUE_HEAD));
//
// nullify pointer
//
m_QueueHead = 0;
//
// release transfer descriptors
//
if (m_TransferDescriptors[0])
{
//
// release transfer descriptors
//
m_DmaManager->Release(m_TransferDescriptors[0], sizeof(QUEUE_TRANSFER_DESCRIPTOR));
m_TransferDescriptors[0] = 0;
}
if (m_TransferDescriptors[1])
{
//
// release transfer descriptors
//
m_DmaManager->Release(m_TransferDescriptors[1], sizeof(QUEUE_TRANSFER_DESCRIPTOR));
m_TransferDescriptors[1] = 0;
}
if (m_TransferDescriptors[2])
{
//
// release transfer descriptors
//
m_DmaManager->Release(m_TransferDescriptors[2], sizeof(QUEUE_TRANSFER_DESCRIPTOR));
m_TransferDescriptors[2] = 0;
}
if (m_DescriptorPacket)
{
//
// release packet descriptor
//
m_DmaManager->Release(m_DescriptorPacket, sizeof(USB_DEFAULT_PIPE_SETUP_PACKET));
m_DescriptorPacket = 0;
}
}
//-----------------------------------------------------------------------------------------
BOOLEAN
CUSBRequest::IsQueueHeadComplete(
struct _QUEUE_HEAD * QueueHead)
{
ULONG Index;
//
// first check - is the queue head currently active
//
if (QueueHead->Token.Bits.Active)
{
//
// queue head is active (currently processed)
//
return FALSE;
}
//
// FIXME: support chained queue heads
//
for(Index = 0; Index < 3; Index++)
{
//
// check transfer descriptors for completion
//
if (m_TransferDescriptors[Index])
{
//
// check for serious error
//
//PC_ASSERT(m_TransferDescriptors[Index]->Token.Bits.Halted == 0);
//
// the transfer descriptor should be in the same state as the queue head
//
//PC_ASSERT(m_TransferDescriptors[Index]->Token.Bits.Active == 0);
}
}
return TRUE;
}
//-----------------------------------------------------------------------------------------
VOID
CUSBRequest::GetTransferBuffer(
OUT PMDL * OutMDL,
OUT PULONG TransferLength)
{
// sanity checks
PC_ASSERT(OutMDL);
PC_ASSERT(TransferLength);
*OutMDL = m_TransferBufferMDL;
*TransferLength = m_TransferBufferLength;
}
//-----------------------------------------------------------------------------------------
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->bEndpointAddress))
{
//
// bulk in request
//
return m_TransferDescriptors[0]->TotalBytesToTransfer - m_TransferDescriptors[0]->Token.Bits.TotalBytesToTransfer;
}
//
// bulk out transfer
//
return m_TransferBufferLength;
}
//-----------------------------------------------------------------------------------------
NTSTATUS
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;
}