USBDK/UsbDk/RedirectorStrategy.cpp
Dmitry Fleytman 32aadcb4fe RedirectorStrategy: Ensure there are no short buffers on read
To avoid "BABBLE" conditions.

Signed-off-by: Dmitry Fleytman <dfleytma@redhat.com>
2015-12-24 11:25:31 +02:00

786 lines
30 KiB
C++

/**********************************************************************
* Copyright (c) 2013-2014 Red Hat, Inc.
*
* Developed by Daynix Computing LTD.
*
* Authors:
* Dmitry Fleytman <dmitry@daynix.com>
* Pavel Gurvich <pavel@daynix.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
**********************************************************************/
#include "stdafx.h"
enum UsbDkTransferDirection
{
Unknown,
Read,
Write
};
#include "RedirectorStrategy.h"
#include "trace.h"
#include "RedirectorStrategy.tmh"
#include "FilterDevice.h"
#include "UsbDkNames.h"
#include "ControlDevice.h"
#include "WdfRequest.h"
NTSTATUS CUsbDkRedirectorStrategy::MakeAvailable()
{
auto status = m_Target.Create(m_Owner->WdfObject());
if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_REDIRECTOR, "%!FUNC! Cannot create USB target");
return status;
}
status = m_ControlDevice->NotifyRedirectorAttached(m_DeviceID, m_InstanceID, m_Owner);
if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_REDIRECTOR, "%!FUNC! Failed to raise creation notification");
}
return status;
}
NTSTATUS CUsbDkRedirectorStrategy::Create(CUsbDkFilterDevice *Owner)
{
auto status = CUsbDkFilterStrategy::Create(Owner);
if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_REDIRECTOR, "%!FUNC! Base class creation failed");
return status;
}
status = m_IncomingDataQueue.Create(*m_Owner);
if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_REDIRECTOR, "%!FUNC! RW Queue creation failed");
}
status = m_IncomingConfigQueue.Create(*m_Owner);
if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_REDIRECTOR, "%!FUNC! IOCTL Queue creation failed");
}
return status;
}
void CUsbDkRedirectorStrategy::PatchDeviceID(PIRP Irp)
{
static const WCHAR RedirectorDeviceId[] = L"USB\\Vid_2B23&Pid_CAFE&Rev_0001";
static const WCHAR RedirectorHardwareIds[] = L"USB\\Vid_2B23&Pid_CAFE&Rev_0001\0USB\\Vid_2B23&Pid_CAFE\0";
static const WCHAR RedirectorCompatibleIds[] = L"USB\\Class_FF&SubClass_FF&Prot_FF\0USB\\Class_FF&SubClass_FF\0USB\\Class_FF\0";
static const size_t MAX_DEC_NUMBER_LEN = 11;
WCHAR SzInstanceID[ARRAY_SIZE(USBDK_DRIVER_NAME) + MAX_DEC_NUMBER_LEN + 1];
const WCHAR *Buffer;
SIZE_T Size = 0;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
switch (irpStack->Parameters.QueryId.IdType)
{
case BusQueryDeviceID:
Buffer = &RedirectorDeviceId[0];
Size = sizeof(RedirectorDeviceId);
break;
case BusQueryInstanceID:
{
CString InstanceID;
auto status = InstanceID.Create(USBDK_DRIVER_NAME, m_Owner->GetInstanceNumber());
if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_REDIRECTOR, "%!FUNC! Failed to create instance ID string %!STATUS!", status);
return;
}
Size = InstanceID.ToWSTR(SzInstanceID, sizeof(SzInstanceID));
Buffer = &SzInstanceID[0];
break;
}
case BusQueryHardwareIDs:
Buffer = &RedirectorHardwareIds[0];
Size = sizeof(RedirectorHardwareIds);
break;
case BusQueryCompatibleIDs:
Buffer = &RedirectorCompatibleIds[0];
Size = sizeof(RedirectorCompatibleIds);
break;
default:
Buffer = nullptr;
break;
}
if (Buffer != nullptr)
{
auto Result = DuplicateStaticBuffer(Buffer, Size);
if (Result == nullptr)
{
return;
}
if (Irp->IoStatus.Information)
{
ExFreePool(reinterpret_cast<PVOID>(Irp->IoStatus.Information));
}
Irp->IoStatus.Information = reinterpret_cast<ULONG_PTR>(Result);
}
}
NTSTATUS CUsbDkRedirectorStrategy::PNPPreProcess(PIRP Irp)
{
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
switch (irpStack->MinorFunction)
{
case IRP_MN_QUERY_ID:
return PostProcessOnSuccess(Irp,
[this](PIRP Irp)
{
PatchDeviceID(Irp);
});
case IRP_MN_QUERY_CAPABILITIES:
return PostProcessOnSuccess(Irp,
[](PIRP Irp)
{
auto irpStack = IoGetCurrentIrpStackLocation(Irp);
irpStack->Parameters.DeviceCapabilities.Capabilities->RawDeviceOK = 1;
irpStack->Parameters.DeviceCapabilities.Capabilities->NoDisplayInUI = 1;
irpStack->Parameters.DeviceCapabilities.Capabilities->Removable = 0;
irpStack->Parameters.DeviceCapabilities.Capabilities->EjectSupported = 0;
irpStack->Parameters.DeviceCapabilities.Capabilities->SilentInstall = 1;
});
default:
return CUsbDkFilterStrategy::PNPPreProcess(Irp);
}
}
typedef struct tag_USBDK_REDIRECTOR_REQUEST_CONTEXT
{
bool PreprocessingDone;
WDFMEMORY LockedBuffer;
ULONG64 EndpointAddress;
USB_DK_TRANSFER_TYPE TransferType;
PUSB_DK_GEN_TRANSFER_RESULT GenResult;
WDF_USB_CONTROL_SETUP_PACKET SetupPacket;
UsbDkTransferDirection Direction;
WDFMEMORY LockedIsochronousPacketsArray;
WDFMEMORY LockedIsochronousResultsArray;
WDFMEMORY ShadowBuffer;
PVOID ShadowBufferPtr;
} USBDK_REDIRECTOR_REQUEST_CONTEXT, *PUSBDK_REDIRECTOR_REQUEST_CONTEXT;
class CRedirectorRequest : public CWdfRequest
{
public:
explicit CRedirectorRequest(WDFREQUEST Request)
: CWdfRequest(Request)
{}
PUSBDK_REDIRECTOR_REQUEST_CONTEXT Context() const
{ return reinterpret_cast<PUSBDK_REDIRECTOR_REQUEST_CONTEXT>(UsbDkFilterRequestGetContext(m_Request)); }
private:
void SetBytesWritten(size_t numBytes);
void SetBytesRead(size_t numBytes);
};
template <typename TLockerFunc>
NTSTATUS CUsbDkRedirectorStrategy::IoInCallerContextRW(CRedirectorRequest &WdfRequest,
TLockerFunc LockerFunc)
{
PUSB_DK_TRANSFER_REQUEST TransferRequest;
auto status = WdfRequest.FetchInputObject(TransferRequest);
if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_REDIRECTOR, "%!FUNC! failed to read transfer request, %!STATUS!", status);
return status;
}
PUSBDK_REDIRECTOR_REQUEST_CONTEXT Context = WdfRequest.Context();
Context->EndpointAddress = TransferRequest->EndpointAddress;
Context->TransferType = static_cast<USB_DK_TRANSFER_TYPE>(TransferRequest->TransferType);
status = WdfRequest.FetchOutputObject(Context->GenResult);
if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_REDIRECTOR, "%!FUNC! failed to fetch output buffer, %!STATUS!", status);
return status;
}
switch (Context->TransferType)
{
case ControlTransferType:
status = IoInCallerContextRWControlTransfer(WdfRequest, *TransferRequest);
break;
case BulkTransferType:
case InterruptTransferType:
status = LockerFunc(WdfRequest, *TransferRequest, Context->LockedBuffer);
if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_REDIRECTOR, "%!FUNC! Failed to lock user buffer, %!STATUS!", status);
}
break;
case IsochronousTransferType:
status = IoInCallerContextRWIsoTransfer(WdfRequest, *TransferRequest, LockerFunc);
break;
default:
TraceEvents(TRACE_LEVEL_ERROR, TRACE_REDIRECTOR, "%!FUNC! Error: Wrong TransferType: %d", Context->TransferType);
status = STATUS_INVALID_PARAMETER;
break;
}
return status;
}
template <typename TLockerFunc>
NTSTATUS CUsbDkRedirectorStrategy::IoInCallerContextRWIsoTransfer(CRedirectorRequest &WdfRequest,
const USB_DK_TRANSFER_REQUEST &TransferRequest,
TLockerFunc LockerFunc)
{
PUSBDK_REDIRECTOR_REQUEST_CONTEXT Context = WdfRequest.Context();
auto status = LockerFunc(WdfRequest, TransferRequest, Context->LockedBuffer);
if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_REDIRECTOR, "%!FUNC! Failed to lock user buffer, %!STATUS!", status);
return status;
}
#pragma warning(push)
#pragma warning(disable:4244) //Unsafe conversions on 32 bit
status = WdfRequest.LockUserBufferForRead(reinterpret_cast<PVOID>(TransferRequest.IsochronousPacketsArray),
sizeof(ULONG64) * TransferRequest.IsochronousPacketsArraySize, Context->LockedIsochronousPacketsArray);
#pragma warning(pop)
if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_REDIRECTOR, "%!FUNC! Failed to lock user buffer of Iso Packet Lengths, %!STATUS!", status);
return status;
}
#pragma warning(push)
#pragma warning(disable:4244) //Unsafe conversions on 32 bit
status = WdfRequest.LockUserBufferForWrite(reinterpret_cast<PVOID>(TransferRequest.Result.IsochronousResultsArray),
sizeof(USB_DK_ISO_TRANSFER_RESULT) * TransferRequest.IsochronousPacketsArraySize, Context->LockedIsochronousResultsArray);
#pragma warning(pop)
if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_REDIRECTOR, "%!FUNC! Failed to lock user buffer of Iso Packet Result, %!STATUS!", status);
}
return status;
}
NTSTATUS CUsbDkRedirectorStrategy::IoInCallerContextRWControlTransfer(CRedirectorRequest &WdfRequest,
const USB_DK_TRANSFER_REQUEST &TransferRequest)
{
NTSTATUS status;
PUSBDK_REDIRECTOR_REQUEST_CONTEXT Context = WdfRequest.Context();
__try
{
#pragma warning(push)
#pragma warning(disable:4244) //Unsafe conversions on 32 bit
ProbeForRead(static_cast<PVOID>(TransferRequest.Buffer), sizeof(WDF_USB_CONTROL_SETUP_PACKET), 1);
Context->SetupPacket = *static_cast<PWDF_USB_CONTROL_SETUP_PACKET>(TransferRequest.Buffer);
#pragma warning(pop)
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_REDIRECTOR, "%!FUNC! ProbeForRead failed!");
return STATUS_ACCESS_VIOLATION;
}
size_t bufferLength = static_cast<size_t>(TransferRequest.BufferLength) - sizeof(WDF_USB_CONTROL_SETUP_PACKET);
if (Context->SetupPacket.Packet.bm.Request.Dir == BMREQUEST_HOST_TO_DEVICE) // write
{
#pragma warning(push)
#pragma warning(disable:4244) //Unsafe conversions on 32 bit
status = WdfRequest.LockUserBufferForRead(static_cast<PVOID>(static_cast<PWDF_USB_CONTROL_SETUP_PACKET>(TransferRequest.Buffer) + 1),
bufferLength, Context->LockedBuffer);
#pragma warning(pop)
if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_REDIRECTOR, "%!FUNC! LockUserBufferForRead failed %!STATUS!", status);
}
}
else // read
{
#pragma warning(push)
#pragma warning(disable:4244) //Unsafe conversions on 32 bit
status = WdfRequest.LockUserBufferForWrite(static_cast<PVOID>(static_cast<PWDF_USB_CONTROL_SETUP_PACKET>(TransferRequest.Buffer) + 1),
bufferLength, Context->LockedBuffer);
#pragma warning(pop)
if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_REDIRECTOR, "%!FUNC! LockUserBufferForWrite failed %!STATUS!", status);
}
}
return status;
}
void CUsbDkRedirectorStrategy::IoInCallerContext(WDFDEVICE Device, WDFREQUEST Request)
{
CRedirectorRequest WdfRequest(Request);
WDF_REQUEST_PARAMETERS Params;
WdfRequest.GetParameters(Params);
NTSTATUS status = STATUS_SUCCESS;
if (Params.Type == WdfRequestTypeDeviceControl)
{
switch (Params.Parameters.DeviceIoControl.IoControlCode)
{
case IOCTL_USBDK_DEVICE_READ_PIPE:
status = IoInCallerContextRW(WdfRequest,
[](const CRedirectorRequest &WdfRequest, const USB_DK_TRANSFER_REQUEST &Transfer, WDFMEMORY &LockedMemory)
#pragma warning(push)
#pragma warning(disable:4244) //Unsafe conversions on 32 bit
{ return WdfRequest.LockUserBufferForWrite(Transfer.Buffer, Transfer.BufferLength, LockedMemory); });
#pragma warning(pop)
break;
case IOCTL_USBDK_DEVICE_WRITE_PIPE:
status = IoInCallerContextRW(WdfRequest,
[](const CRedirectorRequest &WdfRequest, const USB_DK_TRANSFER_REQUEST &Transfer, WDFMEMORY &LockedMemory)
#pragma warning(push)
#pragma warning(disable:4244) //Unsafe conversions on 32 bit
{ return WdfRequest.LockUserBufferForRead(Transfer.Buffer, Transfer.BufferLength, LockedMemory); });
#pragma warning(pop)
break;
default:
break;
}
}
WdfRequest.Context()->PreprocessingDone = true;
if (NT_SUCCESS(status))
{
CUsbDkFilterStrategy::IoInCallerContext(Device, WdfRequest.Detach());
}
else
{
WdfRequest.SetStatus(status);
}
}
void CUsbDkRedirectorStrategy::CompleteTransferRequest(CRedirectorRequest &Request,
NTSTATUS Status,
USBD_STATUS UsbdStatus,
size_t BytesTransferred)
{
auto RequestContext = Request.Context();
RequestContext->GenResult->BytesTransferred = BytesTransferred;
RequestContext->GenResult->UsbdStatus = UsbdStatus;
Request.SetOutputDataLen(sizeof(*RequestContext->GenResult));
Request.SetStatus((UsbdStatus == USBD_STATUS_SUCCESS) ? Status : STATUS_SUCCESS);
}
void CUsbDkRedirectorStrategy::DoControlTransfer(CRedirectorRequest &WdfRequest, WDFMEMORY DataBuffer)
{
PUSBDK_REDIRECTOR_REQUEST_CONTEXT Context = WdfRequest.Context();
WDFMEMORY_OFFSET TransferOffset;
TransferOffset.BufferOffset = 0;
if (DataBuffer != WDF_NO_HANDLE)
{
CPreAllocatedWdfMemoryBuffer MemoryBuffer(DataBuffer);
TransferOffset.BufferLength = static_cast<ULONG>(MemoryBuffer.Size());
}
else
{
TransferOffset.BufferLength = 0;
}
Context->Direction = UsbDkTransferDirection::Unknown;
auto status = m_Target.ControlTransferAsync(WdfRequest, &Context->SetupPacket, DataBuffer, &TransferOffset,
[](WDFREQUEST Request, WDFIOTARGET, PWDF_REQUEST_COMPLETION_PARAMS Params, WDFCONTEXT)
{
auto status = Params->IoStatus.Status;
auto usbCompletionParams = Params->Parameters.Usb.Completion;
CRedirectorRequest WdfRequest(Request);
if (!NT_SUCCESS(status) || !USBD_SUCCESS(usbCompletionParams->UsbdStatus))
{
TraceTransferError(WdfRequest, status, usbCompletionParams->UsbdStatus);
}
CompleteTransferRequest(WdfRequest, status,
usbCompletionParams->UsbdStatus,
usbCompletionParams->Parameters.DeviceControlTransfer.Length);
});
WdfRequest.SetStatus(status);
}
void CUsbDkRedirectorStrategy::IoDeviceControl(WDFREQUEST Request,
size_t OutputBufferLength,
size_t InputBufferLength,
ULONG IoControlCode)
{
UNREFERENCED_PARAMETER(InputBufferLength);
UNREFERENCED_PARAMETER(OutputBufferLength);
switch (IoControlCode)
{
default:
{
CWdfRequest(Request).ForwardToIoQueue(m_IncomingConfigQueue);
break;
}
case IOCTL_USBDK_DEVICE_READ_PIPE:
{
ReadPipe(Request);
break;
}
case IOCTL_USBDK_DEVICE_WRITE_PIPE:
{
WritePipe(Request);
break;
}
}
}
void CUsbDkRedirectorStrategy::IoDeviceControlConfig(WDFREQUEST Request,
size_t OutputBufferLength,
size_t InputBufferLength,
ULONG IoControlCode)
{
switch (IoControlCode)
{
default:
{
CUsbDkFilterStrategy::IoDeviceControl(Request, OutputBufferLength, InputBufferLength, IoControlCode);
return;
}
case IOCTL_USBDK_DEVICE_ABORT_PIPE:
{
CWdfRequest WdfRequest(Request);
UsbDkHandleRequestWithInput<ULONG64>(WdfRequest,
[this, Request](ULONG64 *endpointAddress, size_t)
{return m_Target.AbortPipe(Request, *endpointAddress); });
return;
}
case IOCTL_USBDK_DEVICE_RESET_PIPE:
{
CWdfRequest WdfRequest(Request);
UsbDkHandleRequestWithInput<ULONG64>(WdfRequest,
[this, Request](ULONG64 *endpointAddress, size_t)
{return m_Target.ResetPipe(Request, *endpointAddress); });
return;
}
case IOCTL_USBDK_DEVICE_SET_ALTSETTING:
{
m_IncomingDataQueue.StopSync();
CWdfRequest WdfRequest(Request);
UsbDkHandleRequestWithInput<USBDK_ALTSETTINGS_IDXS>(WdfRequest,
[this, Request](USBDK_ALTSETTINGS_IDXS *altSetting, size_t)
{return m_Target.SetInterfaceAltSetting(altSetting->InterfaceIdx, altSetting->AltSettingIdx);});
m_IncomingDataQueue.Start();
return;
}
case IOCTL_USBDK_DEVICE_RESET_DEVICE:
{
CWdfRequest WdfRequest(Request);
auto status = m_Target.ResetDevice(Request);
WdfRequest.SetStatus(status);
return;
}
}
}
void CUsbDkRedirectorStrategy::WritePipe(WDFREQUEST Request)
{
CRedirectorRequest WdfRequest(Request);
PUSBDK_REDIRECTOR_REQUEST_CONTEXT Context = WdfRequest.Context();
if (!Context->PreprocessingDone)
{
//May happen if EvtioInCallerContext wasn't called due to
//lack of memory of other resources
WdfRequest.SetStatus(STATUS_INSUFFICIENT_RESOURCES);
return;
}
Context->Direction = UsbDkTransferDirection::Write;
switch (Context->TransferType)
{
case ControlTransferType:
DoControlTransfer(WdfRequest, Context->LockedBuffer);
break;
case BulkTransferType:
case InterruptTransferType:
{
m_Target.WritePipeAsync(WdfRequest.Detach(), Context->EndpointAddress, Context->LockedBuffer,
[](WDFREQUEST Request, WDFIOTARGET, PWDF_REQUEST_COMPLETION_PARAMS Params, WDFCONTEXT)
{
auto status = Params->IoStatus.Status;
auto usbCompletionParams = Params->Parameters.Usb.Completion;
CRedirectorRequest WdfRequest(Request);
if (!NT_SUCCESS(status) || !USBD_SUCCESS(usbCompletionParams->UsbdStatus))
{
TraceTransferError(WdfRequest, status, usbCompletionParams->UsbdStatus);
}
CompleteTransferRequest(WdfRequest, status,
usbCompletionParams->UsbdStatus,
usbCompletionParams->Parameters.PipeWrite.Length);
});
}
break;
case IsochronousTransferType:
{
CPreAllocatedWdfMemoryBufferT<ULONG64> PacketSizesArray(Context->LockedIsochronousPacketsArray);
m_Target.WriteIsochronousPipeAsync(WdfRequest.Detach(),
Context->EndpointAddress,
Context->LockedBuffer,
PacketSizesArray,
PacketSizesArray.ArraySize(),
IsoRWCompletion);
}
break;
default:
TraceEvents(TRACE_LEVEL_ERROR, TRACE_REDIRECTOR, "%!FUNC! Error: Wrong transfer type: %d\n", Context->TransferType);
WdfRequest.SetStatus(STATUS_INVALID_PARAMETER);
}
}
void CUsbDkRedirectorStrategy::TraceTransferError(const CRedirectorRequest &WdfRequest,
NTSTATUS Status,
USBD_STATUS UsbdStatus)
{
auto Context = WdfRequest.Context();
CPreAllocatedWdfMemoryBuffer DataBuffer(Context->LockedBuffer);
if (Context->TransferType == ControlTransferType)
{
ASSERT(Context->Direction == UsbDkTransferDirection::Unknown);
Context->Direction =
(Context->SetupPacket.Packet.bm.Request.Dir == BMREQUEST_HOST_TO_DEVICE) ? UsbDkTransferDirection::Write
: UsbDkTransferDirection::Read;
}
else
{
ASSERT(Context->Direction != UsbDkTransferDirection::Unknown);
}
TraceEvents(TRACE_LEVEL_ERROR, TRACE_REDIRECTOR,
"%!FUNC! %!usbdktransferdirection! transfer failed: %!STATUS! UsbdStatus 0x%x, "
"Endpoint address %llu, "
"Transfer type %!usbdktransfertype!, "
"Length %llu",
Context->Direction,
Status, UsbdStatus,
Context->EndpointAddress,
Context->TransferType,
DataBuffer.Size());
}
bool CUsbDkRedirectorStrategy::SupplyMaxPacketSizeShadowBuffer(CRedirectorRequest &WdfRequest,
PUSBDK_REDIRECTOR_REQUEST_CONTEXT RequestContext)
{
CPreAllocatedWdfMemoryBuffer OrigBuffer(RequestContext->LockedBuffer);
auto MaxPacketSize = m_Target.GetPipeMaxPacketSize(RequestContext->EndpointAddress);
if (OrigBuffer.Size() < MaxPacketSize)
{
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_REDIRECTOR,
"%!FUNC! Going to allocate shadow buffer of %llu bytes intead of %llu bytes for pipe %llu",
MaxPacketSize, OrigBuffer.Size(), RequestContext->EndpointAddress);
auto status = WdfMemoryCreate(WDF_NO_OBJECT_ATTRIBUTES,
NonPagedPool,
'MBSU',
MaxPacketSize,
&RequestContext->ShadowBuffer,
&RequestContext->ShadowBufferPtr);
if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_REDIRECTOR,
"%!FUNC! Failed to allocate shadow buffer of %llu bytes: %!STATUS!", MaxPacketSize, status);
WdfRequest.SetStatus(status);
return false;
}
}
return true;
}
void CUsbDkRedirectorStrategy::RetireMaxPacketSizeShadowBuffer(PUSBDK_REDIRECTOR_REQUEST_CONTEXT RequestContext)
{
if (RequestContext->ShadowBufferPtr != nullptr)
{
ASSERT(RequestContext->Direction == UsbDkTransferDirection::Read);
ASSERT((RequestContext->TransferType == BulkTransferType) ||
(RequestContext->TransferType == InterruptTransferType));
CPreAllocatedWdfMemoryBuffer OrigBuffer(RequestContext->LockedBuffer);
RtlCopyMemory(OrigBuffer.Ptr(), RequestContext->ShadowBufferPtr, OrigBuffer.Size());
WdfObjectDelete(RequestContext->ShadowBuffer);
}
}
void CUsbDkRedirectorStrategy::ReadPipe(WDFREQUEST Request)
{
CRedirectorRequest WdfRequest(Request);
PUSBDK_REDIRECTOR_REQUEST_CONTEXT Context = WdfRequest.Context();
if (!Context->PreprocessingDone)
{
//May happen if EvtioInCallerContext wasn't called due to
//lack of memory of other resources
WdfRequest.SetStatus(STATUS_INSUFFICIENT_RESOURCES);
return;
}
Context->Direction = UsbDkTransferDirection::Read;
switch (Context->TransferType)
{
case ControlTransferType:
DoControlTransfer(WdfRequest, Context->LockedBuffer);
break;
case BulkTransferType:
case InterruptTransferType:
{
if (!SupplyMaxPacketSizeShadowBuffer(WdfRequest, Context))
{
break;
}
auto DataBuffer = (Context->ShadowBufferPtr != nullptr) ? Context->ShadowBuffer : Context->LockedBuffer;
m_Target.ReadPipeAsync(WdfRequest.Detach(), Context->EndpointAddress, DataBuffer,
[](WDFREQUEST Request, WDFIOTARGET, PWDF_REQUEST_COMPLETION_PARAMS Params, WDFCONTEXT)
{
auto status = Params->IoStatus.Status;
auto usbCompletionParams = Params->Parameters.Usb.Completion;
CRedirectorRequest WdfRequest(Request);
if (!NT_SUCCESS(status) || !USBD_SUCCESS(usbCompletionParams->UsbdStatus))
{
TraceTransferError(WdfRequest, status, usbCompletionParams->UsbdStatus);
}
RetireMaxPacketSizeShadowBuffer(WdfRequest.Context());
CompleteTransferRequest(WdfRequest, status,
usbCompletionParams->UsbdStatus,
usbCompletionParams->Parameters.PipeRead.Length);
});
}
break;
case IsochronousTransferType:
{
CPreAllocatedWdfMemoryBufferT<ULONG64> PacketSizesArray(Context->LockedIsochronousPacketsArray);
m_Target.ReadIsochronousPipeAsync(WdfRequest.Detach(),
Context->EndpointAddress,
Context->LockedBuffer,
PacketSizesArray,
PacketSizesArray.ArraySize(),
IsoRWCompletion);
}
break;
default:
TraceEvents(TRACE_LEVEL_ERROR, TRACE_REDIRECTOR, "%!FUNC! Error: Wrong transfer type: %d\n", Context->TransferType);
WdfRequest.SetStatus(STATUS_INVALID_PARAMETER);
}
}
void CUsbDkRedirectorStrategy::IsoRWCompletion(WDFREQUEST Request, WDFIOTARGET, PWDF_REQUEST_COMPLETION_PARAMS CompletionParams, WDFCONTEXT)
{
CRedirectorRequest WdfRequest(Request);
auto Context = WdfRequest.Context();
CPreAllocatedWdfMemoryBufferT<URB> urb(CompletionParams->Parameters.Usb.Completion->Parameters.PipeUrb.Buffer);
CPreAllocatedWdfMemoryBufferT<USB_DK_ISO_TRANSFER_RESULT> IsoPacketResult(Context->LockedIsochronousResultsArray);
ASSERT(urb->UrbIsochronousTransfer.NumberOfPackets == IsoPacketResult.ArraySize());
Context->GenResult->UsbdStatus = urb->UrbHeader.Status;
Context->GenResult->BytesTransferred = 0;
for (ULONG i = 0; i < urb->UrbIsochronousTransfer.NumberOfPackets; i++)
{
IsoPacketResult[i].ActualLength = urb->UrbIsochronousTransfer.IsoPacket[i].Length;
IsoPacketResult[i].TransferResult = urb->UrbIsochronousTransfer.IsoPacket[i].Status;
Context->GenResult->BytesTransferred += IsoPacketResult[i].ActualLength;
}
auto status = CompletionParams->IoStatus.Status;
if (!NT_SUCCESS(status) || !USBD_SUCCESS(urb->UrbHeader.Status))
{
TraceTransferError(WdfRequest, status, urb->UrbHeader.Status);
}
WdfRequest.SetOutputDataLen(sizeof(*Context->GenResult));
WdfRequest.SetStatus(USBD_SUCCESS(urb->UrbHeader.Status) ? status : STATUS_SUCCESS);
}
size_t CUsbDkRedirectorStrategy::GetRequestContextSize()
{
return sizeof(USBDK_REDIRECTOR_REQUEST_CONTEXT);
}
void CUsbDkRedirectorStrategy::OnClose()
{
USB_DK_DEVICE_ID ID;
UsbDkFillIDStruct(&ID, *m_DeviceID->begin(), *m_InstanceID->begin());
auto status = m_ControlDevice->RemoveRedirect(ID);
if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_REDIRECTOR, "%!FUNC! RemoveRedirect failed: %!STATUS!", status);
}
}
void CUsbDkRedirectorQueueData::SetCallbacks(WDF_IO_QUEUE_CONFIG &QueueConfig)
{
QueueConfig.EvtIoDeviceControl = [](WDFQUEUE Q, WDFREQUEST R, size_t OL, size_t IL, ULONG CTL)
{ UsbDkFilterGetContext(WdfIoQueueGetDevice(Q))->UsbDkFilter->m_Strategy->IoDeviceControl(R, OL, IL, CTL); };
}
void CUsbDkRedirectorQueueConfig::SetCallbacks(WDF_IO_QUEUE_CONFIG &QueueConfig)
{
QueueConfig.EvtIoDeviceControl = [](WDFQUEUE Q, WDFREQUEST R, size_t OL, size_t IL, ULONG CTL)
{ UsbDkFilterGetContext(WdfIoQueueGetDevice(Q))->UsbDkFilter->m_Strategy->IoDeviceControlConfig(R, OL, IL, CTL); };
}