mirror of
https://github.com/reactos/reactos.git
synced 2024-07-14 00:25:05 +00:00
549 lines
13 KiB
C++
549 lines
13 KiB
C++
![]() |
/*
|
||
|
* 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 VOID GetResultStatus(OUT OPTIONAL NTSTATUS *NtStatusCode, OUT OPTIONAL PULONG UrbStatusCode);
|
||
|
virtual BOOLEAN IsRequestInitialized();
|
||
|
virtual BOOLEAN IsQueueHeadComplete(struct _QUEUE_HEAD * QueueHead);
|
||
|
|
||
|
|
||
|
// local functions
|
||
|
ULONG InternalGetTransferType();
|
||
|
UCHAR InternalGetPidDirection();
|
||
|
UCHAR GetDeviceAddress();
|
||
|
NTSTATUS BuildSetupPacket();
|
||
|
NTSTATUS BuildSetupPacketFromURB();
|
||
|
|
||
|
// 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)
|
||
|
{
|
||
|
//
|
||
|
// 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();
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------------------
|
||
|
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::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::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;
|
||
|
}
|