mirror of
https://github.com/reactos/reactos.git
synced 2025-03-10 10:14:44 +00:00

Not all files are included, but these are necessary to compile cdrom driver. So far it can only be statically linked with drivers, a proper implementation requires wdfldr helper driver
747 lines
16 KiB
C++
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_
|