reactos/sdk/lib/drivers/wdf/shared/inc/private/common/fxusbpipe.hpp
Victor Perevertkin 8a978a179f
[WDF] Add Windows Driver Framework files
Takern from Microsoft GitHub repo:
d9c6040fe9

Licensed under MIT
2020-11-03 00:06:26 +03:00

747 lines
16 KiB
C++

//
// Copyright (C) Microsoft. All rights reserved.
//
#ifndef _FXUSBPIPE_H_
#define _FXUSBPIPE_H_
#include "FxUsbRequestContext.hpp"
#include "FxUsbInterface.hpp"
//
// Technically, EHCI can support 4MB, but the usb driver stack doesn't
// allocate enough TDs for such a transfer, here I arbitrarily chose 2MB
//
enum FxUsbPipeMaxTransferSize {
FxUsbPipeHighSpeedMaxTransferSize = 2*1024*1024 ,
FxUsbPipeLowSpeedMaxTransferSize = 256 * 1024,
FxUsbPipeControlMaxTransferSize = 4*1024
};
struct FxUsbPipeTransferContext : public FxUsbRequestContext {
FxUsbPipeTransferContext(
__in FX_URB_TYPE UrbType
);
~FxUsbPipeTransferContext(
VOID
);
__checkReturn
NTSTATUS
AllocateUrb(
__in USBD_HANDLE USBDHandle
);
virtual
VOID
Dispose(
VOID
);
virtual
VOID
CopyParameters(
__in FxRequestBase* Request
);
virtual
VOID
StoreAndReferenceMemory(
__in FxRequestBuffer* Buffer
);
virtual
VOID
ReleaseAndRestore(
__in FxRequestBase* Request
);
VOID
SetUrbInfo(
__in USBD_PIPE_HANDLE PipeHandle,
__in ULONG TransferFlags
);
USBD_STATUS
GetUsbdStatus(
VOID
);
ULONG
GetUrbTransferLength(
VOID
)
{
#if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
return m_Urb->TransferBufferLength;
#elif (FX_CORE_MODE == FX_CORE_USER_MODE)
return m_UmUrb.UmUrbBulkOrInterruptTransfer.TransferBufferLength;
#endif
}
private:
USBD_HANDLE m_USBDHandle;
public:
_URB_BULK_OR_INTERRUPT_TRANSFER m_UrbLegacy;
//
// m_Urb will either point to m_UrbLegacy or one allocated by USBD_UrbAllocate
//
_URB_BULK_OR_INTERRUPT_TRANSFER* m_Urb;
PMDL m_PartialMdl;
BOOLEAN m_UnlockPages;
};
struct FxUsbUrbContext : public FxUsbRequestContext {
FxUsbUrbContext(
VOID
);
USBD_STATUS
GetUsbdStatus(
VOID
);
virtual
VOID
StoreAndReferenceMemory(
__in FxRequestBuffer* Buffer
);
virtual
VOID
ReleaseAndRestore(
__in FxRequestBase* Request
);
PURB m_pUrb;
};
struct FxUsbPipeRequestContext : public FxUsbRequestContext {
FxUsbPipeRequestContext(
__in FX_URB_TYPE FxUrbType
);
~FxUsbPipeRequestContext(
VOID
);
__checkReturn
NTSTATUS
AllocateUrb(
__in USBD_HANDLE USBDHandle
);
virtual
VOID
Dispose(
VOID
);
VOID
SetInfo(
__in WDF_USB_REQUEST_TYPE Type,
__in USBD_PIPE_HANDLE PipeHandle,
__in USHORT Function
);
#if (FX_CORE_MODE == FX_CORE_USER_MODE)
VOID
SetInfo(
__in WDF_USB_REQUEST_TYPE Type,
__in WINUSB_INTERFACE_HANDLE WinUsbHandle,
__in UCHAR PipeId,
__in USHORT Function
);
#endif
USBD_STATUS
GetUsbdStatus(
VOID
);
private:
USBD_HANDLE m_USBDHandle;
public:
_URB_PIPE_REQUEST m_UrbLegacy;
//
// m_Urb will either point to m_UrbLegacy or one allocated by USBD_UrbAllocate
//
_URB_PIPE_REQUEST* m_Urb;
};
struct FxUsbPipeRepeatReader {
//
// Request used to send IRPs
//
FxRequest* Request;
//
// IRP out of Request. Store it off so we don't have to call
// Request->GetSubmitIrp() everytime.
//
MdIrp RequestIrp;
//
// The containing parent
//
FxUsbPipeContinuousReader* Parent;
#if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
//
// DPC to queue ourselves to when a read completes so that we don't spin in
// the same thread repeatedly.
//
KDPC Dpc;
#elif (FX_CORE_MODE == FX_CORE_USER_MODE)
//
// Workitem to queue ourselves to when a read completes so that we don't recurse in
// the same thread repeatedly.
//
MxWorkItem m_ReadWorkItem;
//
// Check if the CR got called on a recursive call
//
LONG ThreadOwnerId;
#endif
//
// Event that is set when the reader has completed and is not
//
MxEvent ReadCompletedEvent;
};
#define NUM_PENDING_READS_DEFAULT (2)
#define NUM_PENDING_READS_MAX (10)
//
// Work-item callback flags
//
#define FX_USB_WORKITEM_IN_PROGRESS (0x00000001)
#define FX_USB_WORKITEM_RERUN (0x00000002)
//
// In theory this can be a base class independent of bus type, but this is
// easier for now and there is no need on another bus type yet.
//
struct FxUsbPipeContinuousReader : public FxStump {
public:
FxUsbPipeContinuousReader(
__in FxUsbPipe* Pipe,
__in UCHAR NumReaders
);
~FxUsbPipeContinuousReader();
_Must_inspect_result_
NTSTATUS
Config(
__in PWDF_USB_CONTINUOUS_READER_CONFIG Config,
__in size_t TotalBufferLength
);
PVOID
operator new(
__in size_t Size,
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__range(1, NUM_PENDING_READS_MAX) ULONG NumReaders
);
_Must_inspect_result_
NTSTATUS
FormatRepeater(
__in FxUsbPipeRepeatReader* Repeater
);
VOID
CancelRepeaters(
VOID
);
ULONG
ResubmitRepeater(
__in FxUsbPipeRepeatReader* Repeater,
__out NTSTATUS* Status
);
protected:
VOID
DeleteMemory(
__in FxRequestBase* Request
)
{
FxRequestContext* pContext;
pContext = Request->GetContext();
if (pContext != NULL && pContext->m_RequestMemory != NULL) {
pContext->m_RequestMemory->Delete();
//
// NOTE: Don't NULL out the m_RequestMemory member as this will
// prevent Reuse from releasing a reference on the m_RequestMemory object
// and hence these memory objects will not be freed.
//
}
}
BOOLEAN
QueueWorkItemLocked(
__in FxUsbPipeRepeatReader* Repeater
);
__inline
VOID
FxUsbPipeRequestWorkItemHandler(
__in FxUsbPipeRepeatReader* FailedRepeater
);
static
MdDeferredRoutineType
_FxUsbPipeContinuousReadDpc;
static
MX_WORKITEM_ROUTINE
_ReadWorkItem;
static
EVT_WDF_REQUEST_COMPLETION_ROUTINE
_FxUsbPipeRequestComplete;
static
EVT_SYSTEMWORKITEM
_FxUsbPipeRequestWorkItemThunk;
public:
//
// Completion routine for the client
//
PFN_WDF_USB_READER_COMPLETION_ROUTINE m_ReadCompleteCallback;
//
// Context for completion routine
//
WDFCONTEXT m_ReadCompleteContext;
//
// Callback to invoke when a reader fails
//
PFN_WDF_USB_READERS_FAILED m_ReadersFailedCallback;
//
// The owning pipe
//
FxUsbPipe* m_Pipe;
//
// Lookaside list from which we will allocate buffers for each new read
//
FxLookasideList* m_Lookaside;
//
// The devobj we are sending requests to
//
MdDeviceObject m_TargetDevice;
//
// Offsets and length into the memory buffers created by the lookaside list
//
WDFMEMORY_OFFSET m_Offsets;
//
// Work item to queue when we hit various errors
//
FxSystemWorkItem* m_WorkItem;
//
// Work item re-run context.
//
PVOID m_WorkItemRerunContext;
//
// This is a pointer to the work-item's thread object. This value is
// used for not deadlocking when misbehaved drivers (< v1.9) call
// WdfIoTargetStop from EvtUsbTargetPipeReadersFailed callback.
//
volatile POINTER_ALIGNMENT MxThread m_WorkItemThread;
//
// Work item flags (see FX_USB_WORKITEM_Xxx defines).
//
ULONG m_WorkItemFlags;
//
// Number of readers who have failed due to internal allocation errors
//
UCHAR m_NumFailedReaders;
//
// Number of readers
//
UCHAR m_NumReaders;
//
// Value to use with InterlockedXxx to test to see if a work item has been
// queued or not
//
BOOLEAN m_WorkItemQueued;
//
// Track whether the readers should be submitted when moving into the start
// state. We cannot just track a start -> start transition and not send
// the readers on that particular state transition because the first time
// we need to send the readers, the target is already in the started state
//
BOOLEAN m_ReadersSubmitted;
//
// Open ended array of readers. MUST be the last element in this structure.
//
FxUsbPipeRepeatReader m_Readers[1];
};
class FxUsbPipe : public FxIoTarget {
public:
friend FxUsbDevice;
friend FxUsbInterface;
friend FxUsbPipeContinuousReader;
FxUsbPipe(
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__in FxUsbDevice* UsbDevice
);
VOID
InitPipe(
__in PUSBD_PIPE_INFORMATION PipeInfo,
__in UCHAR InterfaceNumber,
__in FxUsbInterface* UsbInterface
);
#if (FX_CORE_MODE == FX_CORE_USER_MODE)
VOID
InitPipe(
__in PWINUSB_PIPE_INFORMATION PipeInfo,
__in UCHAR InterfaceNumber,
__in FxUsbInterface* UsbInterface
);
#endif
_Must_inspect_result_
virtual
NTSTATUS
GotoStartState(
__in PLIST_ENTRY RequestListHead,
__in BOOLEAN Lock = TRUE
);
virtual
VOID
GotoStopState(
__in WDF_IO_TARGET_SENT_IO_ACTION Action,
__in PSINGLE_LIST_ENTRY SentRequestListHead,
__out PBOOLEAN Wait,
__in BOOLEAN LockSelf
);
VOID
GotoPurgeState(
__in WDF_IO_TARGET_PURGE_IO_ACTION Action,
__in PLIST_ENTRY PendedRequestListHead,
__in PSINGLE_LIST_ENTRY SentRequestListHead,
__out PBOOLEAN Wait,
__in BOOLEAN LockSelf
);
virtual
VOID
GotoRemoveState(
__in WDF_IO_TARGET_STATE NewState,
__in PLIST_ENTRY PendedRequestListHead,
__in PSINGLE_LIST_ENTRY SentRequestListHead,
__in BOOLEAN Lock,
__out PBOOLEAN Wait
);
virtual
VOID
WaitForSentIoToComplete(
VOID
);
__inline
VOID
SetNoCheckPacketSize(
VOID
)
{
m_CheckPacketSize = FALSE;
}
VOID
GetInformation(
__out PWDF_USB_PIPE_INFORMATION PipeInformation
);
BOOLEAN
IsType(
__in WDF_USB_PIPE_TYPE Type
);
WDF_USB_PIPE_TYPE
GetType(
VOID
);
WDFUSBPIPE
GetHandle(
VOID
)
{
return (WDFUSBPIPE) GetObjectHandle();
}
__inline
BOOLEAN
IsInEndpoint(
VOID
)
{
//
// USB_ENDPOINT_DIRECTION_IN just does a bitwise compre so it could
// return 0 or some non zero value. Make sure the non zero value is
// TRUE
//
#if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
return USB_ENDPOINT_DIRECTION_IN(m_PipeInformation.EndpointAddress) ? TRUE : FALSE;
#elif (FX_CORE_MODE == FX_CORE_USER_MODE)
return USB_ENDPOINT_DIRECTION_IN(m_PipeInformationUm.PipeId) ? TRUE : FALSE;
#endif
}
__inline
BOOLEAN
IsOutEndpoint(
VOID
)
{
//
// USB_ENDPOINT_DIRECTION_OUT just does a bitwise compre so it could
// return 0 or some non zero value. Make sure the non zero value is
// TRUE
//
#if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
return USB_ENDPOINT_DIRECTION_OUT(m_PipeInformation.EndpointAddress) ? TRUE : FALSE;
#elif (FX_CORE_MODE == FX_CORE_USER_MODE)
return USB_ENDPOINT_DIRECTION_OUT(m_PipeInformationUm.PipeId) ? TRUE : FALSE;
#endif
}
_Must_inspect_result_
NTSTATUS
InitContinuousReader(
__in PWDF_USB_CONTINUOUS_READER_CONFIG Config,
__in size_t TotalBufferLength
);
ULONG
GetMaxPacketSize(
VOID
)
{
#if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
return m_PipeInformation.MaximumPacketSize;
#elif (FX_CORE_MODE == FX_CORE_USER_MODE)
return m_PipeInformationUm.MaximumPacketSize;
#endif
}
_Must_inspect_result_
NTSTATUS
ValidateTransferLength(
__in size_t Length
)
{
//
// Assumes this is not a control pipe
//
if (m_CheckPacketSize &&
#if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
(Length % m_PipeInformation.MaximumPacketSize) != 0) {
#elif (FX_CORE_MODE == FX_CORE_USER_MODE)
(Length % m_PipeInformationUm.MaximumPacketSize) != 0) {
#endif
return STATUS_INVALID_BUFFER_SIZE;
}
else {
return STATUS_SUCCESS;
}
}
_Must_inspect_result_
NTSTATUS
FormatTransferRequest(
__in FxRequestBase* Request,
__in FxRequestBuffer* Buffer,
__in ULONG TransferFlags = 0
);
_Must_inspect_result_
NTSTATUS
FormatAbortRequest(
__in FxRequestBase* Request
);
_Must_inspect_result_
NTSTATUS
FormatResetRequest(
__in FxRequestBase* Request
);
static
_Must_inspect_result_
NTSTATUS
_FormatTransfer(
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__in WDFUSBPIPE Pipe,
__in WDFREQUEST Request,
__in_opt WDFMEMORY TransferMemory,
__in_opt PWDFMEMORY_OFFSET TransferOffsets,
__in ULONG Flags
);
static
_Must_inspect_result_
NTSTATUS
_SendTransfer(
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__in WDFUSBPIPE Pipe,
__in_opt WDFREQUEST Request,
__in_opt PWDF_REQUEST_SEND_OPTIONS RequestOptions,
__in_opt PWDF_MEMORY_DESCRIPTOR MemoryDescriptor,
__out_opt PULONG BytesTransferred,
__in ULONG Flags
);
USBD_PIPE_HANDLE
WdmGetPipeHandle(
VOID
)
{
return m_PipeInformation.PipeHandle;
}
static
WDF_USB_PIPE_TYPE
_UsbdPipeTypeToWdf(
__in USBD_PIPE_TYPE UsbdPipeType
)
{
const static WDF_USB_PIPE_TYPE types[] = {
WdfUsbPipeTypeControl, // UsbdPipeTypeControl
WdfUsbPipeTypeIsochronous, // UsbdPipeTypeIsochronous
WdfUsbPipeTypeBulk, // UsbdPipeTypeBulk
WdfUsbPipeTypeInterrupt, // UsbdPipeTypeInterrupt
};
if (UsbdPipeType < sizeof(types)/sizeof(types[0])) {
return types[UsbdPipeType];
}
else {
return WdfUsbPipeTypeInvalid;
}
}
NTSTATUS
Reset(
VOID
);
USBD_HANDLE
GetUSBDHandle(
VOID
)
{
return m_USBDHandle;
}
FX_URB_TYPE
GetUrbType(
VOID
)
{
return m_UrbType;
}
public:
//
// Link for FxUsbDevice to use to hold a list of Pipes
//
LIST_ENTRY m_ListEntry;
protected:
~FxUsbPipe();
virtual
BOOLEAN
Dispose(
VOID
);
FxUsbDevice* m_UsbDevice;
FxUsbInterface* m_UsbInterface;
//
// If the pipe does not have a continuous reader, this field is NULL.
// It is also cleared within the pipe's Dispose function after deleting
// the continuous reader to prevent misbehaved drivers from
// crashing the system when they call WdfIoTargetStop from their usb pipe's
// destroy callback.
//
FxUsbPipeContinuousReader* m_Reader;
//
// Information about this pipe
//
USBD_PIPE_INFORMATION m_PipeInformation;
#if (FX_CORE_MODE == FX_CORE_USER_MODE)
WINUSB_PIPE_INFORMATION m_PipeInformationUm;
#endif
//
// Interface associated with this pipe
//
UCHAR m_InterfaceNumber;
//
// Indicates if we should check that the buffer being trasnfered is of a
// multiple of max packet size.
//
BOOLEAN m_CheckPacketSize;
//
// The USBD_HANDLE exchanged by FxUsbDevice
//
USBD_HANDLE m_USBDHandle;
//
// If the client driver submits an URB to do a USB transfer, this field indicates
// the type of that Urb
//
FX_URB_TYPE m_UrbType;
};
#endif // _FXUSBPIPE_H_