reactos/drivers/usb/usbohci/usb_request.cpp

1695 lines
45 KiB
C++
Raw Normal View History

/*
* PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface
* LICENSE: GPL - See COPYING in the top level directory
* FILE: drivers/usb/usbohci/usb_request.cpp
* PURPOSE: USB OHCI device driver.
* PROGRAMMERS:
* Michael Martin (michael.martin@reactos.org)
* Johannes Anderwald (johannes.anderwald@reactos.org)
*/
#define INITGUID
#include "usbohci.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 BOOLEAN IsRequestComplete();
virtual ULONG GetTransferType();
virtual NTSTATUS GetEndpointDescriptor(struct _OHCI_ENDPOINT_DESCRIPTOR ** OutEndpointDescriptor);
virtual VOID GetResultStatus(OUT OPTIONAL NTSTATUS *NtStatusCode, OUT OPTIONAL PULONG UrbStatusCode);
virtual BOOLEAN IsRequestInitialized();
virtual BOOLEAN IsQueueHeadComplete(struct _QUEUE_HEAD * QueueHead);
virtual VOID CompletionCallback(struct _OHCI_ENDPOINT_DESCRIPTOR * OutDescriptor);
virtual VOID FreeEndpointDescriptor(struct _OHCI_ENDPOINT_DESCRIPTOR * OutDescriptor);
virtual UCHAR GetInterval();
// local functions
ULONG InternalGetTransferType();
UCHAR InternalGetPidDirection();
UCHAR GetDeviceAddress();
NTSTATUS BuildSetupPacket();
NTSTATUS BuildSetupPacketFromURB();
NTSTATUS BuildControlTransferDescriptor(POHCI_ENDPOINT_DESCRIPTOR * OutEndpointDescriptor);
NTSTATUS BuildBulkInterruptEndpoint(POHCI_ENDPOINT_DESCRIPTOR * OutEndpointDescriptor);
NTSTATUS BuildIsochronousEndpoint(POHCI_ENDPOINT_DESCRIPTOR * OutEndpointDescriptor);
NTSTATUS CreateGeneralTransferDescriptor(POHCI_GENERAL_TD* OutDescriptor, ULONG BufferSize);
VOID FreeDescriptor(POHCI_GENERAL_TD Descriptor);
NTSTATUS AllocateEndpointDescriptor(OUT POHCI_ENDPOINT_DESCRIPTOR *OutDescriptor);
NTSTATUS CreateIsochronousTransferDescriptor(OUT POHCI_ISO_TD *OutDescriptor, ULONG FrameCount);
UCHAR GetEndpointAddress();
USHORT GetMaxPacketSize();
// 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_DESCRIPTOR 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;
};
//----------------------------------------------------------------------------------------
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;
m_TotalBytesTransferred = 0;
//
// Set Length Completed to 0
//
m_TransferBufferLengthCompleted = 0;
//
// allocate completion event
//
m_CompletionEvent = (PKEVENT)ExAllocatePoolWithTag(NonPagedPool, sizeof(KEVENT), TAG_USBOHCI);
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;
m_TotalBytesTransferred = 0;
//
// 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_DESCRIPTOR)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_DESCRIPTOR)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->wMaxPacketSize;
}
UCHAR
CUSBRequest::GetInterval()
{
ASSERT(m_EndpointDescriptor);
ASSERT((m_EndpointDescriptor->bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_INTERRUPT);
//
// return interrupt interval
//
return m_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->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->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;
}
//----------------------------------------------------------------------------------------
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;
}
VOID
CUSBRequest::FreeDescriptor(
POHCI_GENERAL_TD Descriptor)
{
if (Descriptor->BufferSize)
{
//
// free buffer
//
m_DmaManager->Release(Descriptor->BufferLogical, Descriptor->BufferSize);
}
//
// release descriptor
//
m_DmaManager->Release(Descriptor, sizeof(OHCI_GENERAL_TD));
}
//----------------------------------------------------------------------------------------
NTSTATUS
CUSBRequest::CreateIsochronousTransferDescriptor(
POHCI_ISO_TD* OutDescriptor,
ULONG FrameCount)
{
POHCI_ISO_TD Descriptor;
PHYSICAL_ADDRESS DescriptorAddress;
NTSTATUS Status;
//
// allocate transfer descriptor
//
Status = m_DmaManager->Allocate(sizeof(OHCI_ISO_TD), (PVOID*)&Descriptor, &DescriptorAddress);
if (!NT_SUCCESS(Status))
{
//
// no memory
//
return Status;
}
//
// initialize descriptor, hardware part
//
Descriptor->Flags = OHCI_ITD_SET_FRAME_COUNT(FrameCount) | OHCI_ITD_SET_DELAY_INTERRUPT(OHCI_TD_INTERRUPT_NONE);// | OHCI_TD_SET_CONDITION_CODE(OHCI_TD_CONDITION_NOT_ACCESSED);
Descriptor->BufferPhysical = 0;
Descriptor->NextPhysicalDescriptor = 0;
Descriptor->LastPhysicalByteAddress = 0;
//
// software part
//
Descriptor->PhysicalAddress.QuadPart = DescriptorAddress.QuadPart;
Descriptor->NextLogicalDescriptor = 0;
//
// store result
//
*OutDescriptor = Descriptor;
//
// done
//
return STATUS_SUCCESS;
}
NTSTATUS
CUSBRequest::BuildIsochronousEndpoint(
POHCI_ENDPOINT_DESCRIPTOR * OutEndpointDescriptor)
{
POHCI_ISO_TD FirstDescriptor, PreviousDescriptor = NULL, CurrentDescriptor;
POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor;
ULONG Index = 0, SubIndex, NumberOfPackets, PageOffset, Page;
NTSTATUS Status;
PVOID Buffer;
PIO_STACK_LOCATION IoStack;
PURB Urb;
PHYSICAL_ADDRESS Address;
//
// get current irp stack location
//
IoStack = IoGetCurrentIrpStackLocation(m_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;
ASSERT(Urb);
//
// allocate endpoint descriptor
//
Status = AllocateEndpointDescriptor(&EndpointDescriptor);
if (!NT_SUCCESS(Status))
{
//
// failed to create setup descriptor
//
ASSERT(FALSE);
return Status;
}
//
// get buffer
//
Buffer = MmGetSystemAddressForMdlSafe(m_TransferBufferMDL, NormalPagePriority);
ASSERT(Buffer);
//
// FIXME: support requests which spans serveral pages
//
ASSERT(ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(m_TransferBufferMDL), MmGetMdlByteCount(m_TransferBufferMDL)) <= 2);
Status = m_DmaManager->Allocate(m_TransferBufferLength, &Buffer, &Address);
ASSERT(Status == STATUS_SUCCESS);
while(Index < Urb->UrbIsochronousTransfer.NumberOfPackets)
{
//
// get number of packets remaining
//
NumberOfPackets = min(Urb->UrbIsochronousTransfer.NumberOfPackets - Index, OHCI_ITD_NOFFSET);
//
// allocate iso descriptor
//
Status = CreateIsochronousTransferDescriptor(&CurrentDescriptor, NumberOfPackets);
if (!NT_SUCCESS(Status))
{
//
// FIXME: cleanup
// failed to allocate descriptor
//
ASSERT(FALSE);
return Status;
}
//
// get physical page
//
Page = MmGetPhysicalAddress(Buffer).LowPart;
//
// get page offset
//
PageOffset = BYTE_OFFSET(Page);
//
// initialize descriptor
//
CurrentDescriptor->BufferPhysical = Page - PageOffset;
for(SubIndex = 0; SubIndex < NumberOfPackets; SubIndex++)
{
//
// store buffer offset
//
CurrentDescriptor->Offset[SubIndex] = Urb->UrbIsochronousTransfer.IsoPacket[Index+SubIndex].Offset + PageOffset;
DPRINT1("Index %lu PacketOffset %lu FinalOffset %lu\n", SubIndex+Index, Urb->UrbIsochronousTransfer.IsoPacket[Index+SubIndex].Offset, CurrentDescriptor->Offset[SubIndex]);
}
//
// increment packet offset
//
Index += NumberOfPackets;
//
// check if this is the last descriptor
//
if (Index == Urb->UrbIsochronousTransfer.NumberOfPackets)
{
//
// end of transfer
//
CurrentDescriptor->LastPhysicalByteAddress = CurrentDescriptor->BufferPhysical + PageOffset + m_TransferBufferLength - 1;
}
else
{
//
// use start address of next packet - 1
//
CurrentDescriptor->LastPhysicalByteAddress = CurrentDescriptor->BufferPhysical + PageOffset + Urb->UrbIsochronousTransfer.IsoPacket[Index].Offset - 1;
}
//
// is there a previous descriptor
//
if (PreviousDescriptor)
{
//
// link descriptors
//
PreviousDescriptor->NextLogicalDescriptor = CurrentDescriptor;
PreviousDescriptor->NextPhysicalDescriptor = CurrentDescriptor->PhysicalAddress.LowPart;
}
else
{
//
// first descriptor
//
FirstDescriptor = CurrentDescriptor;
}
//
// store as previous descriptor
//
PreviousDescriptor = CurrentDescriptor;
DPRINT1("Current Descriptor %p Logical %lx StartAddress %x EndAddress %x\n", CurrentDescriptor, CurrentDescriptor->PhysicalAddress.LowPart, CurrentDescriptor->BufferPhysical, CurrentDescriptor->LastPhysicalByteAddress);
//
// fire interrupt as soon transfer is finished
//
CurrentDescriptor->Flags |= OHCI_TD_SET_DELAY_INTERRUPT(OHCI_TD_INTERRUPT_IMMEDIATE);
}
//
// clear interrupt mask for last transfer descriptor
//
CurrentDescriptor->Flags &= ~OHCI_TD_INTERRUPT_MASK;
//
// fire interrupt as soon transfer is finished
//
CurrentDescriptor->Flags |= OHCI_TD_SET_DELAY_INTERRUPT(OHCI_TD_INTERRUPT_IMMEDIATE);
//
// set isochronous type
//
EndpointDescriptor->Flags |= OHCI_ENDPOINT_ISOCHRONOUS_FORMAT;
//
// now link descriptor to endpoint
//
EndpointDescriptor->HeadPhysicalDescriptor = FirstDescriptor->PhysicalAddress.LowPart;
EndpointDescriptor->TailPhysicalDescriptor = CurrentDescriptor->PhysicalAddress.LowPart;
EndpointDescriptor->HeadLogicalDescriptor = FirstDescriptor;
//
// store result
//
*OutEndpointDescriptor = EndpointDescriptor;
//
// done
//
return STATUS_SUCCESS;
}
//----------------------------------------------------------------------------------------
NTSTATUS
CUSBRequest::CreateGeneralTransferDescriptor(
POHCI_GENERAL_TD* OutDescriptor,
ULONG BufferSize)
{
POHCI_GENERAL_TD Descriptor;
PHYSICAL_ADDRESS DescriptorAddress;
NTSTATUS Status;
//
// allocate transfer descriptor
//
Status = m_DmaManager->Allocate(sizeof(OHCI_GENERAL_TD), (PVOID*)&Descriptor, &DescriptorAddress);
if (!NT_SUCCESS(Status))
{
//
// no memory
//
return Status;
}
//
// initialize descriptor, hardware part
//
Descriptor->Flags = 0;
Descriptor->BufferPhysical = 0;
Descriptor->NextPhysicalDescriptor = 0;
Descriptor->LastPhysicalByteAddress = 0;
//
// software part
//
Descriptor->PhysicalAddress.QuadPart = DescriptorAddress.QuadPart;
Descriptor->BufferSize = BufferSize;
if (BufferSize > 0)
{
//
// allocate buffer from dma
//
Status = m_DmaManager->Allocate(BufferSize, &Descriptor->BufferLogical, &DescriptorAddress);
if (!NT_SUCCESS(Status))
{
//
// no memory
//
m_DmaManager->Release(Descriptor, sizeof(OHCI_GENERAL_TD));
return Status;
}
//
// set physical address of buffer
//
Descriptor->BufferPhysical = DescriptorAddress.LowPart;
Descriptor->LastPhysicalByteAddress = Descriptor->BufferPhysical + BufferSize - 1;
}
//
// store result
//
*OutDescriptor = Descriptor;
//
// done
//
return STATUS_SUCCESS;
}
NTSTATUS
CUSBRequest::AllocateEndpointDescriptor(
OUT POHCI_ENDPOINT_DESCRIPTOR *OutDescriptor)
{
POHCI_ENDPOINT_DESCRIPTOR Descriptor;
PHYSICAL_ADDRESS DescriptorAddress;
NTSTATUS Status;
//
// allocate descriptor
//
Status = m_DmaManager->Allocate(sizeof(OHCI_ENDPOINT_DESCRIPTOR), (PVOID*)&Descriptor, &DescriptorAddress);
if (!NT_SUCCESS(Status))
{
//
// failed to allocate descriptor
//
return Status;
}
//
// intialize descriptor
//
Descriptor->Flags = OHCI_ENDPOINT_SKIP;
//
// append device address and endpoint number
//
Descriptor->Flags |= OHCI_ENDPOINT_SET_DEVICE_ADDRESS(GetDeviceAddress());
Descriptor->Flags |= OHCI_ENDPOINT_SET_ENDPOINT_NUMBER(GetEndpointAddress());
Descriptor->Flags |= OHCI_ENDPOINT_SET_MAX_PACKET_SIZE(GetMaxPacketSize());
DPRINT("Flags %x DeviceAddress %x EndpointAddress %x PacketSize %x\n", Descriptor->Flags, GetDeviceAddress(), GetEndpointAddress(), GetMaxPacketSize());
//
// is there an endpoint descriptor
//
if (m_EndpointDescriptor)
{
//
// check direction
//
if (USB_ENDPOINT_DIRECTION_OUT(m_EndpointDescriptor->bEndpointAddress))
{
//
// direction out
//
Descriptor->Flags |= OHCI_ENDPOINT_DIRECTION_OUT;
}
else
{
//
// direction in
//
Descriptor->Flags |= OHCI_ENDPOINT_DIRECTION_IN;
}
}
//
// FIXME: detect type
//
Descriptor->Flags |= OHCI_ENDPOINT_FULL_SPEED;
Descriptor->HeadPhysicalDescriptor = 0;
Descriptor->NextPhysicalEndpoint = 0;
Descriptor->TailPhysicalDescriptor = 0;
Descriptor->PhysicalAddress.QuadPart = DescriptorAddress.QuadPart;
//
// store result
//
*OutDescriptor = Descriptor;
//
// done
//
return STATUS_SUCCESS;
}
NTSTATUS
CUSBRequest::BuildBulkInterruptEndpoint(
POHCI_ENDPOINT_DESCRIPTOR * OutEndpointDescriptor)
{
POHCI_GENERAL_TD FirstDescriptor, PreviousDescriptor = NULL, CurrentDescriptor, LastDescriptor;
POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor;
ULONG BufferSize, CurrentSize, Direction, MaxLengthInPage;
NTSTATUS Status;
PVOID Buffer;
//
// allocate endpoint descriptor
//
Status = AllocateEndpointDescriptor(&EndpointDescriptor);
if (!NT_SUCCESS(Status))
{
//
// failed to create setup descriptor
//
return Status;
}
//
// allocate transfer descriptor for last descriptor
//
Status = CreateGeneralTransferDescriptor(&LastDescriptor, 0);
if (!NT_SUCCESS(Status))
{
//
// failed to create transfer descriptor
//
m_DmaManager->Release(EndpointDescriptor, sizeof(OHCI_ENDPOINT_DESCRIPTOR));
return Status;
}
//
// get buffer size
//
BufferSize = m_TransferBufferLength;
ASSERT(BufferSize);
ASSERT(m_TransferBufferMDL);
//
// get buffer
//
Buffer = MmGetSystemAddressForMdlSafe(m_TransferBufferMDL, NormalPagePriority);
ASSERT(Buffer);
if (InternalGetPidDirection())
{
//
// input direction
//
Direction = OHCI_TD_DIRECTION_PID_IN;
}
else
{
//
// output direction
//
Direction = OHCI_TD_DIRECTION_PID_OUT;
}
do
{
//
// get current buffersize
//
CurrentSize = min(8192, BufferSize);
//
// get page offset
//
MaxLengthInPage = PAGE_SIZE - BYTE_OFFSET(Buffer);
//
// get minimum from current page size
//
CurrentSize = min(CurrentSize, MaxLengthInPage);
ASSERT(CurrentSize);
//
// allocate transfer descriptor
//
Status = CreateGeneralTransferDescriptor(&CurrentDescriptor, 0);
if (!NT_SUCCESS(Status))
{
//
// failed to create transfer descriptor
// TODO: cleanup
//
ASSERT(FALSE);
m_DmaManager->Release(EndpointDescriptor, sizeof(OHCI_ENDPOINT_DESCRIPTOR));
FreeDescriptor(LastDescriptor);
return Status;
}
//
// initialize descriptor
//
CurrentDescriptor->Flags = Direction | OHCI_TD_BUFFER_ROUNDING | OHCI_TD_SET_CONDITION_CODE(OHCI_TD_CONDITION_NOT_ACCESSED) | OHCI_TD_SET_DELAY_INTERRUPT(OHCI_TD_INTERRUPT_NONE) | OHCI_TD_TOGGLE_CARRY;
//
// store physical address of buffer
//
CurrentDescriptor->BufferPhysical = MmGetPhysicalAddress(Buffer).LowPart;
CurrentDescriptor->LastPhysicalByteAddress = CurrentDescriptor->BufferPhysical + CurrentSize - 1;
//
// is there a previous descriptor
//
if (PreviousDescriptor)
{
//
// link descriptors
//
PreviousDescriptor->NextLogicalDescriptor = (PVOID)CurrentDescriptor;
PreviousDescriptor->NextPhysicalDescriptor = CurrentDescriptor->PhysicalAddress.LowPart;
}
else
{
//
// it is the first descriptor
//
FirstDescriptor = CurrentDescriptor;
}
DPRINT("PreviousDescriptor %p CurrentDescriptor %p Logical %x Buffer Logical %p Physical %x Last Physical %x CurrentSize %lu\n", PreviousDescriptor, CurrentDescriptor, CurrentDescriptor->PhysicalAddress.LowPart, CurrentDescriptor->BufferLogical, CurrentDescriptor->BufferPhysical, CurrentDescriptor->LastPhysicalByteAddress, CurrentSize);
//
// set previous descriptor
//
PreviousDescriptor = CurrentDescriptor;
//
// subtract buffer size
//
BufferSize -= CurrentSize;
//
// increment buffer offset
//
Buffer = (PVOID)((ULONG_PTR)Buffer + CurrentSize);
}while(BufferSize);
//
// first descriptor has no carry bit
//
FirstDescriptor->Flags &= ~OHCI_TD_TOGGLE_CARRY;
//
// fixme: toggle
//
FirstDescriptor->Flags |= OHCI_TD_TOGGLE_0;
//
// clear interrupt mask for last transfer descriptor
//
CurrentDescriptor->Flags &= ~OHCI_TD_INTERRUPT_MASK;
//
// fire interrupt as soon transfer is finished
//
CurrentDescriptor->Flags |= OHCI_TD_SET_DELAY_INTERRUPT(OHCI_TD_INTERRUPT_IMMEDIATE);
//
// link last data descriptor to last descriptor
//
CurrentDescriptor->NextLogicalDescriptor = LastDescriptor;
CurrentDescriptor->NextPhysicalDescriptor = LastDescriptor->PhysicalAddress.LowPart;
//
// now link descriptor to endpoint
//
EndpointDescriptor->HeadPhysicalDescriptor = FirstDescriptor->PhysicalAddress.LowPart;
EndpointDescriptor->TailPhysicalDescriptor = LastDescriptor->PhysicalAddress.LowPart;
EndpointDescriptor->HeadLogicalDescriptor = FirstDescriptor;
//
// store result
//
*OutEndpointDescriptor = EndpointDescriptor;
//
// done
//
return STATUS_SUCCESS;
}
NTSTATUS
CUSBRequest::BuildControlTransferDescriptor(
POHCI_ENDPOINT_DESCRIPTOR * OutEndpointDescriptor)
{
POHCI_GENERAL_TD SetupDescriptor, StatusDescriptor, DataDescriptor = NULL, LastDescriptor;
POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor;
NTSTATUS Status;
//
// allocate endpoint descriptor
//
Status = AllocateEndpointDescriptor(&EndpointDescriptor);
if (!NT_SUCCESS(Status))
{
//
// failed to create setup descriptor
//
return Status;
}
//
// first allocate setup descriptor
//
Status = CreateGeneralTransferDescriptor(&SetupDescriptor, sizeof(USB_DEFAULT_PIPE_SETUP_PACKET));
if (!NT_SUCCESS(Status))
{
//
// failed to create setup descriptor
//
m_DmaManager->Release(EndpointDescriptor, sizeof(OHCI_ENDPOINT_DESCRIPTOR));
return Status;
}
//
// now create the status descriptor
//
Status = CreateGeneralTransferDescriptor(&StatusDescriptor, 0);
if (!NT_SUCCESS(Status))
{
//
// failed to create status descriptor
//
FreeDescriptor(SetupDescriptor);
m_DmaManager->Release(EndpointDescriptor, sizeof(OHCI_ENDPOINT_DESCRIPTOR));
return Status;
}
//
// finally create the last descriptor
//
Status = CreateGeneralTransferDescriptor(&LastDescriptor, 0);
if (!NT_SUCCESS(Status))
{
//
// failed to create status descriptor
//
FreeDescriptor(SetupDescriptor);
FreeDescriptor(StatusDescriptor);
m_DmaManager->Release(EndpointDescriptor, sizeof(OHCI_ENDPOINT_DESCRIPTOR));
return Status;
}
if (m_TransferBufferLength)
{
//
// FIXME: support more than one data descriptor
//
ASSERT(m_TransferBufferLength < 8192);
//
// now create the data descriptor
//
Status = CreateGeneralTransferDescriptor(&DataDescriptor, 0);
if (!NT_SUCCESS(Status))
{
//
// failed to create status descriptor
//
m_DmaManager->Release(EndpointDescriptor, sizeof(OHCI_ENDPOINT_DESCRIPTOR));
FreeDescriptor(SetupDescriptor);
FreeDescriptor(StatusDescriptor);
FreeDescriptor(LastDescriptor);
return Status;
}
//
// initialize data descriptor
//
DataDescriptor->Flags = OHCI_TD_BUFFER_ROUNDING| OHCI_TD_SET_CONDITION_CODE(OHCI_TD_CONDITION_NOT_ACCESSED) | OHCI_TD_SET_DELAY_INTERRUPT(OHCI_TD_INTERRUPT_NONE) | OHCI_TD_TOGGLE_CARRY | OHCI_TD_DIRECTION_PID_IN;
//
// store physical address of buffer
//
DataDescriptor->BufferPhysical = MmGetPhysicalAddress(MmGetMdlVirtualAddress(m_TransferBufferMDL)).LowPart;
DataDescriptor->LastPhysicalByteAddress = DataDescriptor->BufferPhysical + m_TransferBufferLength - 1;
}
//
// initialize setup descriptor
//
SetupDescriptor->Flags = OHCI_TD_DIRECTION_PID_SETUP | OHCI_TD_SET_CONDITION_CODE(OHCI_TD_CONDITION_NOT_ACCESSED) | OHCI_TD_TOGGLE_0 | OHCI_TD_SET_DELAY_INTERRUPT(OHCI_TD_INTERRUPT_NONE);
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);
}
//
// initialize status descriptor
//
StatusDescriptor->Flags = OHCI_TD_SET_CONDITION_CODE(OHCI_TD_CONDITION_NOT_ACCESSED) | OHCI_TD_TOGGLE_1 | OHCI_TD_SET_DELAY_INTERRUPT(OHCI_TD_INTERRUPT_IMMEDIATE);
if (m_TransferBufferLength == 0)
{
//
// input direction is flipped for the status descriptor
//
StatusDescriptor->Flags |= OHCI_TD_DIRECTION_PID_IN;
}
else
{
//
// output direction is flipped for the status descriptor
//
StatusDescriptor->Flags |= OHCI_TD_DIRECTION_PID_OUT;
}
//
// now link the descriptors
//
if (m_TransferBufferLength)
{
//
// link setup descriptor to data descriptor
//
SetupDescriptor->NextPhysicalDescriptor = DataDescriptor->PhysicalAddress.LowPart;
SetupDescriptor->NextLogicalDescriptor = DataDescriptor;
//
// link data descriptor to status descriptor
// FIXME: check if there are more data descriptors
//
DataDescriptor->NextPhysicalDescriptor = StatusDescriptor->PhysicalAddress.LowPart;
DataDescriptor->NextLogicalDescriptor = StatusDescriptor;
//
// link status descriptor to last descriptor
//
StatusDescriptor->NextPhysicalDescriptor = LastDescriptor->PhysicalAddress.LowPart;
StatusDescriptor->NextLogicalDescriptor = LastDescriptor;
}
else
{
//
// link setup descriptor to status descriptor
//
SetupDescriptor->NextPhysicalDescriptor = StatusDescriptor->PhysicalAddress.LowPart;
SetupDescriptor->NextLogicalDescriptor = StatusDescriptor;
//
// link status descriptor to last descriptor
//
StatusDescriptor->NextPhysicalDescriptor = LastDescriptor->PhysicalAddress.LowPart;
StatusDescriptor->NextLogicalDescriptor = LastDescriptor;
}
//
// now link descriptor to endpoint
//
EndpointDescriptor->HeadPhysicalDescriptor = SetupDescriptor->PhysicalAddress.LowPart;
EndpointDescriptor->TailPhysicalDescriptor = LastDescriptor->PhysicalAddress.LowPart;
EndpointDescriptor->HeadLogicalDescriptor = SetupDescriptor;
//
// store result
//
*OutEndpointDescriptor = EndpointDescriptor;
//
// done
//
return STATUS_SUCCESS;
}
//----------------------------------------------------------------------------------------
NTSTATUS
CUSBRequest::GetEndpointDescriptor(
struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor)
{
ULONG TransferType;
NTSTATUS Status;
//
// get transfer type
//
TransferType = InternalGetTransferType();
//
// build request depending on type
//
switch(TransferType)
{
case USB_ENDPOINT_TYPE_CONTROL:
Status = BuildControlTransferDescriptor((POHCI_ENDPOINT_DESCRIPTOR*)OutDescriptor);
break;
case USB_ENDPOINT_TYPE_BULK:
case USB_ENDPOINT_TYPE_INTERRUPT:
Status = BuildBulkInterruptEndpoint(OutDescriptor);
break;
case USB_ENDPOINT_TYPE_ISOCHRONOUS:
Status = BuildIsochronousEndpoint((POHCI_ENDPOINT_DESCRIPTOR*)OutDescriptor);
break;
default:
PC_ASSERT(FALSE);
Status = STATUS_NOT_IMPLEMENTED;
break;
}
if (NT_SUCCESS(Status))
{
//
// store queue head
//
//m_QueueHead = *OutDescriptor;
//
// store request object
//
(*OutDescriptor)->Request = PVOID(this);
}
//
// done
//
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;
}
}
VOID
CUSBRequest::FreeEndpointDescriptor(
struct _OHCI_ENDPOINT_DESCRIPTOR * OutDescriptor)
{
POHCI_GENERAL_TD TransferDescriptor, NextTransferDescriptor;
POHCI_ISO_TD IsoTransferDescriptor, IsoNextTransferDescriptor;
ULONG Index, PacketCount;
DPRINT("CUSBRequest::FreeEndpointDescriptor EndpointDescriptor %p Logical %x\n", OutDescriptor, OutDescriptor->PhysicalAddress.LowPart);
if (OutDescriptor->Flags & OHCI_ENDPOINT_ISOCHRONOUS_FORMAT)
{
//
// get first iso transfer descriptor
//
IsoTransferDescriptor = (POHCI_ISO_TD)OutDescriptor->HeadLogicalDescriptor;
//
// release endpoint descriptor
//
m_DmaManager->Release(OutDescriptor, sizeof(OHCI_ENDPOINT_DESCRIPTOR));
while(IsoTransferDescriptor)
{
//
// get next
//
IsoNextTransferDescriptor = IsoTransferDescriptor->NextLogicalDescriptor;
//
// get packet count
//
PacketCount = OHCI_ITD_GET_FRAME_COUNT(IsoTransferDescriptor->Flags);
DPRINT1("CUSBRequest::FreeEndpointDescriptor Descriptor %p Logical %x Buffer Physical %x EndAddress %x PacketCount %lu\n", IsoTransferDescriptor, IsoTransferDescriptor->PhysicalAddress.LowPart, IsoTransferDescriptor->BufferPhysical, IsoTransferDescriptor->LastPhysicalByteAddress, PacketCount);
for(Index = 0; Index < PacketCount; Index++)
{
DPRINT1("PSW Index %lu Value %x\n", Index, IsoTransferDescriptor->Offset[Index]);
}
//
// release descriptor
//
m_DmaManager->Release(IsoTransferDescriptor, sizeof(OHCI_ISO_TD));
//
// move to next
//
IsoTransferDescriptor = IsoNextTransferDescriptor;
}
}
else
{
//
// get first general transfer descriptor
//
TransferDescriptor = (POHCI_GENERAL_TD)OutDescriptor->HeadLogicalDescriptor;
//
// release endpoint descriptor
//
m_DmaManager->Release(OutDescriptor, sizeof(OHCI_ENDPOINT_DESCRIPTOR));
while(TransferDescriptor)
{
//
// get next
//
NextTransferDescriptor = (POHCI_GENERAL_TD)TransferDescriptor->NextLogicalDescriptor;
//
// is there a buffer associated
//
if (TransferDescriptor->BufferSize)
{
//
// release buffer
//
m_DmaManager->Release(TransferDescriptor->BufferLogical, TransferDescriptor->BufferSize);
}
DPRINT("CUSBRequest::FreeEndpointDescriptor Descriptor %p Logical %x Buffer Physical %x EndAddress %x\n", TransferDescriptor, TransferDescriptor->PhysicalAddress.LowPart, TransferDescriptor->BufferPhysical, TransferDescriptor->LastPhysicalByteAddress);
//
// release descriptor
//
m_DmaManager->Release(TransferDescriptor, sizeof(OHCI_GENERAL_TD));
//
// move to next
//
TransferDescriptor = NextTransferDescriptor;
}
}
}
VOID
CUSBRequest::CompletionCallback(
struct _OHCI_ENDPOINT_DESCRIPTOR * OutDescriptor)
{
PIO_STACK_LOCATION IoStack;
PURB Urb;
DPRINT("CUSBRequest::CompletionCallback Descriptor %p PhysicalAddress %x\n", OutDescriptor, OutDescriptor->PhysicalAddress.LowPart);
//
// set status code
//
m_NtStatusCode = STATUS_SUCCESS;
m_UrbStatusCode = USBD_STATUS_SUCCESS;
if (m_Irp)
{
//
// set irp completion status
//
m_Irp->IoStatus.Status = STATUS_SUCCESS; //FIXME
//
// 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_SUCCESS; //FIXME
//
// Check if the MDL was created
//
if (!Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL)
{
//
// Free Mdl
//
IoFreeMdl(m_TransferBufferMDL);
}
//
// FIXME: support status and calculate length
//
//
// 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);
}
}
//-----------------------------------------------------------------------------------------
BOOLEAN
CUSBRequest::IsRequestInitialized()
{
if (m_Irp || m_SetupPacket)
{
//
// request is initialized
//
return TRUE;
}
//
// request is not initialized
//
return FALSE;
}
//-----------------------------------------------------------------------------------------
BOOLEAN
CUSBRequest::IsQueueHeadComplete(
struct _QUEUE_HEAD * QueueHead)
{
UNIMPLEMENTED
return TRUE;
}
//-----------------------------------------------------------------------------------------
NTSTATUS
InternalCreateUSBRequest(
PUSBREQUEST *OutRequest)
{
PUSBREQUEST This;
//
// allocate requests
//
This = new(NonPagedPool, TAG_USBOHCI) 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;
}