[WDF] Add Windows Driver Framework files
[reactos.git] / sdk / lib / drivers / wdf / shared / targets / usb / km / fxusbpipekm.cpp
diff --git a/sdk/lib/drivers/wdf/shared/targets/usb/km/fxusbpipekm.cpp b/sdk/lib/drivers/wdf/shared/targets/usb/km/fxusbpipekm.cpp
new file mode 100644 (file)
index 0000000..acae3b5
--- /dev/null
@@ -0,0 +1,379 @@
+//
+//    Copyright (C) Microsoft.  All rights reserved.
+//
+#include "fxusbpch.hpp"
+
+extern "C" {
+#include "FxUsbPipeKm.tmh"
+}
+
+#include "Fxglobals.h"
+
+VOID
+FxUsbPipeTransferContext::StoreAndReferenceMemory(
+    __in FxRequestBuffer* Buffer
+    )
+/*++
+
+Routine Description:
+    virtual function which stores and references memory if it is an FxObject
+    and then fills in the appropriate fields in the URB.
+
+Arguments:
+    Buffer - union which can be many types of memory
+
+Return Value:
+    None
+
+  --*/
+{
+    RtlZeroMemory(m_Urb, sizeof(*m_Urb));
+
+    m_Urb->Hdr.Function = URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER;
+    m_Urb->Hdr.Length = sizeof(*m_Urb);
+
+    __super::StoreAndReferenceMemory(Buffer);
+
+    Buffer->AssignValues(&m_Urb->TransferBuffer,
+                         &m_Urb->TransferBufferMDL,
+                         &m_Urb->TransferBufferLength);
+
+    //
+    // If we have built a partial MDL, use that instead.  TransferBufferLength
+    // is still valid because the Offsets or length in Buffer will have been
+    // used to create this PartialMdl by the caller.
+    //
+    if (m_PartialMdl != NULL) {
+        m_Urb->TransferBufferMDL = m_PartialMdl;
+    }
+}
+
+__drv_functionClass(KDEFERRED_ROUTINE)
+__drv_maxIRQL(DISPATCH_LEVEL)
+__drv_minIRQL(DISPATCH_LEVEL)
+__drv_requiresIRQL(DISPATCH_LEVEL)
+__drv_sameIRQL
+VOID
+FxUsbPipeContinuousReader::_FxUsbPipeContinuousReadDpc(
+    __in struct _KDPC *Dpc,
+    __in_opt PVOID DeferredContext,
+    __in_opt PVOID SystemArgument1,
+    __in_opt PVOID SystemArgument2
+    )
+{
+    FxUsbPipeRepeatReader* pRepeater;
+    FxUsbPipe* pPipe;
+
+    UNREFERENCED_PARAMETER(DeferredContext);
+    UNREFERENCED_PARAMETER(SystemArgument1);
+    UNREFERENCED_PARAMETER(SystemArgument2);
+
+    #pragma prefast(push);
+
+
+    pRepeater = CONTAINING_RECORD(Dpc, FxUsbPipeRepeatReader, Dpc);
+    pPipe = pRepeater->Parent->m_Pipe;
+
+    //
+    // Ignore the return value because once we have sent the request, we
+    // want all processing to be done in the completion routine.
+    //
+    (void) IoCallDriver(pPipe->m_TargetDevice,
+                        pRepeater->Request->GetSubmitIrp());
+    #pragma prefast(pop);
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxUsbPipeContinuousReader::Config(
+    __in PWDF_USB_CONTINUOUS_READER_CONFIG Config,
+    __in size_t TotalBufferLength
+    )
+{
+    PFX_DRIVER_GLOBALS pFxDriverGlobals;
+    WDF_OBJECT_ATTRIBUTES attributes;
+    NTSTATUS status;
+    LONG i;
+
+    pFxDriverGlobals = m_Pipe->GetDriverGlobals();
+
+    if (TotalBufferLength <= MAXUSHORT) {
+        m_Lookaside = new(pFxDriverGlobals, WDF_NO_OBJECT_ATTRIBUTES)
+            FxNPagedLookasideList(pFxDriverGlobals, pFxDriverGlobals->Tag);
+    }
+    else {
+        m_Lookaside = new(pFxDriverGlobals, WDF_NO_OBJECT_ATTRIBUTES)
+            FxNPagedLookasideListFromPool(pFxDriverGlobals, pFxDriverGlobals->Tag);
+    }
+
+    if (m_Lookaside == NULL) {
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    if (Config->BufferAttributes == NULL) {
+        WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
+    }
+    else {
+        RtlCopyMemory(&attributes,
+                      Config->BufferAttributes,
+                      sizeof(WDF_OBJECT_ATTRIBUTES));
+    }
+
+    //
+    // By specifying the loookaside as the parent for the memory objects that
+    // will be created, when we destroy the lookaside list, we will destroy any
+    // outstanding memory objects that have been allocated.  This can happen if
+    // we initialize the repeater, but never send any i/o.  (Normally the
+    // memory object would be freed when the read completes.)
+    //
+    attributes.ParentObject = m_Lookaside->GetObjectHandle();
+
+    status = m_Lookaside->Initialize(TotalBufferLength, &attributes);
+    if (!NT_SUCCESS(status)) {
+        return status;
+    }
+
+    status = FxSystemWorkItem::_Create(pFxDriverGlobals,
+                                      m_Pipe->m_Device->GetDeviceObject(),
+                                      &m_WorkItem
+                                      );
+    if (!NT_SUCCESS(status)) {
+        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
+                            "Could not allocate workitem: %!STATUS!", status);
+        return status;
+    }
+
+    m_Offsets.BufferLength = Config->TransferLength;
+    m_Offsets.BufferOffset = Config->HeaderLength;
+
+    for (i = 0; i < m_NumReaders; i++) {
+        FxUsbPipeRepeatReader* pRepeater;
+
+        pRepeater = &m_Readers[i];
+
+        pRepeater->Parent = this;
+
+        KeInitializeDpc(&pRepeater->Dpc, _FxUsbPipeContinuousReadDpc, NULL);
+
+        //
+        // This will allocate the PIRP
+        //
+        status = FxRequest::_Create(pFxDriverGlobals,
+                                    WDF_NO_OBJECT_ATTRIBUTES,
+                                    NULL,
+                                    m_Pipe,
+                                    FxRequestOwnsIrp,
+                                    FxRequestConstructorCallerIsFx,
+                                    &pRepeater->Request);
+
+        if (!NT_SUCCESS(status)) {
+            return status;
+        }
+
+        pRepeater->RequestIrp = pRepeater->Request->GetSubmitIrp();
+
+        //
+        // Initialize the event before FormatRepeater clears it
+        //
+        status = pRepeater->ReadCompletedEvent.Initialize(NotificationEvent, TRUE);
+
+        if (!NT_SUCCESS(status)) {
+            DoTraceLevelMessage(
+                pFxDriverGlobals, TRACE_LEVEL_INFORMATION, TRACINGIOTARGET,
+                "Could not initialize ReadCompletedEvent: %!STATUS!",
+                status);
+
+            return status;
+        }
+
+        //
+        // This will allocate the context
+        //
+        status = FormatRepeater(pRepeater);
+
+        if (!NT_SUCCESS(status)) {
+            return status;
+        }
+    }
+
+    return STATUS_SUCCESS;
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxUsbPipe::FormatTransferRequest(
+    __in FxRequestBase* Request,
+    __in FxRequestBuffer* Buffer,
+    __in ULONG TransferFlags
+    )
+{
+    FxUsbPipeTransferContext* pContext;
+    NTSTATUS status;
+    size_t bufferSize;
+    ULONG dummyLength;
+    FX_URB_TYPE urbType;
+
+    //
+    // Make sure request is for the right type
+    //
+    if (!(IsType(WdfUsbPipeTypeBulk) || IsType(WdfUsbPipeTypeInterrupt))) {
+        status = STATUS_INVALID_DEVICE_REQUEST;
+
+        DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
+                            "WDFUSBPIPE %p not the right type, %!STATUS!",
+                            GetHandle(), status);
+
+        return status;
+    }
+
+    bufferSize = Buffer->GetBufferLength();
+
+    status = RtlSizeTToULong(bufferSize, &dummyLength);
+    if (!NT_SUCCESS(status)) {
+        DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
+                            "WDFUSBPIPE %p, buffer size truncated, %!STATUS!",
+                            GetHandle(), status);
+        return status;
+    }
+
+    //
+    // On reads, check to make sure the read in value is an integral number of
+    // packet sizes
+    //
+    if (TransferFlags & USBD_TRANSFER_DIRECTION_IN) {
+        if (IsInEndpoint() == FALSE) {
+            DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
+                                "Pipe %p, sending __in transaction on a __out endpoint",
+                                this);
+
+            return STATUS_INVALID_DEVICE_REQUEST;
+        }
+
+        if (m_CheckPacketSize &&
+            (bufferSize % m_PipeInformation.MaximumPacketSize) != 0) {
+            return STATUS_INVALID_BUFFER_SIZE;
+        }
+    }
+    else {
+        if (IsOutEndpoint() == FALSE) {
+            DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
+                                "Pipe %p, sending __out transaction on an __in endpoint",
+                                this);
+
+            return STATUS_INVALID_DEVICE_REQUEST;
+        }
+    }
+
+    status = Request->ValidateTarget(this);
+    if (!NT_SUCCESS(status)) {
+        DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
+                            "Pipe %p, Request %p, setting target failed, "
+                            "status %!STATUS!", this, Request, status);
+
+        return status;
+    }
+
+    if (Request->HasContextType(FX_RCT_USB_PIPE_XFER)) {
+        pContext = (FxUsbPipeTransferContext*) Request->GetContext();
+    }
+    else {
+        urbType = m_UsbDevice->GetFxUrbTypeForRequest(Request);
+
+        pContext = new(GetDriverGlobals()) FxUsbPipeTransferContext(urbType);
+        if (pContext == NULL) {
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        if (urbType == FxUrbTypeUsbdAllocated) {
+            status = pContext->AllocateUrb(m_USBDHandle);
+            if (!NT_SUCCESS(status)) {
+                delete pContext;
+                return status;
+            }
+            //
+            // Since the AllocateUrb routine calls USBD_xxxUrbAllocate APIs to allocate an Urb, it is
+            // important to release those resorces before the devnode is removed. Those
+            // resoruces are removed at the time Request is disposed.
+            //
+            Request->EnableContextDisposeNotification();
+        }
+
+        Request->SetContext(pContext);
+    }
+
+    //
+    // Always set the memory after determining the context.  This way we can
+    // free a previously referenced memory object if necessary.
+    //
+    if (Buffer->HasMdl()) {
+        PMDL pMdl;
+
+        pMdl=NULL;
+        ASSERT(pContext->m_PartialMdl == NULL);
+
+        //
+        // If it is an __in endpoint, the buffer will be written to by the
+        // controller, so request IoWriteAccess locking.
+        //
+        status = Buffer->GetOrAllocateMdl(
+            GetDriverGlobals(),
+            &pMdl,
+            &pContext->m_PartialMdl,
+            &pContext->m_UnlockPages,
+            IsInEndpoint() ? IoWriteAccess : IoReadAccess);
+
+        if (!NT_SUCCESS(status)) {
+            return status;
+        }
+
+        ASSERT(pMdl != NULL);
+    }
+
+    pContext->StoreAndReferenceMemory(Buffer);
+
+    pContext->SetUrbInfo(m_PipeInformation.PipeHandle, TransferFlags);
+
+    if (pContext->m_Urb == &pContext->m_UrbLegacy) {
+        urbType = FxUrbTypeLegacy;
+    }
+    else {
+        urbType = FxUrbTypeUsbdAllocated;
+    }
+
+    FxFormatUsbRequest(Request, (PURB)pContext->m_Urb, urbType, m_USBDHandle);
+
+    return STATUS_SUCCESS;
+}
+
+VOID
+FxUsbPipe::GetInformation(
+    __out PWDF_USB_PIPE_INFORMATION PipeInformation
+    )
+{
+    //
+    // Do a field by field copy for the WDF structure, since fields could change.
+    //
+    PipeInformation->MaximumPacketSize = m_PipeInformation.MaximumPacketSize;
+    PipeInformation->EndpointAddress = m_PipeInformation.EndpointAddress;
+    PipeInformation->Interval = m_PipeInformation.Interval;
+    PipeInformation->PipeType = _UsbdPipeTypeToWdf(m_PipeInformation.PipeType);
+    PipeInformation->MaximumTransferSize = m_PipeInformation.MaximumTransferSize;
+    PipeInformation->SettingIndex = m_UsbInterface->GetConfiguredSettingIndex();
+}
+
+WDF_USB_PIPE_TYPE
+FxUsbPipe::GetType(
+    VOID
+    )
+{
+    return _UsbdPipeTypeToWdf(m_PipeInformation.PipeType);
+}
+
+BOOLEAN
+FxUsbPipe::IsType(
+    __in WDF_USB_PIPE_TYPE Type
+    )
+{
+    return _UsbdPipeTypeToWdf(m_PipeInformation.PipeType) == Type ? TRUE : FALSE;
+}
+