reactos/drivers/usb/usbuhci/usb_request.cpp

1447 lines
36 KiB
C++

/*
* PROJECT: ReactOS Universal Serial Bus Host Controller Interface
* LICENSE: GPL - See COPYING in the top level directory
* FILE: drivers/usb/usbuhci/usb_request.cpp
* PURPOSE: USB UHCI device driver.
* PROGRAMMERS:
* Michael Martin (michael.martin@reactos.org)
* Johannes Anderwald (johannes.anderwald@reactos.org)
*/
#include "usbuhci.h"
#define NDEBUG
#include <debug.h>
class CUSBRequest : public IUHCIRequest
{
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_IUSBREQUEST
IMP_IUHCIREQUEST
// local functions
ULONG InternalGetTransferType();
UCHAR InternalGetPidDirection();
UCHAR GetDeviceAddress();
NTSTATUS BuildSetupPacket();
NTSTATUS BuildSetupPacketFromURB();
UCHAR GetEndpointAddress();
USHORT GetMaxPacketSize();
NTSTATUS CreateDescriptor(PUHCI_TRANSFER_DESCRIPTOR *OutDescriptor, IN UCHAR PidCode, ULONG BufferLength);
NTSTATUS BuildControlTransferDescriptor(IN PUHCI_QUEUE_HEAD * OutQueueHead);
NTSTATUS BuildBulkInterruptTransferDescriptor(IN PUHCI_QUEUE_HEAD * OutQueueHead);
NTSTATUS BuildQueueHead(OUT PUHCI_QUEUE_HEAD *OutQueueHead);
VOID FreeDescriptor(IN PUHCI_TRANSFER_DESCRIPTOR Descriptor);
NTSTATUS BuildTransferDescriptorChain(IN PVOID TransferBuffer, IN ULONG TransferBufferLength, IN UCHAR PidCode, IN UCHAR InitialDataToggle, OUT PUHCI_TRANSFER_DESCRIPTOR * OutFirstDescriptor, OUT PUHCI_TRANSFER_DESCRIPTOR * OutLastDescriptor, OUT PULONG OutTransferBufferOffset, OUT PUCHAR OutDataToggle);
// 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;
//
// 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;
//
// store device speed
//
USB_DEVICE_SPEED m_DeviceSpeed;
// base
PVOID m_Base;
};
//----------------------------------------------------------------------------------------
CUSBRequest::CUSBRequest(IUnknown *OuterUnknown) :
m_CompletionEvent(NULL)
{
UNREFERENCED_PARAMETER(OuterUnknown);
}
//----------------------------------------------------------------------------------------
CUSBRequest::~CUSBRequest()
{
if (m_CompletionEvent != NULL)
{
ExFreePoolWithTag(m_CompletionEvent, TAG_USBUHCI);
}
}
//----------------------------------------------------------------------------------------
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 PUSBDEVICE Device,
IN OPTIONAL struct _USB_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_EndpointDescriptor = EndpointDescriptor;
m_TotalBytesTransferred = 0;
m_DeviceSpeed = Device->GetSpeed();
//
// Set Length Completed to 0
//
m_TransferBufferLengthCompleted = 0;
//
// allocate completion event
//
m_CompletionEvent = (PKEVENT)ExAllocatePoolWithTag(NonPagedPool, sizeof(KEVENT), TAG_USBUHCI);
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 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_DeviceSpeed = 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)
{
case URB_FUNCTION_ISOCH_TRANSFER:
{
//
// there must be at least one packet
//
ASSERT(Urb->UrbIsochronousTransfer.NumberOfPackets);
//
// is there data to be transferred
//
if (Urb->UrbIsochronousTransfer.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
{
//
// use provided mdl
//
m_TransferBufferMDL = Urb->UrbIsochronousTransfer.TransferBufferMDL;
}
}
//
// save buffer length
//
m_TransferBufferLength = Urb->UrbIsochronousTransfer.TransferBufferLength;
//
// Set Length Completed to 0
//
m_TransferBufferLengthCompleted = 0;
//
// get endpoint descriptor
//
m_EndpointDescriptor = (PUSB_ENDPOINT)Urb->UrbIsochronousTransfer.PipeHandle;
//
// completed initialization
//
break;
}
//
// 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);
PC_ASSERT(FALSE);
}
//
// done
//
return STATUS_SUCCESS;
}
//----------------------------------------------------------------------------------------
BOOLEAN
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
CUSBRequest::GetTransferType()
{
//
// call internal implementation
//
return InternalGetTransferType();
}
USHORT
CUSBRequest::GetMaxPacketSize()
{
if (!m_EndpointDescriptor)
{
//
// control request
//
return 0;
}
ASSERT(m_Irp);
ASSERT(m_EndpointDescriptor);
//
// return max packet size
//
return m_EndpointDescriptor->EndPointDescriptor.wMaxPacketSize;
}
UCHAR
CUSBRequest::GetInterval()
{
ASSERT(m_EndpointDescriptor);
ASSERT((m_EndpointDescriptor->EndPointDescriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_INTERRUPT);
//
// return interrupt interval
//
return m_EndpointDescriptor->EndPointDescriptor.bInterval;
}
UCHAR
CUSBRequest::GetEndpointAddress()
{
if (!m_EndpointDescriptor)
{
//
// control request
//
return 0;
}
ASSERT(m_Irp);
ASSERT(m_EndpointDescriptor);
//
// endpoint number is between 1-15
//
return (m_EndpointDescriptor->EndPointDescriptor.bEndpointAddress & 0xF);
}
//----------------------------------------------------------------------------------------
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;
}
//
// 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_SetupPacket);
return (m_SetupPacket->bmRequestType.B >> 7);
}
}
//----------------------------------------------------------------------------------------
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::GetEndpointDescriptor(
struct _UHCI_QUEUE_HEAD ** OutQueueHead)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
ULONG TransferType;
// get transfer type
TransferType = InternalGetTransferType();
if (TransferType == USB_ENDPOINT_TYPE_CONTROL)
{
//
// build queue head
//
Status = BuildControlTransferDescriptor(OutQueueHead);
}
else if (TransferType == USB_ENDPOINT_TYPE_INTERRUPT || TransferType == USB_ENDPOINT_TYPE_BULK)
{
//
// build queue head
//
Status = BuildBulkInterruptTransferDescriptor(OutQueueHead);
}
if (!NT_SUCCESS(Status))
{
//
// failed
//
return Status;
}
//
// store result
//
(*OutQueueHead)->Request = PVOID(this);
//
// done
//
return STATUS_SUCCESS;
}
//----------------------------------------------------------------------------------------
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;
}
}
//-----------------------------------------------------------------------------------------
NTSTATUS
CUSBRequest::CreateDescriptor(
OUT PUHCI_TRANSFER_DESCRIPTOR *OutDescriptor,
IN UCHAR PidCode,
ULONG BufferLength)
{
PUHCI_TRANSFER_DESCRIPTOR Descriptor;
PHYSICAL_ADDRESS Address;
NTSTATUS Status;
//
// allocate descriptor
//
Status = m_DmaManager->Allocate(sizeof(UHCI_TRANSFER_DESCRIPTOR), (PVOID*)&Descriptor, &Address);
if (!NT_SUCCESS(Status))
{
DPRINT1("[USBUHCI] Failed to allocate descriptor\n");
return Status;
}
//
// init descriptor
//
Descriptor->PhysicalAddress = Address.LowPart;
Descriptor->Status = TD_STATUS_ACTIVE;
if (InternalGetTransferType() == USB_ENDPOINT_TYPE_ISOCHRONOUS)
{
//
// isochronous transfer descriptor
//
Descriptor->Status |= TD_CONTROL_ISOCHRONOUS;
}
else
{
//
// error count
//
Descriptor->Status |= TD_CONTROL_3_ERRORS;
if (PidCode == TD_TOKEN_IN && (InternalGetTransferType() != USB_ENDPOINT_TYPE_CONTROL))
{
//
// enable short packet detect for bulk & interrupt
//
Descriptor->Status |= TD_CONTROL_SPD;
}
}
//
// is it low speed device
//
if (m_DeviceSpeed == UsbLowSpeed)
{
//
// low speed device
//
Descriptor->Status |= TD_CONTROL_LOWSPEED;
}
//
// store buffer size
//
Descriptor->BufferSize = BufferLength;
//
// is there a buffer
//
if(BufferLength)
{
//
// store buffer length
//
Descriptor->Token = (BufferLength - 1) << TD_TOKEN_MAXLEN_SHIFT;
}
else
{
//
// no buffer magic constant
//
Descriptor->Token = TD_TOKEN_NULL_DATA;
}
//
// store address & endpoint number
//
Descriptor->Token |= GetEndpointAddress() << TD_TOKEN_ENDPTADDR_SHIFT;
Descriptor->Token |= GetDeviceAddress() << TD_TOKEN_DEVADDR_SHIFT | PidCode;
if (BufferLength)
{
//
// allocate buffer for descriptor
//
Status = m_DmaManager->Allocate(BufferLength, (PVOID*)&Descriptor->BufferLogical, &Address);
if (!NT_SUCCESS(Status))
{
DPRINT1("[USBUHCI] Failed to allocate descriptor buffer length %lu\n", BufferLength);
m_DmaManager->Release(Descriptor, sizeof(UHCI_TRANSFER_DESCRIPTOR));
return Status;
}
//
// store address
//
Descriptor->BufferPhysical = Address.LowPart;
}
//
// done
//
*OutDescriptor = Descriptor;
return STATUS_SUCCESS;
}
NTSTATUS
CUSBRequest::BuildTransferDescriptorChain(
IN PVOID TransferBuffer,
IN ULONG TransferBufferLength,
IN UCHAR PidCode,
IN UCHAR InitialDataToggle,
OUT PUHCI_TRANSFER_DESCRIPTOR * OutFirstDescriptor,
OUT PUHCI_TRANSFER_DESCRIPTOR * OutLastDescriptor,
OUT PULONG OutTransferBufferOffset,
OUT PUCHAR OutDataToggle)
{
PUHCI_TRANSFER_DESCRIPTOR FirstDescriptor = NULL, CurrentDescriptor, LastDescriptor = NULL;
ULONG TransferBufferOffset = 0;
NTSTATUS Status;
ULONG MaxPacketSize, CurrentBufferSize;
//
// FIXME FIXME FIXME FIXME FIXME
//
if (GetDeviceSpeed() == UsbLowSpeed)
{
//
// low speed use max 8 bytes
//
MaxPacketSize = 8;
}
else
{
if (m_EndpointDescriptor)
{
//
// use endpoint size
//
MaxPacketSize = m_EndpointDescriptor->EndPointDescriptor.wMaxPacketSize;
}
else
{
//
// use max 64 bytes
//
MaxPacketSize = 64;
}
}
do
{
//
// determine current packet size
//
CurrentBufferSize = min(MaxPacketSize, TransferBufferLength - TransferBufferOffset);
//
// allocate descriptor
//
Status = CreateDescriptor(&CurrentDescriptor, PidCode, CurrentBufferSize);
if (!NT_SUCCESS(Status))
{
//
// failed to allocate queue head
//
DPRINT1("[UHCI] Failed to create descriptor\n");
ASSERT(FALSE);
return Status;
}
if (PidCode == TD_TOKEN_OUT)
{
//
// copy buffer
//
RtlCopyMemory(CurrentDescriptor->BufferLogical, (PVOID)((ULONG_PTR)TransferBuffer + TransferBufferOffset), CurrentBufferSize);
}
else
{
//
// store user buffer
//
CurrentDescriptor->UserBuffer = (PVOID)((ULONG_PTR)TransferBuffer + TransferBufferOffset);
}
if (!FirstDescriptor)
{
//
// first descriptor
//
FirstDescriptor = CurrentDescriptor;
}
else
{
//
// link descriptor
//
LastDescriptor->LinkPhysical = CurrentDescriptor->PhysicalAddress | TD_DEPTH_FIRST;
LastDescriptor->NextLogicalDescriptor = (PVOID)CurrentDescriptor;
}
if (InitialDataToggle)
{
//
// apply data toggle
//
CurrentDescriptor->Token |= TD_TOKEN_DATA1;
}
//
// re-run
//
LastDescriptor = CurrentDescriptor;
TransferBufferOffset += CurrentBufferSize;
InitialDataToggle = !InitialDataToggle;
}while(TransferBufferOffset < TransferBufferLength);
if (OutTransferBufferOffset)
{
//
// store transfer buffer length
//
*OutTransferBufferOffset = TransferBufferOffset;
}
if (OutFirstDescriptor)
{
//
// store first descriptor
//
*OutFirstDescriptor = FirstDescriptor;
}
if (OutLastDescriptor)
{
//
// store last descriptor
//
*OutLastDescriptor = CurrentDescriptor;
}
if (OutDataToggle)
{
//
// store data toggle
//
*OutDataToggle = InitialDataToggle;
}
//
// done
//
return STATUS_SUCCESS;
}
NTSTATUS
CUSBRequest::BuildQueueHead(
OUT PUHCI_QUEUE_HEAD *OutQueueHead)
{
PUHCI_QUEUE_HEAD QueueHead;
NTSTATUS Status;
PHYSICAL_ADDRESS Address;
//
// allocate queue head
//
Status = m_DmaManager->Allocate(sizeof(UHCI_QUEUE_HEAD), (PVOID*)&QueueHead, &Address);
if (!NT_SUCCESS(Status))
{
//
// failed to allocate queue head
//
DPRINT1("[UHCI] Failed to create queue head\n");
return Status;
}
//
// store address
//
QueueHead->PhysicalAddress = Address.LowPart;
QueueHead->ElementPhysical = Address.LowPart;
//
// store result
//
*OutQueueHead = QueueHead;
return STATUS_SUCCESS;
}
VOID
CUSBRequest::FreeDescriptor(
IN PUHCI_TRANSFER_DESCRIPTOR Descriptor)
{
if (Descriptor->BufferLogical)
{
//
// free buffer
//
m_DmaManager->Release(Descriptor->BufferLogical, Descriptor->BufferSize);
}
//
// free descriptors
//
m_DmaManager->Release(Descriptor, sizeof(UHCI_TRANSFER_DESCRIPTOR));
}
NTSTATUS
CUSBRequest::BuildBulkInterruptTransferDescriptor(
IN PUHCI_QUEUE_HEAD * OutQueueHead)
{
NTSTATUS Status;
PUHCI_QUEUE_HEAD QueueHead;
PUHCI_TRANSFER_DESCRIPTOR FirstDescriptor, LastDescriptor;
ULONG ChainDescriptorLength;
BOOLEAN Direction;
PVOID Buffer;
ULONG BufferSize;
// create queue head
Status = BuildQueueHead(&QueueHead);
if (!NT_SUCCESS(Status))
{
// failed to allocate queue head
DPRINT1("[UHCI] Failed to create queue head\n");
return Status;
}
// get direction
Direction = InternalGetPidDirection();
if (!m_Base)
{
// get buffer base
m_Base = MmGetMdlVirtualAddress(m_TransferBufferMDL);
// sanity check
ASSERT(m_Base != NULL);
}
// get new buffer offset
Buffer = (PVOID)((ULONG_PTR)m_Base + m_TransferBufferLengthCompleted);
// FIXME determine buffer limit
BufferSize = min(m_TransferBufferLength - m_TransferBufferLengthCompleted, PAGE_SIZE);
// create descriptor chain
Status = BuildTransferDescriptorChain(Buffer,
BufferSize,
Direction ? TD_TOKEN_IN : TD_TOKEN_OUT,
m_EndpointDescriptor->DataToggle,
&FirstDescriptor,
&LastDescriptor,
&ChainDescriptorLength,
NULL);
if (!NT_SUCCESS(Status))
{
//
// failed to allocate descriptor
//
DPRINT1("[UHCI] Failed to create descriptor chain\n");
m_DmaManager->Release(QueueHead, sizeof(UHCI_QUEUE_HEAD));
return Status;
}
// adjust buffer offset
m_TransferBufferLengthCompleted += ChainDescriptorLength;
// fire interrupt when the last descriptor is complete
LastDescriptor->Status |= TD_CONTROL_IOC;
LastDescriptor->LinkPhysical = TD_TERMINATE;
LastDescriptor->NextLogicalDescriptor = NULL;
// link queue head with first data descriptor descriptor
QueueHead->NextElementDescriptor = (PVOID)FirstDescriptor;
QueueHead->ElementPhysical = FirstDescriptor->PhysicalAddress;
// store result
*OutQueueHead = QueueHead;
return STATUS_SUCCESS;
}
NTSTATUS
CUSBRequest::BuildControlTransferDescriptor(
IN PUHCI_QUEUE_HEAD * OutQueueHead)
{
PUHCI_TRANSFER_DESCRIPTOR SetupDescriptor, StatusDescriptor, FirstDescriptor, LastDescriptor;
PUHCI_QUEUE_HEAD QueueHead;
BOOLEAN Direction;
NTSTATUS Status;
ULONG ChainDescriptorLength;
//
// create queue head
//
Status = BuildQueueHead(&QueueHead);
if (!NT_SUCCESS(Status))
{
//
// failed to allocate queue head
//
DPRINT1("[UHCI] Failed to create queue head\n");
return Status;
}
//
// get direction
//
Direction = InternalGetPidDirection();
//
// build setup descriptor
//
Status = CreateDescriptor(&SetupDescriptor,
TD_TOKEN_SETUP,
sizeof(USB_DEFAULT_PIPE_SETUP_PACKET));
if (!NT_SUCCESS(Status))
{
//
// failed to allocate descriptor
//
DPRINT1("[UHCI] Failed to create setup descriptor\n");
m_DmaManager->Release(QueueHead, sizeof(UHCI_QUEUE_HEAD));
return Status;
}
//
// build status descriptor
//
Status = CreateDescriptor(&StatusDescriptor,
Direction ? TD_TOKEN_OUT : TD_TOKEN_IN,
0);
if (!NT_SUCCESS(Status))
{
//
// failed to allocate descriptor
//
DPRINT1("[UHCI] Failed to create status descriptor\n");
FreeDescriptor(SetupDescriptor);
m_DmaManager->Release(QueueHead, sizeof(UHCI_QUEUE_HEAD));
return Status;
}
if (m_SetupPacket)
{
//
// copy setup packet
//
RtlCopyMemory(SetupDescriptor->BufferLogical, m_SetupPacket, sizeof(USB_DEFAULT_PIPE_SETUP_PACKET));
}
else
{
//
// generate setup packet from urb
//
ASSERT(FALSE);
}
//
// init status descriptor
//
StatusDescriptor->Status |= TD_CONTROL_IOC;
StatusDescriptor->Token |= TD_TOKEN_DATA1;
StatusDescriptor->LinkPhysical = TD_TERMINATE;
StatusDescriptor->NextLogicalDescriptor = NULL;
if (m_TransferBufferLength)
{
//
// create descriptor chain
//
Status = BuildTransferDescriptorChain(MmGetMdlVirtualAddress(m_TransferBufferMDL),
m_TransferBufferLength,
Direction ? TD_TOKEN_IN : TD_TOKEN_OUT,
TRUE,
&FirstDescriptor,
&LastDescriptor,
&ChainDescriptorLength,
NULL);
if (!NT_SUCCESS(Status))
{
//
// failed to allocate descriptor
//
DPRINT1("[UHCI] Failed to create descriptor chain\n");
FreeDescriptor(SetupDescriptor);
FreeDescriptor(StatusDescriptor);
m_DmaManager->Release(QueueHead, sizeof(UHCI_QUEUE_HEAD));
return Status;
}
//
// link setup descriptor to first data descriptor
//
SetupDescriptor->LinkPhysical = FirstDescriptor->PhysicalAddress | TD_DEPTH_FIRST;
SetupDescriptor->NextLogicalDescriptor = (PVOID)FirstDescriptor;
//
// link last data descriptor to status descriptor
//
LastDescriptor->LinkPhysical = StatusDescriptor->PhysicalAddress | TD_DEPTH_FIRST;
LastDescriptor->NextLogicalDescriptor = (PVOID)StatusDescriptor;
}
else
{
//
// directly link setup to status descriptor
//
SetupDescriptor->LinkPhysical = StatusDescriptor->PhysicalAddress | TD_DEPTH_FIRST;
SetupDescriptor->NextLogicalDescriptor = (PVOID)StatusDescriptor;
}
//
// link queue head with setup descriptor
//
QueueHead->NextElementDescriptor = (PVOID)SetupDescriptor;
QueueHead->ElementPhysical = SetupDescriptor->PhysicalAddress;
//
// store result
//
*OutQueueHead = QueueHead;
return STATUS_SUCCESS;
}
USB_DEVICE_SPEED
CUSBRequest::GetDeviceSpeed()
{
return m_DeviceSpeed;
}
VOID
CUSBRequest::FreeEndpointDescriptor(
struct _UHCI_QUEUE_HEAD * OutDescriptor)
{
PUHCI_TRANSFER_DESCRIPTOR Descriptor, NextDescriptor;
ULONG ErrorCount;
UCHAR DataToggle = 0;
ULONG Index = 0;
//
// grab first transfer descriptor
//
Descriptor = (PUHCI_TRANSFER_DESCRIPTOR)OutDescriptor->NextElementDescriptor;
while(Descriptor)
{
// get data toggle
DataToggle = (Descriptor->Token >> TD_TOKEN_DATA_TOGGLE_SHIFT) & 0x01;
if (Descriptor->Status & TD_ERROR_MASK)
{
//
// error happened
//
DPRINT1("[USBUHCI] Error detected at descriptor %p Physical %x\n", Descriptor, Descriptor->PhysicalAddress);
//
// get error count
//
ErrorCount = (Descriptor->Status >> TD_ERROR_COUNT_SHIFT) & TD_ERROR_COUNT_MASK;
if (ErrorCount == 0)
{
//
// error retry count elapsed
//
m_NtStatusCode = STATUS_UNSUCCESSFUL;
if (Descriptor->Status & TD_STATUS_ERROR_BUFFER)
{
DPRINT1("[USBUHCI] Buffer Error detected in descriptor %p Index %lu\n", Descriptor, Index);
m_UrbStatusCode = USBD_STATUS_DATA_BUFFER_ERROR;
}
else if (Descriptor->Status & TD_STATUS_ERROR_TIMEOUT)
{
DPRINT1("[USBUHCI] Timeout detected in descriptor %p Index %lu\n", Descriptor, Index);
m_UrbStatusCode = USBD_STATUS_TIMEOUT;
}
else if (Descriptor->Status & TD_STATUS_ERROR_NAK)
{
DPRINT1("[USBUHCI] Unexpected pid detected in descriptor %p Index %lu\n", Descriptor, Index);
m_UrbStatusCode = USBD_STATUS_UNEXPECTED_PID;
}
else if (Descriptor->Status & TD_STATUS_ERROR_BITSTUFF)
{
DPRINT1("[USBUHCI] BitStuff detected in descriptor %p Index %lu\n", Descriptor, Index);
m_UrbStatusCode = USBD_STATUS_BTSTUFF;
}
}
else if (Descriptor->Status & TD_STATUS_ERROR_BABBLE)
{
//
// babble error
//
DPRINT1("[USBUHCI] Babble detected in descriptor %p Index %lu\n", Descriptor, Index);
m_UrbStatusCode = USBD_STATUS_BABBLE_DETECTED;
}
else
{
//
// stall detected
//
DPRINT1("[USBUHCI] Stall detected Descriptor %p Index %lu\n", Descriptor, Index);
m_UrbStatusCode = USBD_STATUS_STALL_PID;
}
}
else
{
//
// FIXME detect actual length
//
if (Descriptor->UserBuffer)
{
//
// copy contents back
//
RtlCopyMemory(Descriptor->UserBuffer, Descriptor->BufferLogical, Descriptor->BufferSize);
}
}
//
// move to next descriptor
//
NextDescriptor = (PUHCI_TRANSFER_DESCRIPTOR)Descriptor->NextLogicalDescriptor;
//
// free endpoint descriptor
//
FreeDescriptor(Descriptor);
//
// move to next
//
Descriptor = NextDescriptor;
Index++;
}
//
// now free queue head
//
m_DmaManager->Release(OutDescriptor, sizeof(UHCI_QUEUE_HEAD));
// is there an endpoint descriptor
if (m_EndpointDescriptor)
{
// invert last data toggle
m_EndpointDescriptor->DataToggle = (DataToggle == 0);
}
}
VOID
CUSBRequest::CompletionCallback()
{
PIO_STACK_LOCATION IoStack;
PURB Urb;
DPRINT("CUSBRequest::CompletionCallback\n");
if (m_Irp)
{
//
// set irp completion status
//
m_Irp->IoStatus.Status = m_NtStatusCode;
//
// get current irp stack location
//
IoStack = IoGetCurrentIrpStackLocation(m_Irp);
//
// get urb
//
Urb = (PURB)IoStack->Parameters.Others.Argument1;
//
// store urb status
//
Urb->UrbHeader.Status = m_UrbStatusCode;
//
// Check if the MDL was created
//
if (!Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL)
{
//
// Free Mdl
//
IoFreeMdl(m_TransferBufferMDL);
}
//
// FIXME calculate length
//
//
// complete request
//
IoCompleteRequest(m_Irp, IO_NO_INCREMENT);
}
else
{
//
// signal completion event
//
PC_ASSERT(m_CompletionEvent);
KeSetEvent(m_CompletionEvent, 0, FALSE);
}
}
//-----------------------------------------------------------------------------------------
NTSTATUS
NTAPI
InternalCreateUSBRequest(
PUSBREQUEST *OutRequest)
{
PUSBREQUEST This;
//
// allocate requests
//
This = new(NonPagedPool, TAG_USBUHCI) 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;
}