// // 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); FxUsbRequestContext::StoreAndReferenceMemory(Buffer); // __super call 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; }