From 225c72d5434400409487db3803328a0e51c3a7cf Mon Sep 17 00:00:00 2001 From: Sam Tertzakian <32625754+samtertzakian@users.noreply.github.com> Date: Fri, 26 Feb 2021 13:06:53 -0800 Subject: [PATCH] 1. Correct SAL issues in DMF_ThreadedBufferQueue. (#155) 2. Correct issue in DMF_VirtualHidMini that causes legacy devices with no report id to fail in User-mode (but not Kernel -mode). 3. Add DMF_UdeClient Module to make it easier to create virtual USB devices. 4. Correct report id issue in VirtualHidAmbientColorSensor. 5. Correct issues in DMF_HingeAngle and DMF_SimpleOrientation Modules. 6. Add new Method to DMF_AcpiTarget. --- Dmf/DmfVersion.h | 2 +- Dmf/Modules.Library/DmfModules.Library.h | 1 + Dmf/Modules.Library/Dmf_AcpiTarget.c | 211 +- Dmf/Modules.Library/Dmf_AcpiTarget.h | 19 + Dmf/Modules.Library/Dmf_HingeAngle.cpp | 6 + Dmf/Modules.Library/Dmf_MobileBroadband.cpp | 54 +- Dmf/Modules.Library/Dmf_SimpleOrientation.cpp | 4 + Dmf/Modules.Library/Dmf_ThreadedBufferQueue.c | 2 +- Dmf/Modules.Library/Dmf_ThreadedBufferQueue.h | 2 +- .../Dmf_ThreadedBufferQueue.md | 2 +- Dmf/Modules.Library/Dmf_UdeClient.c | 1825 +++++++++++++++++ Dmf/Modules.Library/Dmf_UdeClient.h | 301 +++ Dmf/Modules.Library/Dmf_UdeClient.md | 510 +++++ .../Dmf_VirtualHidAmbientColorSensor.h | 1 - Dmf/Modules.Library/Dmf_VirtualHidMini.c | 30 +- 15 files changed, 2933 insertions(+), 37 deletions(-) create mode 100644 Dmf/Modules.Library/Dmf_UdeClient.c create mode 100644 Dmf/Modules.Library/Dmf_UdeClient.h create mode 100644 Dmf/Modules.Library/Dmf_UdeClient.md diff --git a/Dmf/DmfVersion.h b/Dmf/DmfVersion.h index f8b03669..5b051628 100644 --- a/Dmf/DmfVersion.h +++ b/Dmf/DmfVersion.h @@ -4,7 +4,7 @@ // built using DMF. // -// DMF Release: v1.1.82 +// DMF Release: v1.1.83 // // eof: DmfVersion.h diff --git a/Dmf/Modules.Library/DmfModules.Library.h b/Dmf/Modules.Library/DmfModules.Library.h index 782f15ee..52c8c78d 100644 --- a/Dmf/Modules.Library/DmfModules.Library.h +++ b/Dmf/Modules.Library/DmfModules.Library.h @@ -88,6 +88,7 @@ extern "C" #include "Dmf_Doorbell.h" #include "Dmf_VirtualEyeGaze.h" #include "Dmf_EyeGazeIoctl.h" +#include "Dmf_UdeClient.h" #if defined(DMF_WDF_DRIVER) #include "Dmf_Wmi.h" diff --git a/Dmf/Modules.Library/Dmf_AcpiTarget.c b/Dmf/Modules.Library/Dmf_AcpiTarget.c index 80dfa1ac..c54478bc 100644 --- a/Dmf/Modules.Library/Dmf_AcpiTarget.c +++ b/Dmf/Modules.Library/Dmf_AcpiTarget.c @@ -401,15 +401,18 @@ Return Value: *ReturnBuffer = outputBuffer; outputBuffer = NULL; } - if (ARGUMENT_PRESENT(ReturnBufferSize) != FALSE) + if (ARGUMENT_PRESENT(ReturnBufferSize)) { *ReturnBufferSize = (ULONG)sizeReturned; } } else { - *ReturnBuffer = NULL; - if (ARGUMENT_PRESENT(ReturnBufferSize) != FALSE) + if (ARGUMENT_PRESENT(ReturnBuffer)) + { + *ReturnBuffer = NULL; + } + if (ARGUMENT_PRESENT(ReturnBufferSize)) { *ReturnBufferSize = 0; } @@ -432,6 +435,95 @@ Return Value: return ntStatus; } +_Must_inspect_result_ +__drv_requiresIRQL(PASSIVE_LEVEL) +NTSTATUS +AcpiTarget_EvaluateMethodReturningUlong( + _In_ DMFMODULE DmfModule, + _In_ ULONG MethodNameAsUlong, + _In_ WDF_MEMORY_DESCRIPTOR *InputMemoryDescriptor, + _Out_ ULONG* ReturnValue + ) +/* + +Routine Description: + + This function sends an IRP to ACPI to evaluate a method. The method is + expected to return only a single UONG value. + ACPI must be in the device stack (either as a bus or filter driver). + +Arguments: + + DmfModule - This Module's Module handle. + MethodNameAsUlong - Supplies a packed string identifying the method. + InputMemoryDescriptor - Memory discriptor for input buffer. + ReturnValue - Returns the ULONG value which is returned from the method. + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS ntStatus; + ACPI_EVAL_OUTPUT_BUFFER_V1 outputBuffer; + WDF_MEMORY_DESCRIPTOR outputMemoryDescriptor; + WDFDEVICE device; + WDFIOTARGET ioTarget; + + PAGED_CODE(); + + FuncEntry(DMF_TRACE); + + RtlZeroMemory(&outputBuffer, + sizeof(outputBuffer)); + + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&outputMemoryDescriptor, + &outputBuffer, + (ULONG)sizeof(outputBuffer)); + + device = DMF_ParentDeviceGet(DmfModule); + ioTarget = WdfDeviceGetIoTarget(device); + ntStatus = WdfIoTargetSendIoctlSynchronously(ioTarget, + NULL, + IOCTL_ACPI_EVAL_METHOD_V1, + InputMemoryDescriptor, + &outputMemoryDescriptor, + NULL, + NULL); + if (!NT_SUCCESS(ntStatus)) + { + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "IOCTL_ACPI_EVAL_METHOD_V1 for method 0x%x fails: ntStatus=%!STATUS!", + MethodNameAsUlong, + ntStatus); + } + else if (outputBuffer.Signature != ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE_V1) + { + ntStatus = STATUS_ACPI_INVALID_DATA; + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "ACPI_EVAL_OUTPUT_BUFFER signature is incorrect"); + } + else if (outputBuffer.Count < 1) + { + ntStatus = STATUS_ACPI_INVALID_DATA; + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "Method 0x%x didn't return anything", MethodNameAsUlong); + } + else if (outputBuffer.Argument[0].Type != ACPI_METHOD_ARGUMENT_INTEGER) + { + ntStatus = STATUS_ACPI_INVALID_DATA; + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "Method 0x%x returned an unexpected argument of type %d", + MethodNameAsUlong, + outputBuffer.Argument[0].Type); + } + else + { + *ReturnValue = outputBuffer.Argument[0].Argument; + } + + FuncExit(DMF_TRACE, "ntStatus=%!STATUS!", ntStatus); + + return ntStatus; +} + __drv_requiresIRQL(PASSIVE_LEVEL) NTSTATUS AcpiTarget_IsDsmFunctionSupported( @@ -869,6 +961,119 @@ Return Value: return ntStatus; } +_Must_inspect_result_ +__drv_requiresIRQL(PASSIVE_LEVEL) +NTSTATUS +DMF_AcpiTarget_EvaluateMethodReturningUlong( + _In_ DMFMODULE DmfModule, + _In_ ULONG MethodNameAsUlong, + _Out_ ULONG* ReturnValue + ) +/* + +Routine Description: + + This function sends an IRP to ACPI to evaluate a method. The method is + expected to take no input and return only a single UONG value. + ACPI must be in the device stack (either as a bus or filter driver). + +Arguments: + + DmfModule - This Module's Module handle. + MethodNameAsUlong - Supplies a packed string identifying the method. + ReturnValue - Returns the ULONG value which is returned from the method. + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS ntStatus; + ACPI_EVAL_INPUT_BUFFER_V1 inputBuffer; + WDF_MEMORY_DESCRIPTOR inputMemoryDescriptor; + + PAGED_CODE(); + + FuncEntry(DMF_TRACE); + + RtlZeroMemory(&inputBuffer, + sizeof(inputBuffer)); + inputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE_V1; + inputBuffer.MethodNameAsUlong = MethodNameAsUlong; + + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&inputMemoryDescriptor, + &inputBuffer, + (ULONG)sizeof(inputBuffer)); + + ntStatus = AcpiTarget_EvaluateMethodReturningUlong(DmfModule, + MethodNameAsUlong, + &inputMemoryDescriptor, + ReturnValue); + + FuncExit(DMF_TRACE, "ntStatus=%!STATUS!", ntStatus); + + return ntStatus; +} + +_Must_inspect_result_ +__drv_requiresIRQL(PASSIVE_LEVEL) +NTSTATUS +DMF_AcpiTarget_EvaluateMethodWithUlongReturningUlong( + _In_ DMFMODULE DmfModule, + _In_ ULONG MethodNameAsUlong, + _In_ ULONG MethodArgument, + _Out_ ULONG* ReturnValue + ) +/* + +Routine Description: + + This function sends an IRP to ACPI to evaluate a method. The method is + expected to take one ULONG input and return only a single UONG value. + ACPI must be in the device stack (either as a bus or filter driver). + +Arguments: + + DmfModule - This Module's Module handle. + MethodNameAsUlong - Supplies a packed string identifying the method. + MethodArgument - The single ULONG input value. + ReturnValue - Returns the ULONG value which is returned from the method. + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS ntStatus; + ACPI_EVAL_INPUT_BUFFER_SIMPLE_INTEGER_V1 inputBuffer; + WDF_MEMORY_DESCRIPTOR inputMemoryDescriptor; + + PAGED_CODE(); + + FuncEntry(DMF_TRACE); + + RtlZeroMemory(&inputBuffer, + sizeof(inputBuffer)); + inputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIMPLE_INTEGER_SIGNATURE_V1; + inputBuffer.MethodNameAsUlong = MethodNameAsUlong; + inputBuffer.IntegerArgument = MethodArgument; + + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&inputMemoryDescriptor, + &inputBuffer, + (ULONG)sizeof(inputBuffer)); + + ntStatus = AcpiTarget_EvaluateMethodReturningUlong(DmfModule, + MethodNameAsUlong, + &inputMemoryDescriptor, + ReturnValue); + + FuncExit(DMF_TRACE, "ntStatus=%!STATUS!", ntStatus); + + return ntStatus; +} + _IRQL_requires_max_(PASSIVE_LEVEL) NTSTATUS DMF_AcpiTarget_InvokeDsm( diff --git a/Dmf/Modules.Library/Dmf_AcpiTarget.h b/Dmf/Modules.Library/Dmf_AcpiTarget.h index d298f0e2..f2a23383 100644 --- a/Dmf/Modules.Library/Dmf_AcpiTarget.h +++ b/Dmf/Modules.Library/Dmf_AcpiTarget.h @@ -54,6 +54,25 @@ DMF_AcpiTarget_EvaluateMethod( _In_ ULONG Tag ); +_Must_inspect_result_ +__drv_requiresIRQL(PASSIVE_LEVEL) +NTSTATUS +DMF_AcpiTarget_EvaluateMethodReturningUlong( + _In_ DMFMODULE DmfModule, + _In_ ULONG MethodNameAsUlong, + _Out_ ULONG* ReturnValue + ); + +_Must_inspect_result_ +__drv_requiresIRQL(PASSIVE_LEVEL) +NTSTATUS +DMF_AcpiTarget_EvaluateMethodWithUlongReturningUlong( + _In_ DMFMODULE DmfModule, + _In_ ULONG MethodNameAsUlong, + _In_ ULONG MethodArgument, + _Out_ ULONG* ReturnValue + ); + _IRQL_requires_max_(PASSIVE_LEVEL) NTSTATUS DMF_AcpiTarget_InvokeDsm( diff --git a/Dmf/Modules.Library/Dmf_HingeAngle.cpp b/Dmf/Modules.Library/Dmf_HingeAngle.cpp index 0eed7423..2384b786 100644 --- a/Dmf/Modules.Library/Dmf_HingeAngle.cpp +++ b/Dmf/Modules.Library/Dmf_HingeAngle.cpp @@ -915,6 +915,12 @@ Return Value: goto Exit; } + moduleContext->hingeAngleDevice->thisModuleHandle = DmfModule; + if (moduleConfig->DeviceId == NULL) + { + moduleConfig->DeviceId = L""; + } + moduleContext->hingeAngleDevice->thisModuleHandle = DmfModule; moduleContext->hingeAngleDevice->DeviceIdToFind = to_hstring(moduleConfig->DeviceId); moduleContext->hingeAngleDevice->EvtHingeAngleReadingChangeCallback = moduleConfig->EvtHingeAngleReadingChangeCallback; diff --git a/Dmf/Modules.Library/Dmf_MobileBroadband.cpp b/Dmf/Modules.Library/Dmf_MobileBroadband.cpp index 22d75673..a3258687 100644 --- a/Dmf/Modules.Library/Dmf_MobileBroadband.cpp +++ b/Dmf/Modules.Library/Dmf_MobileBroadband.cpp @@ -259,7 +259,15 @@ Return Value: // Get MobileBroadbandModem specific selector for device watcher. // - auto mbbSelector = MobileBroadbandModem::GetDeviceSelector(); + // Create device watcher according to the sensor GUID from ModuleConfig. + // + // Don't call CustomSensor::GetDeviceSelector(moduleConfig->DeviceGUID) as a deviceWatcher parameter. There is a bug in OS at and before 20h1. + // This CustomSensor API call will cause appverif failure and potential pre-OOBE driver crash fail. + // Manually construct an AQS instead and pass to deviceWatcher will resolve this issue. + // + hstring mbbSelector = L"System.Devices.InterfaceClassGuid:=\"{CAC88484-7515-4C03-82E6-71A87ABAC361}\"" + L" AND System.Devices.Wwan.InterfaceGuid:-[] AND System.Devices.InterfaceEnabled:=System.StructuredQueryType.Boolean#True"; + modemWatcher = DeviceInformation::CreateWatcher(mbbSelector, nullptr); // Using lambda function is necessary here, because it need access variables that outside function scope, @@ -311,7 +319,7 @@ Return Value: modem = nullptr; break; } - // Open this module. + // Open this Module. // ntStatus = DMF_ModuleOpen(DmfModule); if (!NT_SUCCESS(ntStatus)) @@ -383,7 +391,7 @@ Return Value: // sarManager.TransmissionStateChanged(tokenTransmissionStateChanged) should be called here, // but it will never return. - // Close this module. + // Close this Module. // DMF_ModuleClose(DmfModule); modem = nullptr; @@ -590,7 +598,7 @@ Routine Description: Arguments: - DmfModule - This module's Dmf Module. + DmfModule - This Module's Dmf Module. Return Value: @@ -627,7 +635,7 @@ Return Value: TraceEvents(TRACE_LEVEL_INFORMATION, DMF_TRACE, "mobileBroadbandWirelessState event triggered"); mobileBroadbandWirelessState.IsTransmitting = (BOOLEAN)args.IsTransmitting(); mobileBroadbandWirelessState.IsNetworkConnected = MobileBroadband_IsNetworkConnected(); - // Call back parent module. + // Call back parent Module. // if (moduleContext->EvtMobileBroadbandWirelessStateChangeCallback != nullptr) { @@ -739,7 +747,7 @@ Return Value: moduleContext = DMF_CONTEXT_GET(DmfModule); moduleConfig = DMF_CONFIG_GET(DmfModule); - // Store module config. + // Store Module config. // moduleContext->EvtMobileBroadbandWirelessStateChangeCallback = moduleConfig->EvtMobileBroadbandWirelessStateChangeCallback; @@ -915,7 +923,7 @@ Routine Description: Arguments: - DmfModule - This module's Dmf Module. + DmfModule - This Module's Dmf Module. AntennaIndex - The antenna index on device to be control. AntennaBackOffTableIndex - The power back off table index to be get. @@ -999,7 +1007,7 @@ Routine Description: Arguments: - DmfModule - This module's Dmf Module. + DmfModule - This Module's Dmf Module. AntennaIndex - Index of antenna to set. AntennaBackOffTableIndex - Index into the power back-off table to set. AntennaCount - Number of antennas present. @@ -1132,20 +1140,20 @@ _IRQL_requires_max_(PASSIVE_LEVEL) NTSTATUS DMF_MobileBroadband_MccMncGet( _In_ DMFMODULE DmfModule, - _Out_ DWORD* MobileCountryCode, + _Out_ DWORD* MobileAreaCode, _Out_ DWORD* MobileNetworkCode ) /*++ Routine Description: - Get the Mobile Country Code and Mobile Network Code of the mobile broadband network + Get the Mobile Area Code and Mobile Network Code of the mobile broadband network to which the device is attached from the modem. Arguments: - DmfModule - This module's Dmf Module. - MobileCountryCode - The three digit number corresponding to the country where the device is connected. + DmfModule - This Module's Dmf Module. + MobileAreaCode - The three digit number corresponding to the area where the device is connected. MobileNetworkCode - The three digit number corresponding to the network where the device is connected. Return Value: @@ -1159,8 +1167,8 @@ Return Value: MobileBroadbandNetwork currentNetwork = nullptr; const wchar_t* providerId; uint32_t productIdLength; - DWORD mobileCountryCode = 0; - DWORD mobileNetworkCode = 0; + DWORD mobileAreaCode; + DWORD mobileNetworkCode; int mccLength; int mncLength; @@ -1168,6 +1176,8 @@ Return Value: FuncEntry(DMF_TRACE); + mobileAreaCode = 0; + mobileNetworkCode = 0; moduleContext = DMF_CONTEXT_GET(DmfModule); ntStatus = DMF_ModuleReference(DmfModule); @@ -1213,10 +1223,10 @@ Return Value: ntStatus = MobileCodeCalulate(providerId, 0, mccLength, - &mobileCountryCode); + &mobileAreaCode); if (!NT_SUCCESS(ntStatus)) { - TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "mobileCountryCode get fails"); + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "mobileAreaCode get fails"); goto Exit; } @@ -1229,14 +1239,14 @@ Return Value: &mobileNetworkCode); if (!NT_SUCCESS(ntStatus)) { - TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "mobileCountryCode get fails"); + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "mobileAreaCode get fails"); goto Exit; } TraceEvents(TRACE_LEVEL_INFORMATION, DMF_TRACE, "Mcc = %d, Mnc = %d", - mobileCountryCode, + mobileAreaCode, mobileNetworkCode); - *MobileCountryCode = mobileCountryCode; + *MobileAreaCode = mobileAreaCode; *MobileNetworkCode = mobileNetworkCode; ntStatus = STATUS_SUCCESS; @@ -1266,7 +1276,7 @@ Routine Description: Arguments: - DmfModule - This module's Dmf Module. + DmfModule - This Module's Dmf Module. Return Value: @@ -1367,7 +1377,7 @@ Routine Description: Arguments: - DmfModule - This module's Dmf Module. + DmfModule - This Module's Dmf Module. Return Value: @@ -1432,7 +1442,7 @@ Routine Description: Arguments: - DmfModule - This module's Dmf Module. + DmfModule - This Module's Dmf Module. MobileBroadbandWirelessState - MobileBroadband wireless state to return. Return Value: diff --git a/Dmf/Modules.Library/Dmf_SimpleOrientation.cpp b/Dmf/Modules.Library/Dmf_SimpleOrientation.cpp index 71fdbe57..55555cfe 100644 --- a/Dmf/Modules.Library/Dmf_SimpleOrientation.cpp +++ b/Dmf/Modules.Library/Dmf_SimpleOrientation.cpp @@ -914,6 +914,10 @@ Return Value: } moduleContext->simpleOrientationDevice->thisModuleHandle = DmfModule; + if (moduleConfig->DeviceId == NULL) + { + moduleConfig->DeviceId = L""; + } moduleContext->simpleOrientationDevice->DeviceIdToFind = to_hstring(moduleConfig->DeviceId); moduleContext->simpleOrientationDevice->EvtSimpleOrientationReadingChangeCallback = moduleConfig->EvtSimpleOrientationReadingChangeCallback; ntStatus = moduleContext->simpleOrientationDevice->Initialize(); diff --git a/Dmf/Modules.Library/Dmf_ThreadedBufferQueue.c b/Dmf/Modules.Library/Dmf_ThreadedBufferQueue.c index 02807c8e..c38808b2 100644 --- a/Dmf/Modules.Library/Dmf_ThreadedBufferQueue.c +++ b/Dmf/Modules.Library/Dmf_ThreadedBufferQueue.c @@ -667,7 +667,7 @@ NTSTATUS DMF_ThreadedBufferQueue_Fetch( _In_ DMFMODULE DmfModule, _Out_ VOID** ClientBuffer, - _Out_ VOID** ClientBufferContext + _Out_opt_ VOID** ClientBufferContext ) /*++ diff --git a/Dmf/Modules.Library/Dmf_ThreadedBufferQueue.h b/Dmf/Modules.Library/Dmf_ThreadedBufferQueue.h index fad4a17f..b895f221 100644 --- a/Dmf/Modules.Library/Dmf_ThreadedBufferQueue.h +++ b/Dmf/Modules.Library/Dmf_ThreadedBufferQueue.h @@ -101,7 +101,7 @@ NTSTATUS DMF_ThreadedBufferQueue_Fetch( _In_ DMFMODULE DmfModule, _Out_ VOID** ClientBuffer, - _Out_ VOID** ClientBufferContext + _Out_opt_ VOID** ClientBufferContext ); _IRQL_requires_max_(DISPATCH_LEVEL) diff --git a/Dmf/Modules.Library/Dmf_ThreadedBufferQueue.md b/Dmf/Modules.Library/Dmf_ThreadedBufferQueue.md index 83a90307..82c4022e 100644 --- a/Dmf/Modules.Library/Dmf_ThreadedBufferQueue.md +++ b/Dmf/Modules.Library/Dmf_ThreadedBufferQueue.md @@ -205,7 +205,7 @@ NTSTATUS DMF_ThreadedBufferQueue_Fetch( _In_ DMFMODULE DmfModule, _Out_ VOID** ClientBuffer, - _Out_ VOID** ClientBufferContext + _Outopt_ VOID** ClientBufferContext ); ```` diff --git a/Dmf/Modules.Library/Dmf_UdeClient.c b/Dmf/Modules.Library/Dmf_UdeClient.c new file mode 100644 index 00000000..69f91a79 --- /dev/null +++ b/Dmf/Modules.Library/Dmf_UdeClient.c @@ -0,0 +1,1825 @@ +/*++ + + Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + Dmf_UdeClient.c + +Abstract: + + UdeClient that is used to create additional DMF Modules from scratch. + +Environment: + + Kernel-mode Driver Framework + +--*/ + +// DMF and this Module's Library specific definitions. +// +#include "DmfModule.h" +#include "DmfModules.Library.h" +#include "DmfModules.Library.Trace.h" + +#if defined(DMF_INCLUDE_TMH) +#include "Dmf_UdeClient.tmh" +#endif + +/////////////////////////////////////////////////////////////////////////////////////////////////////// +// Module Private Enumerations and Structures +/////////////////////////////////////////////////////////////////////////////////////////////////////// +// + +/////////////////////////////////////////////////////////////////////////////////////////////////////// +// Module Private Context +/////////////////////////////////////////////////////////////////////////////////////////////////////// +// + +typedef struct _DMF_CONTEXT_UdeClient +{ + // Handles IOCTLs for Host Controller Interface. + // + DMFMODULE DmfModuleIoctlHandler; +} DMF_CONTEXT_UdeClient; + +// This macro declares the following function: +// DMF_CONTEXT_GET() +// +DMF_MODULE_DECLARE_CONTEXT(UdeClient) + +// This macro declares the following function: +// DMF_CONFIG_GET() +// +DMF_MODULE_DECLARE_CONFIG(UdeClient) + +/////////////////////////////////////////////////////////////////////////////////////////////////////// +// DMF Module Support Code +/////////////////////////////////////////////////////////////////////////////////////////////////////// +// + +// This context associated for UdeCxUsbController. +// This is bound at ModuleOpen. +// +typedef struct _CONTEXT_UdeClient_UsbController +{ + // This Module's handle. + // + DMFMODULE DmfModule; + // UDECX Usb Device handle (created during Modules' Open) + // + UDECXUSBDEVICE UdecxUsbDevice; +} CONTEXT_UdeClient_UsbController; +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(CONTEXT_UdeClient_UsbController, UdeClientControllerContextGet) + +// This context associated for UdeCxUsbDevice. +// +typedef struct _CONTEXT_UdeClient_UsbDevice +{ + // This Module's handle. + // + DMFMODULE DmfModule; + // Speed of Usb Device. + // + UDECX_USB_DEVICE_SPEED UsbDeviceSpeed; +} CONTEXT_UdeClient_UsbDevice; +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(CONTEXT_UdeClient_UsbDevice, UdeClientDeviceContextGet) + +// This context associated for UdeCxEndpoint. +// +typedef struct _CONTEXT_UdeClient_Endpoint +{ + // This Module's handle. + // + DMFMODULE DmfModule; + // Configuration for this endpoint. + // + UdeClient_CONFIG_Endpoint EndpointConfig; +} CONTEXT_UdeClient_Endpoint; +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(CONTEXT_UdeClient_Endpoint, UdeClientEndpointContextGet) + +// This context associated for EndpointQueue. +// +typedef struct _CONTEXT_UdeClient_EndpointQyeue +{ + // This Module's handle. + // + DMFMODULE DmfModule; + // UDECX Endpoint handle associated with this queue. + // + UDECXUSBENDPOINT Endpoint; +} CONTEXT_UdeClient_EndpointQueue; +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(CONTEXT_UdeClient_EndpointQueue, UdeClientEndpointQueueContextGet) + +#pragma code_seg("PAGE") +_IRQL_requires_max_(PASSIVE_LEVEL) +_Must_inspect_result_ +static +NTSTATUS +UdeClient_EvtIoControl( + _In_ DMFMODULE DmfModule, + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ ULONG IoControlCode, + _In_reads_(InputBufferSize) VOID* InputBuffer, + _In_ size_t InputBufferSize, + _Out_writes_(OutputBufferSize) VOID* OutputBuffer, + _In_ size_t OutputBufferSize, + _Out_ size_t* BytesReturned + ) +/*++ + +Routine Description: + + Handle Request coming on GUID_DEVINTERFACE_USB_HOST_CONTROLLER. + +Arguments: + + DmfModule - The Child Module (Ioctl Handler) from which this callback is called. + + Queue - The WDFQUEUE associated with Request. + + Request - Request data. + + IoctlCode - IOCTL that has been validated to be supported by this Module. + + InputBuffer - Input data buffer. + + InputBufferSize - Input data buffer size. + + OutputBuffer - Output data buffer. + + OutputBufferSize - Output data buffer size. + + BytesReturned - Amount of data to be sent back. + +Returns: + + STATUS_PENDING - This Module owns the given Request. It will not be completed by the Child Module. This + Module must complete the request eventually. + Any other NTSTATUS - The given request will be completed with this NTSTATUS. + +--*/ +{ + NTSTATUS ntStatus; + BOOLEAN handled; + WDFDEVICE device; + + UNREFERENCED_PARAMETER(DmfModule); + UNREFERENCED_PARAMETER(InputBufferSize); + UNREFERENCED_PARAMETER(OutputBufferSize); + + UNREFERENCED_PARAMETER(IoControlCode); + UNREFERENCED_PARAMETER(InputBuffer); + UNREFERENCED_PARAMETER(OutputBuffer); + + TraceEvents(TRACE_LEVEL_INFORMATION, DMF_TRACE, "UdeClient_EvtIoControl Request 0x%p Queue 0x%p", Request, Queue); + + *BytesReturned = 0; + device = WdfIoQueueGetDevice(Queue); + handled = UdecxWdfDeviceTryHandleUserIoctl(device, + Request); + if (!handled) + { + ntStatus = STATUS_INVALID_DEVICE_REQUEST; + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "UdecxWdfDeviceTryHandleUserIoctl fails: ntStatus=%!STATUS!", ntStatus); + goto Exit; + } + + // When UdecxWdfDeviceTryHandleUserIoctl returns TRUE, the request is already completed. + // Return STATUS_PENDING so that the IOCTL handler would not do anything further with this request. + // + ntStatus = STATUS_PENDING; + +Exit: + + return ntStatus; +} +#pragma code_seg() + +#pragma code_seg("PAGE") +NTSTATUS +UdeClient_EvtDeviceQueryUsbCapability( + _In_ WDFDEVICE UdecxWdfDevice, + _In_ GUID* CapabilityType, + _In_ ULONG OutputBufferLength, + _Out_writes_to_opt_(OutputBufferLength, *ResultLength) PVOID OutputBuffer, + _Out_ ULONG* ResultLength + ) +/*++ + +Routine Description: + + Callback for querying the controller capability. + +Arguments: + + UdecxWdfDevice - Controller device. + + CapabilityType - GUID of supported USB capability. + + OutputBufferLength - Length of buffer in bytes. + + OutputBuffer - Buffer pointer. + + ResultLength - Result output buffer length. + +Return Value: + + NTSTATUS + +--*/ +{ + DMFMODULE dmfModule; + DMF_CONFIG_UdeClient* moduleConfig; + CONTEXT_UdeClient_UsbController* controllerContext; + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_INFORMATION, DMF_TRACE, "UdeClient_EvtDeviceQueryUsbCapability Controller 0x%p GUID 0x%p", UdecxWdfDevice, CapabilityType); + + controllerContext = UdeClientControllerContextGet(UdecxWdfDevice); + dmfModule = controllerContext->DmfModule; + moduleConfig = DMF_CONFIG_GET(dmfModule); + + // EvtControllerQueryUsbCapability is mandatory. + // + return moduleConfig->UsbControllerConfig.EvtControllerQueryUsbCapability(dmfModule, + UdecxWdfDevice, + CapabilityType, + OutputBufferLength, + OutputBuffer, + ResultLength); + +} +#pragma code_seg() + +#pragma code_seg("PAGE") +VOID +UdeClient_EvtControllerReset( + _In_ WDFDEVICE UdecxWdfDevice + ) +/*++ + +Routine Description: + + Callback for reseting the controller. + +Arguments: + + UdecxWdfDevice - Controller device. + +Return Value: + + NTSTATUS + +--*/ +{ + DMFMODULE dmfModule; + DMF_CONFIG_UdeClient* moduleConfig; + CONTEXT_UdeClient_UsbController* controllerContext; + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_INFORMATION, DMF_TRACE, "UdeClient_EvtControllerReset Controller 0x%p", UdecxWdfDevice); + + controllerContext = UdeClientControllerContextGet(UdecxWdfDevice); + dmfModule = controllerContext->DmfModule; + moduleConfig = DMF_CONFIG_GET(dmfModule); + + // EvtControllerReset is mandatory. + // + moduleConfig->UsbControllerConfig.EvtControllerReset(dmfModule, + UdecxWdfDevice); + +} +#pragma code_seg() + +#pragma code_seg("PAGE") +VOID +UdeClient_EvtEndpointReset( + _In_ UDECXUSBENDPOINT Endpoint, + _In_ WDFREQUEST Request + ) +/*++ + +Routine Description: + + Callback for reseting an endpoint on a Usb Device. + +Arguments: + + Endpoint - Endpoint to be resetted. + + Request - Request that represent the request to reset the endpoint. + +Return Value: + + None. + +--*/ +{ + DMFMODULE dmfModule; + UdeClient_CONFIG_Endpoint* endpointConfig; + CONTEXT_UdeClient_Endpoint* endpointContext; + + PAGED_CODE(); + + endpointContext = UdeClientEndpointContextGet(Endpoint); + dmfModule = endpointContext->DmfModule; + endpointConfig = &endpointContext->EndpointConfig; + + TraceEvents(TRACE_LEVEL_INFORMATION, DMF_TRACE, "UdeClient_EvtEndpointReset Endpoint 0x%p Request 0x%p", Endpoint, Request); + + // EvtEndpointReset is mandatory. + // + endpointConfig->EvtEndpointReset(dmfModule, + Endpoint, + Request); +} +#pragma code_seg() + +#pragma code_seg("PAGE") +VOID +UdeClient_EvtEndpointDeviceIoControl( + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ size_t OutputBufferLength, + _In_ size_t InputBufferLength, + _In_ ULONG IoControlCode + ) +/*++ + +Routine Description: + + Callback for DeviceIoControl on an endpoint queue. + +Arguments: + + Queue - Handle to the framework queue object that is associated with the I/O request. + + Request - Handle to a framework request object. + + OutputBufferLength - Length of the request's output buffer, if an output buffer is available. + + InputBufferLength - Length of the request's input buffer, if an input buffer is available. + + IoControlCode - The driver-defined or system-defined I/O control code + (IOCTL) that is associated with the request. + +Return Value: + + None + +--*/ +{ + DMFMODULE dmfModule; + UdeClient_CONFIG_Endpoint* endpointConfig; + CONTEXT_UdeClient_EndpointQueue* queueContext; + CONTEXT_UdeClient_Endpoint* endpointContext; + UDECXUSBENDPOINT endpoint; + + PAGED_CODE(); + + queueContext = UdeClientEndpointQueueContextGet(Queue); + dmfModule = queueContext->DmfModule; + endpoint = queueContext->Endpoint; + endpointContext = UdeClientEndpointContextGet(endpoint); + endpointConfig = &endpointContext->EndpointConfig; + + TraceEvents(TRACE_LEVEL_INFORMATION, DMF_TRACE, "UdeClient_EvtEndpointDeviceIoControl Queue 0x%p Endpoint 0x%p", Queue, endpoint); + + endpointConfig->EvtEndpointDeviceIoControl(dmfModule, + endpoint, + Queue, + Request, + OutputBufferLength, + InputBufferLength, + IoControlCode); +} +#pragma code_seg() + +#pragma code_seg("PAGE") +VOID +UdeClient_EvtEndpointStart( + _In_ UDECXUSBENDPOINT Endpoint + ) +/*++ + +Routine Description: + + Callback for starting an endpoint on a Usb Device. + +Arguments: + + Endpoint - Endpoint to be started. + +Return Value: + + None. + +--*/ +{ + DMFMODULE dmfModule; + UdeClient_CONFIG_Endpoint* endpointConfig; + CONTEXT_UdeClient_Endpoint* endpointContext; + + PAGED_CODE(); + + endpointContext = UdeClientEndpointContextGet(Endpoint); + dmfModule = endpointContext->DmfModule; + endpointConfig = &endpointContext->EndpointConfig; + + TraceEvents(TRACE_LEVEL_INFORMATION, DMF_TRACE, "UdeClient_EvtEndpointStart Endpoint 0x%p", Endpoint); + + // NOTE: EvtEndpointStart is optional. + // Since a call is being made to this function, it is assured that client has set EvtEndpointStart. + // + endpointConfig->EvtEndpointStart(dmfModule, + Endpoint); +} +#pragma code_seg() + +#pragma code_seg("PAGE") +VOID +UdeClient_EvtEndpointPurge( + _In_ UDECXUSBENDPOINT Endpoint + ) +/*++ + +Routine Description: + + Callback for purging an endpoint on a Usb Device. + +Arguments: + + Endpoint - Endpoint to be started. + +Return Value: + + None. + +--*/ +{ + DMFMODULE dmfModule; + UdeClient_CONFIG_Endpoint* endpointConfig; + CONTEXT_UdeClient_Endpoint* endpointContext; + + PAGED_CODE(); + + endpointContext = UdeClientEndpointContextGet(Endpoint); + dmfModule = endpointContext->DmfModule; + endpointConfig = &endpointContext->EndpointConfig; + + TraceEvents(TRACE_LEVEL_INFORMATION, DMF_TRACE, "UdeClient_EvtEndpointPurge Endpoint 0x%p", Endpoint); + + // NOTE: EvtEndpointPurge is optional. + // Since a call is being made to this function, it is assured that client has set EvtEndpointPurge. + // + endpointConfig->EvtEndpointPurge(dmfModule, + Endpoint); +} +#pragma code_seg() + +#pragma code_seg("PAGE") +_IRQL_requires_max_(PASSIVE_LEVEL) +_Must_inspect_result_ +static +NTSTATUS +UdeClient_EndpointCreate( + _In_ DMFMODULE DmfModule, + _In_ PUDECXUSBENDPOINT_INIT* EndpointInit, + _In_ UdeClient_CONFIG_Endpoint* EndpointConfig, + _Out_ UDECXUSBENDPOINT* Endpoint + ) +/*++ + +Routine Description: + + Create an endpoint for a Usb device. + +Arguments: + + DmfModule - This Module's handle. + EndpointInit - Endpoint Init associated with the Usb device. + EndpointConfig - Endpoint Configuration. + Endpoint - The newly created Endpoint. + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS ntStatus; + WDFDEVICE device; + DMF_CONFIG_UdeClient* moduleConfig; + WDF_IO_QUEUE_CONFIG queueConfig; + WDFQUEUE endpointQueue; + UDECXUSBENDPOINT endpoint; + UDECX_USB_ENDPOINT_CALLBACKS callbacks; + WDF_OBJECT_ATTRIBUTES queueAttributes; + CONTEXT_UdeClient_Endpoint* endpointContext; + CONTEXT_UdeClient_EndpointQueue* queueContext; + + PAGED_CODE(); + + FuncEntry(DMF_TRACE); + + device = DMF_ParentDeviceGet(DmfModule); + moduleConfig = DMF_CONFIG_GET(DmfModule); + endpointQueue = NULL; + + WDF_IO_QUEUE_CONFIG_INIT(&queueConfig, + EndpointConfig->QueueDispatchType); + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&queueAttributes, + CONTEXT_UdeClient_EndpointQueue); + queueConfig.EvtIoInternalDeviceControl = UdeClient_EvtEndpointDeviceIoControl; + ntStatus = WdfIoQueueCreate(device, + &queueConfig, + &queueAttributes, + &endpointQueue); + if (! NT_SUCCESS(ntStatus)) + { + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "WdfIoQueueCreate fails: ntStatus=%!STATUS!", ntStatus); + goto Exit; + } + + UdecxUsbEndpointInitSetEndpointAddress(*EndpointInit, + EndpointConfig->EndpointAddress); + + // EvtEndpointReset is Mandatory. + // + DmfAssert(EndpointConfig->EvtEndpointReset != NULL); + UDECX_USB_ENDPOINT_CALLBACKS_INIT(&callbacks, + UdeClient_EvtEndpointReset); + if (EndpointConfig->EvtEndpointStart) + { + callbacks.EvtUsbEndpointStart = UdeClient_EvtEndpointStart; + } + + if (EndpointConfig->EvtEndpointPurge) + { + callbacks.EvtUsbEndpointPurge = UdeClient_EvtEndpointPurge; + } + + UdecxUsbEndpointInitSetCallbacks(*EndpointInit, + &callbacks); + + WDF_OBJECT_ATTRIBUTES attributes; + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, + CONTEXT_UdeClient_Endpoint); + ntStatus = UdecxUsbEndpointCreate(EndpointInit, + &attributes, + &endpoint); + if (! NT_SUCCESS(ntStatus)) + { + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "UdecxUsbEndpointCreate fails: ntStatus=%!STATUS!", ntStatus); + goto Exit; + } + + UdecxUsbEndpointSetWdfIoQueue(endpoint, + endpointQueue); + + // Update the endpoint and its queue contexts. + // + endpointContext = UdeClientEndpointContextGet(endpoint); + endpointContext->EndpointConfig = *EndpointConfig; + endpointContext->DmfModule = DmfModule; + + queueContext = UdeClientEndpointQueueContextGet(endpointQueue); + queueContext->DmfModule = DmfModule; + queueContext->Endpoint = endpoint; + + endpointQueue = NULL; + + // Return the created endpoint. + // + *Endpoint = endpoint; + +Exit: + + if (endpointQueue != NULL) + { + WdfObjectDelete(endpointQueue); + } + + FuncExit(DMF_TRACE, "ntStatus=%!STATUS!", ntStatus); + + return ntStatus; +} +#pragma code_seg() + +#pragma code_seg("PAGE") +_IRQL_requires_max_(PASSIVE_LEVEL) +_Must_inspect_result_ +static +NTSTATUS +UdeClient_SimpleEndpointCreate( + _In_ DMFMODULE DmfModule, + _In_ UDECXUSBDEVICE UsbDevice, + _In_ UdeClient_CONFIG_Endpoint* EndpointConfig, + _Out_ UDECXUSBENDPOINT* Endpoint + ) +/*++ + +Routine Description: + + Create a simple endpoint for a Usb device. + +Arguments: + + DmfModule - This Module's handle. + UsbDevice - The Usb device for which the endpoint is being created. + EndpointConfig - Endpoint Configuration. + Endpoint - The newly created Endpoint. + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS ntStatus; + + PAGED_CODE(); + + FuncEntry(DMF_TRACE); + + PUDECXUSBENDPOINT_INIT endpointInit; + endpointInit = UdecxUsbSimpleEndpointInitAllocate(UsbDevice); + if (endpointInit == NULL) + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + goto Exit; + } + + ntStatus = UdeClient_EndpointCreate(DmfModule, + &endpointInit, + EndpointConfig, + Endpoint); + if (! NT_SUCCESS(ntStatus)) + { + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "UdeClient_EndpointCreate fails: ntStatus=%!STATUS!", ntStatus); + goto Exit; + } + +Exit: + + if (endpointInit != NULL) + { + UdecxUsbEndpointInitFree(endpointInit); + } + + FuncExit(DMF_TRACE, "ntStatus=%!STATUS!", ntStatus); + + return ntStatus; +} +#pragma code_seg() + +#pragma code_seg("PAGE") +_IRQL_requires_max_(PASSIVE_LEVEL) +_Must_inspect_result_ +static +NTSTATUS +UdeClient_UsbDeviceConfigValidate( + _In_ DMFMODULE DmfModule, + _In_ UdeClient_CONFIG_UsbDevice* UsbDeviceConfig + ) +/*++ + +Routine Description: + + Validate the given Usb Device Config. + +Arguments: + + DmfModule - This Module's handle. + UsbDeviceConfig - The configuration for this Usb Device. + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS ntStatus; + DMF_CONFIG_UdeClient* moduleConfig; + + PAGED_CODE(); + + FuncEntry(DMF_TRACE); + + moduleConfig = DMF_CONFIG_GET(DmfModule); + + if (UsbDeviceConfig->UsbDeviceEndpointType == UdecxEndpointTypeDynamic) + { + // For Dynamic Endpoint client need to provide the precreate callback to setup EndpointAdd callbacks. + // + if (moduleConfig->EvtUsbDevicePreCreate == NULL) + { + ntStatus = STATUS_INVALID_PARAMETER; + goto Exit; + } + } + else if (UsbDeviceConfig->UsbDeviceEndpointType == UdecxEndpointTypeSimple) + { + // Atleast 1 endpoint configuration is needed for Simple EndpointType. + // + if (UsbDeviceConfig->SimpleEndpointCount == 0) + { + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "Simple Endpoint Type Required atleast 1 endpoint"); + + ntStatus = STATUS_INVALID_PARAMETER; + goto Exit; + } + else + { + if (UsbDeviceConfig->SimpleEndpointConfigs == NULL) + { + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "Missing Endpoint configuration(s)"); + + ntStatus = STATUS_INVALID_PARAMETER; + goto Exit; + } + + // Make sure EndpointReset Callback is present in each endpoint config. + // + for (ULONG endpointIndex = 0; endpointIndex < UsbDeviceConfig->SimpleEndpointCount; ++endpointIndex) + { + UdeClient_CONFIG_Endpoint* endpointConfig = &UsbDeviceConfig->SimpleEndpointConfigs[endpointIndex]; + + // EndpointReset Callback is mandatory. + // + if (endpointConfig->EvtEndpointReset == NULL) + { + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "No Endpoint Reset Callback configured on Endpoint Configuration[%d]", endpointIndex); + + ntStatus = STATUS_INVALID_PARAMETER; + goto Exit; + } + + // DeviceIoControl Callback is mandatory. + // + if (endpointConfig->EvtEndpointDeviceIoControl == NULL) + { + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "No Endpoint DeviceIoControl Callback configured on Endpoint Configuration[%d]", endpointIndex); + + ntStatus = STATUS_INVALID_PARAMETER; + goto Exit; + } + } + } + } + else + { + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "Invalid Endpoint Type %d", UsbDeviceConfig->UsbDeviceEndpointType); + + ntStatus = STATUS_INVALID_PARAMETER; + goto Exit; + } + + ntStatus = STATUS_SUCCESS; + +Exit: + + FuncExit(DMF_TRACE, "ntStatus=%!STATUS!", ntStatus); + + return ntStatus; +} + +#pragma code_seg("PAGE") +_IRQL_requires_max_(PASSIVE_LEVEL) +_Must_inspect_result_ +static +NTSTATUS +UdeClient_CreateUsbDevice( + _In_ DMFMODULE DmfModule, + _In_ UdeClient_CONFIG_UsbDevice* UsbDeviceConfig, + _Out_ UDECXUSBDEVICE* UdecxUsbDevice + ) +/*++ + +Routine Description: + + Create a Usb Device. + +Arguments: + + DmfModule - This Module's handle. + UsbDeviceConfig - The configuration for this Usb Device. + UdecxUsbDevice - The created Usb Device. + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS ntStatus; + WDFDEVICE device; + DMF_CONFIG_UdeClient* moduleConfig; + DMF_CONTEXT_UdeClient* moduleContext; + PUDECXUSBDEVICE_INIT usbDeviceInit; + UDECXUSBDEVICE usbDevice; + CONTEXT_UdeClient_UsbController* controllerContext; + + PAGED_CODE(); + + FuncEntry(DMF_TRACE); + + device = DMF_ParentDeviceGet(DmfModule); + moduleConfig = DMF_CONFIG_GET(DmfModule); + moduleContext = DMF_CONTEXT_GET(DmfModule); + controllerContext = UdeClientControllerContextGet(device); + + ntStatus = STATUS_SUCCESS; + usbDeviceInit = NULL; + usbDevice = NULL; + + ntStatus = UdeClient_UsbDeviceConfigValidate(DmfModule, + UsbDeviceConfig); + if (! NT_SUCCESS(ntStatus)) + { + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "UdeClient_UsbDeviceConfigValidate fails: ntStatus=%!STATUS!", ntStatus); + goto Exit; + } + + usbDeviceInit = UdecxUsbDeviceInitAllocate(device); + if (usbDeviceInit == NULL) + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "UdecxUsbDeviceInitAllocate fails: ntStatus=%!STATUS!", ntStatus); + goto Exit; + } + + // Set required attributes. Client can change these through the preCreate callback. + // + UdecxUsbDeviceInitSetSpeed(usbDeviceInit, + UsbDeviceConfig->UsbDeviceSpeed); + + UdecxUsbDeviceInitSetEndpointsType(usbDeviceInit, + UsbDeviceConfig->UsbDeviceEndpointType); + + // Let the client define/override options like State Changed Callbacks. + // + if (moduleConfig->EvtUsbDevicePreCreate) + { + ntStatus = moduleConfig->EvtUsbDevicePreCreate(DmfModule, + usbDeviceInit); + if (! NT_SUCCESS(ntStatus)) + { + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "EvtUsbDevicePreCreate fails: ntStatus=%!STATUS!", ntStatus); + goto Exit; + } + } + + // Add Device descriptor. + // + ntStatus = UdecxUsbDeviceInitAddDescriptor(usbDeviceInit, + UsbDeviceConfig->UsbDeviceDescriptor, + UsbDeviceConfig->UsbDeviceDescriptorSize); + if (! NT_SUCCESS(ntStatus)) + { + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "UdecxUsbDeviceInitAddDescriptor (Device) fails: ntStatus=%!STATUS!", ntStatus); + goto Exit; + } + + if (UsbDeviceConfig->UsbBosDescriptorSize != 0) + { + // Add BOS descriptor. + // + ntStatus = UdecxUsbDeviceInitAddDescriptor(usbDeviceInit, + UsbDeviceConfig->UsbBosDescriptor, + UsbDeviceConfig->UsbBosDescriptorSize); + if (! NT_SUCCESS(ntStatus)) + { + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "UdecxUsbDeviceInitAddDescriptor (BOS) fails: ntStatus=%!STATUS!", ntStatus); + goto Exit; + } + } + + // Add Config descriptor. + // + ntStatus = UdecxUsbDeviceInitAddDescriptor(usbDeviceInit, + UsbDeviceConfig->UsbConfigDescriptor, + UsbDeviceConfig->UsbConfigDescriptorSize); + if (! NT_SUCCESS(ntStatus)) + { + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "UdecxUsbDeviceInitAddDescriptor (Config) fails: ntStatus=%!STATUS!", ntStatus); + goto Exit; + } + + // Add Language descriptors. + // + ntStatus = UdecxUsbDeviceInitAddDescriptorWithIndex(usbDeviceInit, + UsbDeviceConfig->UsbLanguageDescriptor, + UsbDeviceConfig->UsbLanguageDescriptorSize, + UsbDeviceConfig->UsbLanguageDescriptorIndex); + if (! NT_SUCCESS(ntStatus)) + { + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "UdecxUsbDeviceInitAddDescriptorWithIndex (Language) fails: ntStatus=%!STATUS!", ntStatus); + goto Exit; + } + + // Add Manufacturer String descriptor. + // + UNICODE_STRING UsbManufacturerStringDescriptor; + RtlInitUnicodeString(&UsbManufacturerStringDescriptor, + UsbDeviceConfig->UsbManufacturerStringDescriptor); + + ntStatus = UdecxUsbDeviceInitAddStringDescriptor(usbDeviceInit, + &UsbManufacturerStringDescriptor, + UsbDeviceConfig->UsbManufacturerStringDescriptorIndex, + UsbDeviceConfig->LanguageIdentifier); + if (! NT_SUCCESS(ntStatus)) + { + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "UdecxUsbDeviceInitAddStringDescriptor (Manufacturer) fails: ntStatus=%!STATUS!", ntStatus); + goto Exit; + } + + // Add Product String descriptor. + // + UNICODE_STRING ProductStringDescriptor; + RtlInitUnicodeString(&ProductStringDescriptor, + UsbDeviceConfig->ProductStringDescriptor); + ntStatus = UdecxUsbDeviceInitAddStringDescriptor(usbDeviceInit, + &ProductStringDescriptor, + UsbDeviceConfig->ProductStringDescriptorIndex, + UsbDeviceConfig->LanguageIdentifier); + if (! NT_SUCCESS(ntStatus)) + { + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "UdecxUsbDeviceInitAddStringDescriptor (Product) fails: ntStatus=%!STATUS!", ntStatus); + goto Exit; + } + + WDF_OBJECT_ATTRIBUTES attributes; + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, + CONTEXT_UdeClient_UsbDevice); + ntStatus = UdecxUsbDeviceCreate(&usbDeviceInit, + &attributes, + &usbDevice); + if (! NT_SUCCESS(ntStatus)) + { + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "UdecxUsbDeviceCreate fails: ntStatus=%!STATUS!", ntStatus); + goto Exit; + } + + // Create succeeded. Update the contexts. + // + CONTEXT_UdeClient_UsbDevice* usbDeviceContext = UdeClientDeviceContextGet(usbDevice); + usbDeviceContext->DmfModule = DmfModule; + usbDeviceContext->UsbDeviceSpeed = UsbDeviceConfig->UsbDeviceSpeed; + + *UdecxUsbDevice = usbDevice; + usbDevice = NULL; + + if (moduleConfig->EvtUsbDevicePostCreate) + { + moduleConfig->EvtUsbDevicePostCreate(DmfModule, + usbDevice); + } + +Exit: + + if (usbDevice != NULL) + { + WdfObjectDelete(usbDevice); + } + + if (usbDeviceInit != NULL) + { + UdecxUsbDeviceInitFree(usbDeviceInit); + } + + FuncExit(DMF_TRACE, "ntStatus=%!STATUS!", ntStatus); + + return ntStatus; +} +#pragma code_seg() + +#pragma code_seg("PAGE") +_IRQL_requires_max_(PASSIVE_LEVEL) +_Must_inspect_result_ +static +NTSTATUS +UdeClient_CreateAndPlugUsbDevice( + _In_ DMFMODULE DmfModule, + _In_ UdeClient_CONFIG_UsbDevice* UsbDeviceConfig, + _In_ UDE_CLIENT_PLUGIN_PORT_TYPE PortType, + _In_ ULONG PlugInPortNumber, + _Out_ UDECXUSBDEVICE* UdecxUsbDevice + ) +/*++ + +Routine Description: + + Create a Usb Device and Plug to UdeCx Controller. + +Arguments: + + DmfModule - This Module's handle. + UsbDeviceConfig - Usb Configuration for this device. + PortType - USB 2.0 or USB 3.0 port. + PlugInPortNumber - Port Number to be used while PlugIn this Usb Device. + UdecxUsbDevice - The created Usb Device. + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS ntStatus; + WDFDEVICE device; + DMF_CONFIG_UdeClient* moduleConfig; + DMF_CONTEXT_UdeClient* moduleContext; + UDECXUSBDEVICE usbDevice; + + PAGED_CODE(); + + FuncEntry(DMF_TRACE); + + device = DMF_ParentDeviceGet(DmfModule); + moduleConfig = DMF_CONFIG_GET(DmfModule); + moduleContext = DMF_CONTEXT_GET(DmfModule); + usbDevice = NULL; + + if (PortType != UDE_CLIENT_PORT_USB2_0 && PortType != UDE_CLIENT_PORT_USB3_0) + { + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "PortType %d Invalid.", PortType); + ntStatus = STATUS_INVALID_PARAMETER; + goto Exit; + } + + // Create a USB Device. + // + ntStatus = UdeClient_CreateUsbDevice(DmfModule, + UsbDeviceConfig, + &usbDevice); + if (! NT_SUCCESS(ntStatus)) + { + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "UdeClient_CreateUsbDevice fails: ntStatus=%!STATUS!", ntStatus); + goto Exit; + } + + // The Only place to create the Simple EndpointType is just before the PlugIn. + // + if (UsbDeviceConfig->UsbDeviceEndpointType == UdecxEndpointTypeSimple) + { + // Loop through and create all the required endpoints before PlugIn. + // + for (ULONG endpointIndex = 0; endpointIndex < UsbDeviceConfig->SimpleEndpointCount; ++endpointIndex) + { + UdeClient_CONFIG_Endpoint* endpointConfig = &UsbDeviceConfig->SimpleEndpointConfigs[endpointIndex]; + UDECXUSBENDPOINT endpointOut; + + ntStatus = UdeClient_SimpleEndpointCreate(DmfModule, + usbDevice, + endpointConfig, + &endpointOut); + if (! NT_SUCCESS(ntStatus)) + { + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "UdeClient_SimpleEndpointCreate fails: ntStatus=%!STATUS!", ntStatus); + goto Exit; + } + } + } + + // Plug the newly created Usb Device. + // + UDECX_USB_DEVICE_PLUG_IN_OPTIONS pluginOptions; + UDECX_USB_DEVICE_PLUG_IN_OPTIONS_INIT(&pluginOptions); + if (PortType == UDE_CLIENT_PORT_USB2_0) + { + pluginOptions.Usb20PortNumber = PlugInPortNumber; + } + else + { + pluginOptions.Usb30PortNumber = PlugInPortNumber; + } + + ntStatus = UdecxUsbDevicePlugIn(usbDevice, + &pluginOptions); + if (! NT_SUCCESS(ntStatus)) + { + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "UdecxUsbDevicePlugIn fails: ntStatus=%!STATUS!", ntStatus); + goto Exit; + } + + *UdecxUsbDevice = usbDevice; + usbDevice = NULL; + +Exit: + + // Even if the Create succeeded, but the PlugIn failed free up the just created Device. + // + if (usbDevice != NULL) + { + WdfObjectDelete(usbDevice); + } + + FuncExit(DMF_TRACE, "ntStatus=%!STATUS!", ntStatus); + + return ntStatus; +} +#pragma code_seg() + +/////////////////////////////////////////////////////////////////////////////////////////////////////// +// WDF Module Callbacks +/////////////////////////////////////////////////////////////////////////////////////////////////////// +// + +// The table of IOCTLS that this Module supports. +// +IoctlHandler_IoctlRecord UdeClient_IoctlHandlerTable[] = +{ + {IOCTL_GET_HCD_DRIVERKEY_NAME, 0, 0, UdeClient_EvtIoControl }, + {IOCTL_USB_GET_ROOT_HUB_NAME, 0, 0, UdeClient_EvtIoControl } +}; + +_Function_class_(DMF_ModuleD0Entry) +_IRQL_requires_max_(PASSIVE_LEVEL) +_Must_inspect_result_ +static +NTSTATUS +DMF_UdeClient_ModuleD0Entry( + _In_ DMFMODULE DmfModule, + _In_ WDF_POWER_DEVICE_STATE PreviousState + ) +/*++ + +Routine Description: + + UdeClient callback for ModuleD0Entry for a given DMF Module. + +Arguments: + + DmfModule - This Module's handle. + PreviousState - The WDF Power State that the given DMF Module should exit from. + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS ntStatus; + + UNREFERENCED_PARAMETER(DmfModule); + UNREFERENCED_PARAMETER(PreviousState); + + FuncEntry(DMF_TRACE); + + // NOP Currently. + // + ntStatus = STATUS_SUCCESS; + + TraceEvents(TRACE_LEVEL_INFORMATION, DMF_TRACE, "DMF_UdeClient_ModuleD0Entry ntStatus=%!STATUS!", ntStatus); + + FuncExit(DMF_TRACE, "ntStatus=%!STATUS!", ntStatus); + + return ntStatus; +} + +_Function_class_(DMF_ModuleD0Exit) +_IRQL_requires_max_(PASSIVE_LEVEL) +static +NTSTATUS +DMF_UdeClient_ModuleD0Exit( + _In_ DMFMODULE DmfModule, + _In_ WDF_POWER_DEVICE_STATE TargetState + ) +/*++ + +Routine Description: + + UdeClient callback for ModuleD0Exit for a given DMF Module. + +Arguments: + + DmfModule - This Module's handle. + TargetState - The WDF Power State that the given DMF Module will enter. + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS ntStatus; + + UNREFERENCED_PARAMETER(DmfModule); + UNREFERENCED_PARAMETER(TargetState); + + FuncEntry(DMF_TRACE); + + // NOP Currently. + // + ntStatus = STATUS_SUCCESS; + + TraceEvents(TRACE_LEVEL_INFORMATION, DMF_TRACE, "DMF_UdeClient_ModuleD0Exit ntStatus=%!STATUS!", ntStatus); + + FuncExitVoid(DMF_TRACE); + + return ntStatus; +} + +_Function_class_(DMF_ModuleSurpriseRemoval) +_IRQL_requires_max_(PASSIVE_LEVEL) +VOID +DMF_UdeClient_SurpriseRemoval( + _In_ DMFMODULE DmfModule + ) +/*++ + +Routine Description: + + UdeClient callback for ModuleSurpriseRemoval for a given DMF Module. + +Arguments: + + DmfModule - The given DMF Module. + +Return Value: + + None + +--*/ +{ + FuncEntry(DMF_TRACE); + + // NOP Currently. + // + + UNREFERENCED_PARAMETER(DmfModule); + + FuncExitVoid(DMF_TRACE); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////// +// DMF Module Callbacks +/////////////////////////////////////////////////////////////////////////////////////////////////////// +// + +#pragma code_seg("PAGE") +_Function_class_(DMF_ChildModulesAdd) +_IRQL_requires_max_(PASSIVE_LEVEL) +VOID +DMF_UdeClient_ChildModulesAdd( + _In_ DMFMODULE DmfModule, + _In_ DMF_MODULE_ATTRIBUTES* DmfParentModuleAttributes, + _In_ PDMFMODULE_INIT DmfModuleInit + ) +/*++ + +Routine Description: + + Configure and add the required Child Modules to the given Parent Module. + +Arguments: + + DmfModule - The given Parent Module. + DmfParentModuleAttributes - Pointer to the parent DMF_MODULE_ATTRIBUTES structure. + DmfModuleInit - Opaque structure to be passed to DMF_DmfModuleAdd. + +Return Value: + + None + +--*/ +{ + DMF_CONFIG_UdeClient* moduleConfig; + DMF_CONTEXT_UdeClient* moduleContext; + DMF_MODULE_ATTRIBUTES moduleAttributes; + DMF_CONFIG_IoctlHandler moduleConfigIoctlHandler; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER(DmfParentModuleAttributes); + + FuncEntry(DMF_TRACE); + + moduleConfig = DMF_CONFIG_GET(DmfModule); + moduleContext = DMF_CONTEXT_GET(DmfModule); + + // IoctlHandler + // ------------ + // + DMF_CONFIG_IoctlHandler_AND_ATTRIBUTES_INIT(&moduleConfigIoctlHandler, + &moduleAttributes); + moduleConfigIoctlHandler.DeviceInterfaceGuid = GUID_DEVINTERFACE_USB_HOST_CONTROLLER; + moduleConfigIoctlHandler.IoctlRecordCount = _countof(UdeClient_IoctlHandlerTable); + moduleConfigIoctlHandler.IoctlRecords = UdeClient_IoctlHandlerTable; + moduleConfigIoctlHandler.AccessModeFilter = IoctlHandler_AccessModeDefault; + moduleConfigIoctlHandler.ReferenceString = L"UDE"; + + DMF_DmfModuleAdd(DmfModuleInit, + &moduleAttributes, + WDF_NO_OBJECT_ATTRIBUTES, + &moduleContext->DmfModuleIoctlHandler); + + FuncExitVoid(DMF_TRACE); +} +#pragma code_seg() + +#pragma code_seg("PAGE") +_Function_class_(DMF_Open) +_IRQL_requires_max_(PASSIVE_LEVEL) +_Must_inspect_result_ +static +NTSTATUS +DMF_UdeClient_Open( + _In_ DMFMODULE DmfModule + ) +/*++ + +Routine Description: + + Initialize an instance of a DMF Module of type UdeClient. + Initialize the UdeCx Host Controller Emulation and if configured to Create and PlugIn a USB device, + Create the device and attached to the designated port. + +Arguments: + + DmfModule - This Module's handle. + +Return Value: + + STATUS_SUCCESS + +--*/ +{ + NTSTATUS ntStatus; + WDFDEVICE device; + WDF_OBJECT_ATTRIBUTES attributes; + UDECX_WDF_DEVICE_CONFIG controllerConfig; + DMF_CONFIG_UdeClient* moduleConfig; + CONTEXT_UdeClient_UsbController* controllerContext; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER(DmfModule); + + FuncEntry(DMF_TRACE); + + device = DMF_ParentDeviceGet(DmfModule); + moduleConfig = DMF_CONFIG_GET(DmfModule); + + // Ensure we have the required callbacks on the controller. + // NOTE: Rest of the validation is done at a later stage. + // + if (moduleConfig->UsbControllerConfig.EvtControllerQueryUsbCapability == NULL) + { + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "EvtControllerQueryUsbCapability is Mandatory"); + ntStatus = STATUS_INVALID_PARAMETER; + goto Exit; + } + + // Allocate a Context to keep items for the UsbController. + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, + CONTEXT_UdeClient_UsbController); + ntStatus = WdfObjectAllocateContext(device, + &attributes, + (VOID**)&controllerContext); + if (! NT_SUCCESS(ntStatus)) + { + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "WdfObjectAllocateContext (UsbController) fails: ntStatus=%!STATUS!", ntStatus); + goto Exit; + } + + // Save the DmfModule in the Controller context. + // + controllerContext->DmfModule = DmfModule; + controllerContext->UdecxUsbDevice = NULL; + + UDECX_WDF_DEVICE_CONFIG_INIT(&controllerConfig, + UdeClient_EvtDeviceQueryUsbCapability); + controllerConfig.NumberOfUsb20Ports = moduleConfig->UsbControllerConfig.NumberOfUsb20Ports; + controllerConfig.NumberOfUsb30Ports = moduleConfig->UsbControllerConfig.NumberOfUsb30Ports; + controllerConfig.ResetAction = moduleConfig->UsbControllerConfig.ControllerResetAction; + if (moduleConfig->UsbControllerConfig.EvtControllerReset != NULL) + { + controllerConfig.EvtUdecxWdfDeviceReset = UdeClient_EvtControllerReset; + } + + ntStatus = UdecxWdfDeviceAddUsbDeviceEmulation(device, + &controllerConfig); + if (! NT_SUCCESS(ntStatus)) + { + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "UdecxWdfDeviceAddUsbDeviceEmulation fails: ntStatus=%!STATUS!", ntStatus); + goto Exit; + } + + if (moduleConfig->UsbCreateAndPlugOnOpen) + { + UDECXUSBDEVICE udecxUsbDevice; + ntStatus = UdeClient_CreateAndPlugUsbDevice(DmfModule, + &moduleConfig->UsbDeviceConfig, + moduleConfig->PlugInPortType, + moduleConfig->PlugInPortNumber, + &udecxUsbDevice); + if (! NT_SUCCESS(ntStatus)) + { + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "UdeClient_CreateAndPlugUsbDevice fails: ntStatus=%!STATUS!", ntStatus); + goto Exit; + } + + // Save this in the controller Context. + // + controllerContext->UdecxUsbDevice = udecxUsbDevice; + TraceEvents(TRACE_LEVEL_INFORMATION, DMF_TRACE, "On Open Usb Device 0x%p Plugged In", udecxUsbDevice); + } + +Exit: + + FuncExit(DMF_TRACE, "ntStatus=%!STATUS!", ntStatus); + + return ntStatus; +} +#pragma code_seg() + +#pragma code_seg("PAGE") +_Function_class_(DMF_Close) +_IRQL_requires_max_(PASSIVE_LEVEL) +static +VOID +DMF_UdeClient_Close( + _In_ DMFMODULE DmfModule + ) +/*++ + +Routine Description: + + Uninitialize an instance of a DMF Module of type UdeClient. + +Arguments: + + DmfModule - This Module's handle. + +Return Value: + + None + +--*/ +{ + NTSTATUS ntStatus; + WDFDEVICE device; + DMF_CONFIG_UdeClient* moduleConfig; + CONTEXT_UdeClient_UsbController* controllerContext; + + PAGED_CODE(); + + FuncEntry(DMF_TRACE); + + device = DMF_ParentDeviceGet(DmfModule); + moduleConfig = DMF_CONFIG_GET(DmfModule); + controllerContext = UdeClientControllerContextGet(device); + + if (moduleConfig->UsbCreateAndPlugOnOpen && + controllerContext->UdecxUsbDevice != NULL) + { + ntStatus = UdecxUsbDevicePlugOutAndDelete(controllerContext->UdecxUsbDevice); + if (! NT_SUCCESS(ntStatus)) + { + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "UdecxUsbDevicePlugOutAndDelete fails: ntStatus=%!STATUS!", ntStatus); + } + + controllerContext->UdecxUsbDevice = NULL; + } + + FuncExitVoid(DMF_TRACE); +} +#pragma code_seg() + +/////////////////////////////////////////////////////////////////////////////////////////////////////// +// Public Calls by Client +/////////////////////////////////////////////////////////////////////////////////////////////////////// +// + +#pragma code_seg("PAGE") +_IRQL_requires_max_(PASSIVE_LEVEL) +_Must_inspect_result_ +NTSTATUS +DMF_UdeClient_Create( + _In_ WDFDEVICE Device, + _In_ DMF_MODULE_ATTRIBUTES* DmfModuleAttributes, + _In_ WDF_OBJECT_ATTRIBUTES* ObjectAttributes, + _Out_ DMFMODULE* DmfModule + ) +/*++ + +Routine Description: + + Create an instance of a DMF Module of type UdeClient. + +Arguments: + + Device - Client driver's WDFDEVICE object. + DmfModuleAttributes - Opaque structure that contains parameters DMF needs to initialize the Module. + ObjectAttributes - WDF object attributes for DMFMODULE. + DmfModule - Address of the location where the created DMFMODULE handle is returned. + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS ntStatus; + DMF_MODULE_DESCRIPTOR dmfModuleDescriptor_UdeClient; + DMF_CALLBACKS_DMF dmfCallbacksDmf_UdeClient; + DMF_CALLBACKS_WDF dmfCallbacksWdf_UdeClient; + + PAGED_CODE(); + + FuncEntry(DMF_TRACE); + + DMF_CALLBACKS_DMF_INIT(&dmfCallbacksDmf_UdeClient); + dmfCallbacksDmf_UdeClient.ChildModulesAdd = DMF_UdeClient_ChildModulesAdd; + dmfCallbacksDmf_UdeClient.DeviceOpen = DMF_UdeClient_Open; + dmfCallbacksDmf_UdeClient.DeviceClose = DMF_UdeClient_Close; + + DMF_CALLBACKS_WDF_INIT(&dmfCallbacksWdf_UdeClient); + dmfCallbacksWdf_UdeClient.ModuleSurpriseRemoval = DMF_UdeClient_SurpriseRemoval; + dmfCallbacksWdf_UdeClient.ModuleD0Entry = DMF_UdeClient_ModuleD0Entry; + dmfCallbacksWdf_UdeClient.ModuleD0Exit = DMF_UdeClient_ModuleD0Exit; + + DMF_MODULE_DESCRIPTOR_INIT_CONTEXT_TYPE(dmfModuleDescriptor_UdeClient, + UdeClient, + DMF_CONTEXT_UdeClient, + DMF_MODULE_OPTIONS_PASSIVE, + DMF_MODULE_OPEN_OPTION_OPEN_PrepareHardware); + + dmfModuleDescriptor_UdeClient.CallbacksDmf = &dmfCallbacksDmf_UdeClient; + dmfModuleDescriptor_UdeClient.CallbacksWdf = &dmfCallbacksWdf_UdeClient; + + ntStatus = DMF_ModuleCreate(Device, + DmfModuleAttributes, + ObjectAttributes, + &dmfModuleDescriptor_UdeClient, + DmfModule); + if (! NT_SUCCESS(ntStatus)) + { + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "DMF_ModuleCreate fails: ntStatus=%!STATUS!", ntStatus); + } + + FuncExit(DMF_TRACE, "ntStatus=%!STATUS!", ntStatus); + + return(ntStatus); +} +#pragma code_seg() + +// Module Methods +// + +_IRQL_requires_max_(PASSIVE_LEVEL) +_Must_inspect_result_ +NTSTATUS +DMF_UdeClient_DeviceCreateAndPlugIn( + _In_ DMFMODULE DmfModule, + _In_ UdeClient_CONFIG_UsbDevice* UsbDeviceConfig, + _In_ UDE_CLIENT_PLUGIN_PORT_TYPE PortType, + _In_ ULONG PortNumber, + _Out_ UDECXUSBDEVICE* UdecxUsbDevice + ) +/*++ + +Routine Description: + + Create and Plug In a Usb device. + +Arguments: + + DmfModule - This Module's handle. + UsbDeviceConfig - The Usb Configuration for the device to be created. + PortType - USB 2.0 or USB 3.0 port. + PortNumber - The plugIn port number. + UdecxUsbDevice - Usb device created. + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS ntStatus; + + PAGED_CODE(); + + FuncEntry(DMF_TRACE); + + DMFMODULE_VALIDATE_IN_METHOD(DmfModule, + UdeClient); + + ntStatus = UdeClient_CreateAndPlugUsbDevice(DmfModule, + UsbDeviceConfig, + PortType, + PortNumber, + UdecxUsbDevice); + if (! NT_SUCCESS(ntStatus)) + { + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "UdeClient_CreateAndPlugUsbDevice fails: ntStatus=%!STATUS!", ntStatus); + goto Exit; + } + + TraceEvents(TRACE_LEVEL_INFORMATION, DMF_TRACE, "Usb Device 0x%p Plugged In", *UdecxUsbDevice); + +Exit: + + FuncExit(DMF_TRACE, "ntStatus=%!STATUS!", ntStatus); + + return ntStatus; +} +#pragma code_seg() + +#pragma code_seg("PAGE") +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +DMF_UdeClient_EndpointCreate( + _In_ DMFMODULE DmfModule, + _In_ PUDECXUSBENDPOINT_INIT EndpointInit, + _In_ UdeClient_CONFIG_Endpoint* EndpointConfig, + _Out_ UDECXUSBENDPOINT* Endpoint + ) +/*++ + +Routine Description: + + Create an endpoint for a Usb device. + +Arguments: + + DmfModule - This Module's handle. + EndpointInit - Endpoint Init associated with the Usb device + EndpointConfig - Endpoint Configuration + Endpoint - The newly created Endpoint + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS ntStatus; + + PAGED_CODE(); + + FuncEntry(DMF_TRACE); + + DMFMODULE_VALIDATE_IN_METHOD(DmfModule, + UdeClient); + + ntStatus = UdeClient_EndpointCreate(DmfModule, + &EndpointInit, + EndpointConfig, + Endpoint); + if (! NT_SUCCESS(ntStatus)) + { + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "UdeClient_EndpointCreate fails: ntStatus=%!STATUS!", ntStatus); + goto Exit; + } + + TraceEvents(TRACE_LEVEL_INFORMATION, DMF_TRACE, "EndpointInit 0x%p Endpoint 0x%p Created", EndpointInit, *Endpoint); + +Exit: + + FuncExit(DMF_TRACE, "ntStatus=%!STATUS!", ntStatus); + + return ntStatus; +} +#pragma code_seg() + +_IRQL_requires_max_(PASSIVE_LEVEL) +_Must_inspect_result_ +NTSTATUS +DMF_UdeClient_DevicePlugOutAndDelete( + _In_ DMFMODULE DmfModule, + _In_ UDECXUSBDEVICE UdecxUsbDevice + ) +/*++ + +Routine Description: + + Unplug and delete an already plugged In Usb Device. + +Arguments: + + DmfModule - This Module's handle. + UdecxUsbDevice - Usb device to be plugged Out and Delete. + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS ntStatus; + + PAGED_CODE(); + + FuncEntry(DMF_TRACE); + + DMFMODULE_VALIDATE_IN_METHOD(DmfModule, + UdeClient); + + ntStatus = UdecxUsbDevicePlugOutAndDelete(UdecxUsbDevice); + if (! NT_SUCCESS(ntStatus)) + { + TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "UdecxUsbDevicePlugOutAndDelete fails: ntStatus=%!STATUS!", ntStatus); + goto Exit; + } + + TraceEvents(TRACE_LEVEL_INFORMATION, DMF_TRACE, "Usb Device 0x%p Plugged Out", UdecxUsbDevice); + +Exit: + + FuncExit(DMF_TRACE, "ntStatus=%!STATUS!", ntStatus); + + return ntStatus; +} +#pragma code_seg() + + +_IRQL_requires_max_(PASSIVE_LEVEL) +VOID +DMF_UdeClient_DeviceSignalFunctionWake( + _In_ DMFMODULE DmfModule, + _In_ UDECXUSBDEVICE UdecxUsbDevice, + _In_ ULONG Interface + ) +/*++ + +Routine Description: + + Send Wake Signal to the Usb device. + +Arguments: + + DmfModule - This Module's handle. + UdecxUsbDevice - Usb device to be which the signal is sent. + Interface - Interface. This is applicable only for USB 3.0 device. + +Return Value: + + None. + +--*/ +{ + CONTEXT_UdeClient_UsbDevice* usbDeviceContext; + + PAGED_CODE(); + + FuncEntry(DMF_TRACE); + + DMFMODULE_VALIDATE_IN_METHOD(DmfModule, + UdeClient); + + usbDeviceContext = UdeClientDeviceContextGet(UdecxUsbDevice); + if (usbDeviceContext->UsbDeviceSpeed == UdecxUsbSuperSpeed) + { + UdecxUsbDeviceSignalFunctionWake(UdecxUsbDevice, + Interface); + } + else + { + UdecxUsbDeviceSignalWake(UdecxUsbDevice); + } + + FuncExitVoid(DMF_TRACE); + +} +#pragma code_seg() + +// Module Static Methods +// + +_IRQL_requires_max_(PASSIVE_LEVEL) +_Must_inspect_result_ +NTSTATUS +DMF_UdeClient_Static_DeviceInitInitialize( + _In_ PWDFDEVICE_INIT DeviceInit + ) +/*++ + +Routine Description: + + This static method initialises the UdeCx. + Client of this Module MUST call this method before the device is created. + +Arguments: + + DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure. + +Return Value: + + NTSTATUS. + +--*/ +{ + return UdecxInitializeWdfDeviceInit(DeviceInit); +} + +// eof: Dmf_UdeClient.c +// diff --git a/Dmf/Modules.Library/Dmf_UdeClient.h b/Dmf/Modules.Library/Dmf_UdeClient.h new file mode 100644 index 00000000..d8b1cfff --- /dev/null +++ b/Dmf/Modules.Library/Dmf_UdeClient.h @@ -0,0 +1,301 @@ +/*++ + + Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + Dmf_UdeClient.h + +Abstract: + + Companion file to Dmf_UdeClient.c. + +Environment: + + Kernel-mode Driver Framework + +--*/ + +#pragma once + +// This Module is only supported in Kernel-mode because UDE only support Kernel-mode. +// +#if !defined(DMF_USER_MODE) + +#include + +// Specifies the type of Port on the virtual controller's root hub. +// +typedef enum +{ + UDE_CLIENT_PORT_USB2_0, + UDE_CLIENT_PORT_USB3_0 +} UDE_CLIENT_PLUGIN_PORT_TYPE; + +// Allows Client to return the Controllers Capability. +// +typedef +_Function_class_(EVT_DMF_UdeClient_Controller_QueryUsbCapability) +_IRQL_requires_max_(PASSIVE_LEVEL) +_IRQL_requires_same_ +NTSTATUS +EVT_DMF_UdeClient_Controller_QueryUsbCapability( + _In_ DMFMODULE DmfModule, + _In_ WDFDEVICE UdecxWdfDevice, + _In_ GUID* CapabilityType, + _In_ ULONG OutputBufferLength, + _Out_writes_to_opt_(OutputBufferLength, *ResultLength) PVOID OutputBuffer, + _Out_ ULONG* ResultLength + ); + +// Allows Client to do reset Controller or the devices attached to it. +// +typedef +_Function_class_(EVT_DMF_UdeClient_Controller_Reset) +_IRQL_requires_max_(PASSIVE_LEVEL) +_IRQL_requires_same_ +VOID +EVT_DMF_UdeClient_Controller_Reset( + _In_ DMFMODULE DmfModule, + _In_ WDFDEVICE UdecxWdfDevice + ); + +// Allows Client to perform other operations before the UsbDevice is created. +// +typedef +_Function_class_(EVT_DMF_UdeClient_UsbDevice_PreCreate) +_IRQL_requires_max_(PASSIVE_LEVEL) +_IRQL_requires_same_ +NTSTATUS +EVT_DMF_UdeClient_UsbDevice_PreCreate( + _In_ DMFMODULE DmfModule, + _In_ PUDECXUSBDEVICE_INIT UsbDeviceInit + ); + +// Allows Client to perform other operations after the UsbDevice is created. +// +typedef +_Function_class_(EVT_DMF_UdeClient_UsbDevice_PostCreate) +_IRQL_requires_max_(PASSIVE_LEVEL) +_IRQL_requires_same_ +VOID +EVT_DMF_UdeClient_UsbDevice_PostCreate( + _In_ DMFMODULE DmfModule, + _In_ UDECXUSBDEVICE UsbDevice + ); + +// Allows Client to perform IO operations on an endpoint. +// +typedef +_Function_class_(EVT_DMF_UdeClient_Endpoint_DeviceIoControl) +_IRQL_requires_same_ +_IRQL_requires_max_(PASSIVE_LEVEL) +VOID +EVT_DMF_UdeClient_Endpoint_DeviceIoControl( + _In_ DMFMODULE DmfModule, + _In_ UDECXUSBENDPOINT Endpoint, + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ size_t OutputBufferLength, + _In_ size_t InputBufferLength, + _In_ ULONG IoControlCode + ); + +// Endpoint Reset callback. This is mandatory. +// +typedef +_Function_class_(EVT_DMF_UdeClient_Endpoint_Reset) +_IRQL_requires_max_(PASSIVE_LEVEL) +_IRQL_requires_same_ +VOID +EVT_DMF_UdeClient_Endpoint_Reset( + _In_ DMFMODULE DmfModule, + _In_ UDECXUSBENDPOINT Endpoint, + _In_ WDFREQUEST Request + ); + +// Endpoint Start callback. This is optional. +// +typedef +_Function_class_(EVT_DMF_UdeClient_Endpoint_Start) +_IRQL_requires_max_(PASSIVE_LEVEL) +_IRQL_requires_same_ +VOID +EVT_DMF_UdeClient_Endpoint_Start( + _In_ DMFMODULE DmfModule, + _In_ UDECXUSBENDPOINT Endpoint + ); + +// Endpoint Purge callback. This is optional. +// +typedef +_Function_class_(EVT_DMF_UdeClient_Endpoint_Purge) +_IRQL_requires_max_(PASSIVE_LEVEL) +_IRQL_requires_same_ +VOID +EVT_DMF_UdeClient_Endpoint_Purge( + _In_ DMFMODULE DmfModule, + _In_ UDECXUSBENDPOINT Endpoint + ); + +typedef struct _UdeClient_CONFIG_Endpoint +{ + // Endpoint Address to used. + // + UCHAR EndpointAddress; + // Endpoint Queue Dispatch Type. + // + WDF_IO_QUEUE_DISPATCH_TYPE QueueDispatchType; + // DeviceIOControl callback. + // + EVT_DMF_UdeClient_Endpoint_DeviceIoControl* EvtEndpointDeviceIoControl; + // Endpoint Reset callbacks. This is Mandatory. + // + EVT_DMF_UdeClient_Endpoint_Reset* EvtEndpointReset; + // Endpoint Start callbacks. This is Optional. + // + EVT_DMF_UdeClient_Endpoint_Start* EvtEndpointStart; + // Endpoint Purge callbacks. This is Optional. + // + EVT_DMF_UdeClient_Endpoint_Purge* EvtEndpointPurge; +} UdeClient_CONFIG_Endpoint; + +typedef struct _UdeClient_CONFIG_UsbDevice +{ + // Usb Descriptors. + // + UCHAR* UsbDeviceDescriptor; + USHORT UsbDeviceDescriptorSize; + UCHAR* UsbBosDescriptor; + USHORT UsbBosDescriptorSize; + UCHAR* UsbConfigDescriptor; + USHORT UsbConfigDescriptorSize; + USHORT LanguageIdentifier; + UCHAR* UsbLanguageDescriptor; + USHORT UsbLanguageDescriptorSize; + UCHAR UsbLanguageDescriptorIndex; + WCHAR* UsbManufacturerStringDescriptor; + USHORT UsbManufacturerStringDescriptorSize; + UCHAR UsbManufacturerStringDescriptorIndex; + WCHAR* ProductStringDescriptor; + USHORT ProductStringDescriptorSize; + UCHAR ProductStringDescriptorIndex; + // Usb Device Speed. + // + UDECX_USB_DEVICE_SPEED UsbDeviceSpeed; + // Endpoint type. If Simple EndpointType, the endpoints are created before plugIn. + // + UDECX_ENDPOINT_TYPE UsbDeviceEndpointType; + // Simple Endpoint configuration details. + // Applicable only if UsbDeviceEndpointType is UdecxEndpointTypeSimple. + // + UdeClient_CONFIG_Endpoint* SimpleEndpointConfigs; + ULONG SimpleEndpointCount; +} UdeClient_CONFIG_UsbDevice; + +typedef struct _UdeClient_CONFIG_Controller +{ + // Number of Usb2.0 Ports. + // + USHORT NumberOfUsb20Ports; + // Number of Usb3.0 Ports. + // + USHORT NumberOfUsb30Ports; + // Type of reset operation supported by the Controller. + // + UDECX_WDF_DEVICE_RESET_ACTION ControllerResetAction; + // Callback for Controller's USB Capability Query. This is Mandatory. + // + EVT_DMF_UdeClient_Controller_QueryUsbCapability* EvtControllerQueryUsbCapability; + // Callback for Controller's Reset. + // + EVT_DMF_UdeClient_Controller_Reset* EvtControllerReset; +} UdeClient_CONFIG_Controller; + +// Client uses this structure to configure the Module specific parameters. +// +typedef struct _DMF_CONFIG_UdeClient +{ + // Configuration for the Usb Host Controller. + // + UdeClient_CONFIG_Controller UsbControllerConfig; + // Whether the Usb Device need to be Created-and-PluggedIn on Open or not. + // + BOOLEAN UsbCreateAndPlugOnOpen; + // Below two entries are looked at only if the UsbCreateAndPlugOnOpen is TRUE. + // + // Port Number and PortType to be used while PlugIn this Usb Device. + // + UDE_CLIENT_PLUGIN_PORT_TYPE PlugInPortType; + ULONG PlugInPortNumber; + // Configuration Details for the Usb Device. + // + UdeClient_CONFIG_UsbDevice UsbDeviceConfig; + // Callbacks for Pre and Post Create callbacks for any Usb Device Create + // This is applicable for both plugged in at Open or later. + // + EVT_DMF_UdeClient_UsbDevice_PreCreate* EvtUsbDevicePreCreate; + EVT_DMF_UdeClient_UsbDevice_PostCreate* EvtUsbDevicePostCreate; +} DMF_CONFIG_UdeClient; + +// This macro declares the following functions: +// DMF_UdeClient_ATTRIBUTES_INIT() +// DMF_CONFIG_UdeClient_AND_ATTRIBUTES_INIT() +// DMF_UdeClient_Create() +// +DECLARE_DMF_MODULE(UdeClient) + +// Module Methods +// + +_IRQL_requires_max_(PASSIVE_LEVEL) +_Must_inspect_result_ +NTSTATUS +DMF_UdeClient_DeviceCreateAndPlugIn( + _In_ DMFMODULE DmfModule, + _In_ UdeClient_CONFIG_UsbDevice* UsbDeviceConfig, + _In_ UDE_CLIENT_PLUGIN_PORT_TYPE PortType, + _In_ ULONG PortNumber, + _Out_ UDECXUSBDEVICE* UdecxUsbDevice + ); + +_IRQL_requires_max_(PASSIVE_LEVEL) +_Must_inspect_result_ +NTSTATUS +DMF_UdeClient_DeviceEndpointCreate( + _In_ DMFMODULE DmfModule, + _In_ PUDECXUSBENDPOINT_INIT EndpointInit, + _In_ UdeClient_CONFIG_Endpoint* ConfigEndpoint, + _Out_ UDECXUSBENDPOINT* Endpoint + ); + +_IRQL_requires_max_(PASSIVE_LEVEL) +_Must_inspect_result_ +NTSTATUS +DMF_UdeClient_DevicePlugOutAndDelete( + _In_ DMFMODULE DmfModule, + _In_ UDECXUSBDEVICE UdecxUsbDevice + ); + +_IRQL_requires_max_(PASSIVE_LEVEL) +VOID +DMF_UdeClient_DeviceSignalFunctionWake( + _In_ DMFMODULE DmfModule, + _In_ UDECXUSBDEVICE UdecxUsbDevice, + _In_ ULONG Interface + ); + +// Static Methods +// + +_IRQL_requires_max_(PASSIVE_LEVEL) +_Must_inspect_result_ +NTSTATUS +DMF_UdeClient_Static_DeviceInitInitialize( + _In_ PWDFDEVICE_INIT DeviceInit + ); + +#endif // !defined(DMF_USER_MODE) + +// eof: Dmf_UdeClient.h +// diff --git a/Dmf/Modules.Library/Dmf_UdeClient.md b/Dmf/Modules.Library/Dmf_UdeClient.md new file mode 100644 index 00000000..6382319c --- /dev/null +++ b/Dmf/Modules.Library/Dmf_UdeClient.md @@ -0,0 +1,510 @@ +## DMF_UdeClient + +----------------------------------------------------------------------------------------------------------------------------------- + +#### Module Summary + +----------------------------------------------------------------------------------------------------------------------------------- + +This Module implements the functionality of an USB Device Emulation Extension (UDECX) client driver. + +The Module allows the client manage creating/destroying a virtual USB Device and plugIn/plugOut it on virtual USB Host Controller's root port. +The created virtual USB device can have simple or complex endpoints. +The Module allows the client to create the USB device on demand or at the Module is opened. +If the Module configured for Create & Plug In at Open, the client can pass the configuration details on the virtual USB Device as well as the port to which +the device needed to plugged in, in the Module's configuration. + +----------------------------------------------------------------------------------------------------------------------------------- + +#### Module Configuration + +----------------------------------------------------------------------------------------------------------------------------------- +##### DMF_CONFIG_UdeClient +```` +typedef struct _UdeClient_CONFIG_Endpoint +{ + // Endpoint Address to used. + // + UCHAR EndpointAddress; + // Endpoint Queue Dispatch Type. + // + WDF_IO_QUEUE_DISPATCH_TYPE QueueDispatchType; + // DeviceIOControl callback. + // + EVT_DMF_UdeClient_Endpoint_DeviceIoControl* EvtEndpointDeviceIoControl; + // Endpoint Reset callbacks. This is Mandatory. + // + EVT_DMF_UdeClient_Endpoint_Reset* EvtEndpointReset; + // Endpoint Start callbacks. This is Optional. + // + EVT_DMF_UdeClient_Endpoint_Start* EvtEndpointStart; + // Endpoint Purge callbacks. This is Optional. + // + EVT_DMF_UdeClient_Endpoint_Purge* EvtEndpointPurge; +} UdeClient_CONFIG_Endpoint; + +typedef struct _UdeClient_CONFIG_UsbDevice +{ + // Usb Descriptors. + // + UCHAR* UsbDeviceDescriptor; + USHORT UsbDeviceDescriptorSize; + UCHAR* UsbBosDescriptor; + USHORT UsbBosDescriptorSize; + UCHAR* UsbConfigDescriptor; + USHORT UsbConfigDescriptorSize; + USHORT LanguageIdentifier; + UCHAR* UsbLanguageDescriptor; + USHORT UsbLanguageDescriptorSize; + UCHAR UsbLanguageDescriptorIndex; + WCHAR* UsbManufacturerStringDescriptor; + USHORT UsbManufacturerStringDescriptorSize; + UCHAR UsbManufacturerStringDescriptorIndex; + WCHAR* ProductStringDescriptor; + USHORT ProductStringDescriptorSize; + UCHAR ProductStringDescriptorIndex; + // Usb Device Speed. + // + UDECX_USB_DEVICE_SPEED UsbDeviceSpeed; + // Endpoint type. If Simple EndpointType, the endpoints are created before plugIn. + // + UDECX_ENDPOINT_TYPE UsbDeviceEndpointType; + // Simple Endpoint configuration details. + // Applicable only if UsbDeviceEndpointType is UdecxEndpointTypeSimple. + // + UdeClient_CONFIG_Endpoint* SimpleEndpointConfigs; + ULONG SimpleEndpointCount; +} UdeClient_CONFIG_UsbDevice; + +typedef struct _UdeClient_CONFIG_Controller +{ + // Number of Usb2.0 Ports. + // + USHORT NumberOfUsb20Ports; + // Number of Usb3.0 Ports. + // + USHORT NumberOfUsb30Ports; + // Type of reset operation supported by the Controller. + // + UDECX_WDF_DEVICE_RESET_ACTION ControllerResetAction; + // Callback for Controller's USB Capability Query. This is Mandatory. + // + EVT_DMF_UdeClient_Controller_QueryUsbCapability* EvtControllerQueryUsbCapability; + // Callback for Controller's Reset. + // + EVT_DMF_UdeClient_Controller_Reset* EvtControllerReset; +} UdeClient_CONFIG_Controller; + +// Client uses this structure to configure the Module specific parameters. +// +typedef struct _DMF_CONFIG_UdeClient +{ + // Configuration for the Usb Host Controller. + // + UdeClient_CONFIG_Controller UsbControllerConfig; + // Whether the Usb Device need to be Created-and-PluggedIn on Open or not. + // + BOOLEAN UsbCreateAndPlugOnOpen; + // Below two entries are looked at only if the UsbCreateAndPlugOnOpen is TRUE. + // + // Port Number and PortType to be used while PlugIn this Usb Device. + // + UDE_CLIENT_PLUGIN_PORT_TYPE PlugInPortType; + ULONG PlugInPortNumber; + // Configuration Details for the Usb Device. + // + UdeClient_CONFIG_UsbDevice UsbDeviceConfig; + // Callbacks for Pre and Post Create callbacks for any Usb Device Create + // This is applicable for both plugged in at Open or later. + // + EVT_DMF_UdeClient_UsbDevice_PreCreate* EvtUsbDevicePreCreate; + EVT_DMF_UdeClient_UsbDevice_PostCreate* EvtUsbDevicePostCreate; +} DMF_CONFIG_UdeClient; +```` +Member | Description +----|---- +UsbControllerConfig | Configuration for the Virtual Usb Host Controller to be created. +UsbCreateAndPlugOnOpen | Whether the Virtual Usb Device need to be Created-and-PluggedIn on Open or not. +PlugInPortType | Port Type (USB 2.0 or USB 3.0) to be used while PlugIn this Usb Device. Set this if the UsbCreateAndPlugOnOpen is set as TRUE. +PlugInPortNumber | Port Number to be used while PlugIn this Usb Device. Set this if the UsbCreateAndPlugOnOpen is set as TRUE. +UsbDeviceConfig | Configuration Details for the Usb Device to be created and plugged in. Set this if the UsbCreateAndPlugOnOpen is set as TRUE. +EvtUsbDevicePreCreate | Optional callback that allows clients perform pre create operation like setting up state change operation callbacks on the Usb Device being created. This callback is called just before the Usb Device is created. Refer UDECX_USB_DEVICE_STATE_CHANGE_CALLBACKS. +EvtUsbDevicePreCreate | Optional callback that allows clients perform post processing tasks. This callback is called just after the Usb Device is successfully created. + +----------------------------------------------------------------------------------------------------------------------------------- + +#### Module Enumeration Types + +----------------------------------------------------------------------------------------------------------------------------------- + +##### UDE_CLIENT_PLUGIN_PORT_TYPE +```` +typedef enum +{ + UDE_CLIENT_PORT_USB2_0, + UDE_CLIENT_PORT_USB3_0 +} UDE_CLIENT_PLUGIN_PORT_TYPE +```` +Member | Description +----|---- +UDE_CLIENT_PORT_USB2_0 | USB 2.0 Port. +UDE_CLIENT_PORT_USB3_0 | USB 3.0 Port. + +----------------------------------------------------------------------------------------------------------------------------------- + +#### Module Callbacks + +----------------------------------------------------------------------------------------------------------------------------------- +##### EVT_DMF_UdeClient_Controller_QueryUsbCapability +```` +typedef +_Function_class_(EVT_DMF_UdeClient_Controller_QueryUsbCapability) +_IRQL_requires_max_(PASSIVE_LEVEL) +_IRQL_requires_same_ +NTSTATUS +EVT_DMF_UdeClient_Controller_QueryUsbCapability( + _In_ DMFMODULE DmfModule, + _In_ WDFDEVICE UdecxWdfDevice, + _In_ GUID* CapabilityType, + _In_ ULONG OutputBufferLength, + _Out_writes_to_opt_(OutputBufferLength, *ResultLength) PVOID OutputBuffer, + _Out_ ULONG* ResultLength + ); +```` +This callback is called to determine the capabilities that are supported by the emulated USB host controller. + +##### Parameters +Parameter | Description +----|---- +DmfModule | An open DMF_UdeClient Module handle. +UdecxWdfDevice | A handle to a framework device object that represents the controller. +CapabilityType | Pointer to a GUID specifying the requested capability. The possible GUID values are: GUID_USB_CAPABILITY_CHAINED_MDLS GUID_USB_CAPABILITY_SELECTIVE_SUSPEND GUID_USB_CAPABILITY_FUNCTION_SUSPEND GUID_USB_CAPABILITY_DEVICE_CONNECTION_HIGH_SPEED_COMPATIBLE GUID_USB_CAPABILITY_DEVICE_CONNECTION_SUPER_SPEED_COMPATIBLE +OutputBufferLength | The length, in bytes, of the request's output buffer, if an output buffer is available. +OutputBuffer | An optional pointer to a location that receives the buffer's address. +ResultLength | A location that, on return, contains the size, in bytes, of the information that the callback function stored in OutputBuffer. + +----------------------------------------------------------------------------------------------------------------------------------- +##### EVT_DMF_UdeClient_Controller_Reset +```` +typedef +_Function_class_(EVT_DMF_UdeClient_Controller_Reset) +_IRQL_requires_max_(PASSIVE_LEVEL) +_IRQL_requires_same_ +VOID +EVT_DMF_UdeClient_Controller_Reset( + _In_ DMFMODULE DmfModule, + _In_ WDFDEVICE UdecxWdfDevice + ); +```` +This callback is called to notify the client that it must handle a reset request including resetting all downstream devices attached to the emulated host controller. This call is asynchronous. The client signals completion with status information by calling UdecxWdfDeviceResetComplete. + +NOTE: This is called ONLY if ControllerResetAction is set as UdecxWdfDeviceResetActionResetWdfDevice. + +##### Parameters +Parameter | Description +----|---- +DmfModule | An open DMF_UdeClient Module handle. +UdecxWdfDevice | A handle to a framework device object that represents the controller. + +----------------------------------------------------------------------------------------------------------------------------------- +##### EVT_DMF_UdeClient_UsbDevice_PreCreate +```` +typedef +_Function_class_(EVT_DMF_UdeClient_UsbDevice_PreCreate) +_IRQL_requires_max_(PASSIVE_LEVEL) +_IRQL_requires_same_ +NTSTATUS +EVT_DMF_UdeClient_UsbDevice_PreCreate( + _In_ DMFMODULE DmfModule, + _In_ PUDECXUSBDEVICE_INIT UsbDeviceInit + ); +```` +Optional callback that allows clients perform pre-create operation. This callback is called just before the Usb Device is created. +An example of pre-create operation is- setting up state change operation callbacks using UdecxUsbDeviceInitSetStateChangeCallbacks. + +##### Parameters +Parameter | Description +----|---- +DmfModule | An open DMF_UdeClient Module handle. +UsbDeviceInit | A pointer to an opaque structure that the client can pass into UdecxUsbDeviceInitSetStateChangeCallbacks. + +----------------------------------------------------------------------------------------------------------------------------------- +##### EVT_DMF_UdeClient_UsbDevice_PostCreate +```` +typedef +_Function_class_(EVT_DMF_UdeClient_UsbDevice_PostCreate) +_IRQL_requires_max_(PASSIVE_LEVEL) +_IRQL_requires_same_ +VOID +EVT_DMF_UdeClient_UsbDevice_PostCreate( + _In_ DMFMODULE DmfModule, + _In_ UDECXUSBDEVICE UsbDevice + ); +```` +Optional callback that allows clients perform post-create operation. This callback is called just after the Usb Device is created. +An example of post-create operation is- setting up context for this newly created device or saving the device for later use. + +##### Parameters +Parameter | Description +----|---- +DmfModule | An open DMF_UdeClient Module handle. +UsbDevice | A handle to a framework device object that represents the Virtual USB Device. + +----------------------------------------------------------------------------------------------------------------------------------- +##### EVT_DMF_UdeClient_Endpoint_DeviceIoControl +```` +VOID +EVT_DMF_UdeClient_Endpoint_DeviceIoControl( + _In_ DMFMODULE DmfModule, + _In_ UDECXUSBENDPOINT Endpoint, + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ size_t OutputBufferLength, + _In_ size_t InputBufferLength, + _In_ ULONG IoControlCode + ); +```` +Callback that allows clients to handle Requests coming in an Endpoint. This callback is Mandatory. The client completes the request and signals completion with +status by calling WdfRequestCompleteWithInformation method. + +##### Parameters +Parameter | Description +----|---- +DmfModule | An open DMF_UdeClient Module handle. +Endpoint | A handle to a framework device object that represents an Endpoint on a Virtual USB Device. +Queue | The WDFQUEUE associated with Request. +Request | A handle to a framework Request object that represent the request. +OutputBufferLength | Length of the request's output buffer, if an output buffer is available. +InputBufferLength | Length of the request's input buffer, if an input buffer is available. +IoControlCode | The driver-defined or system-defined I/O control code (IOCTL) that is associated with the request. + +----------------------------------------------------------------------------------------------------------------------------------- +##### EVT_DMF_UdeClient_Endpoint_Reset +```` +typedef +_Function_class_(EVT_DMF_UdeClient_Endpoint_Reset) +_IRQL_requires_max_(PASSIVE_LEVEL) +_IRQL_requires_same_ +VOID +EVT_DMF_UdeClient_Endpoint_Reset( + _In_ DMFMODULE DmfModule, + _In_ UDECXUSBENDPOINT Endpoint, + _In_ WDFREQUEST Request + ); +```` +Callback that allows clients to handle Reset an Endpoint. This callback is Mandatory. The client completes the request and signals completion with status by calling WdfRequestCompleteWithInformation method. + +##### Parameters +Parameter | Description +----|---- +DmfModule | An open DMF_UdeClient Module handle. +Endpoint | A handle to a framework device object that represents an Endpoint on a Virtual USB Device. +Request | A handle to a framework Request object that represent the request to reset the endpoint. + +----------------------------------------------------------------------------------------------------------------------------------- +##### EVT_DMF_UdeClient_Endpoint_Start +```` +typedef +_Function_class_(EVT_DMF_UdeClient_Endpoint_Start) +_IRQL_requires_max_(PASSIVE_LEVEL) +_IRQL_requires_same_ +VOID +EVT_DMF_UdeClient_Endpoint_Start( + _In_ DMFMODULE DmfModule, + _In_ UDECXUSBENDPOINT Endpoint + ); +```` +Callback to indicate to clients that that framework is starting processing I/O request on an Endpoint. This callback is Optional. + +##### Parameters +Parameter | Description +----|---- +DmfModule | An open DMF_UdeClient Module handle. +Endpoint | A handle to a framework device object that represents an Endpoint on a Virtual USB Device. +----------------------------------------------------------------------------------------------------------------------------------- +##### EVT_DMF_UdeClient_Endpoint_Purge +```` +typedef +_Function_class_(EVT_DMF_UdeClient_Endpoint_Purge) +_IRQL_requires_max_(PASSIVE_LEVEL) +_IRQL_requires_same_ +VOID +EVT_DMF_UdeClient_Endpoint_Purge( + _In_ DMFMODULE DmfModule, + _In_ UDECXUSBENDPOINT Endpoint + ); +```` +Callback to indicate to clients that framework has stopped queuing I/O requests to the endpoint's queue and cancelled unprocessed requests. This callback is Optional. +The client is required to ensure all I/O forwarded from the endpoint’s queue has been completed, and that newly forwarded I/O request fail, until a callback to EVT_DMF_UdeClient_Endpoint_Start. + +##### Parameters +Parameter | Description +----|---- +DmfModule | An open DMF_UdeClient Module handle. +Endpoint | A handle to a framework device object that represents an Endpoint on a Virtual USB Device. + +----------------------------------------------------------------------------------------------------------------------------------- +#### Module Methods + +----------------------------------------------------------------------------------------------------------------------------------- + +##### DMF_UdeClient_DeviceCreateAndPlugIn + +```` +_IRQL_requires_max_(PASSIVE_LEVEL) +_Must_inspect_result_ +NTSTATUS +DMF_UdeClient_DeviceCreateAndPlugIn( + _In_ DMFMODULE DmfModule, + _In_ UdeClient_CONFIG_UsbDevice* UsbDeviceConfig, + _In_ UDE_CLIENT_PLUGIN_PORT_TYPE PortType, + _In_ ULONG PortNumber, + _Out_ UDECXUSBDEVICE* UdecxUsbDevice + ); +```` + +This Method is for creating Virtual USB device and then plug it into the desired port on the virtual controller's root hub. + +##### Returns + +NTSTATUS + +##### Parameters +Parameter | Description +----|---- +DmfModule | An open DMF_UdeClient Module handle. +UsbDeviceConfig | Configuration Details for the Usb Device to be created and plugged in. +PortType | Port Type (USB 2.0 or USB 3.0) to be used while PlugIn this Usb Device. +PortNumber | Port Number to be used while PlugIn this Usb Device. +UdecxUsbDevice | A handle to a framework device object that represents the newly created Virtual USB Device. + +----------------------------------------------------------------------------------------------------------------------------------- +##### DMF_UdeClient_DeviceEndpointCreate + +```` +_IRQL_requires_max_(PASSIVE_LEVEL) +_Must_inspect_result_ +NTSTATUS +DMF_UdeClient_DeviceEndpointCreate( + _In_ DMFMODULE DmfModule, + _In_ PUDECXUSBENDPOINT_INIT EndpointInit, + _In_ UdeClient_CONFIG_Endpoint* EndpointConfig, + _Out_ UDECXUSBENDPOINT* Endpoint + ); +```` + +This Method is for creating an Endpoint on a Virtual USB device. + +##### Returns + +NTSTATUS + +##### Parameters +Parameter | Description +----|---- +DmfModule | An open DMF_UdeClient Module handle. +EndpointInit | An opaque structure that represent an initialization configuration for Endpoint. +EndpointConfig | Endpoint configuration. +Endpoint | A handle to a framework device object that represents the newly created Endpoint. + +----------------------------------------------------------------------------------------------------------------------------------- +##### DMF_UdeClient_DevicePlugOutAndDelete + +```` +_IRQL_requires_max_(PASSIVE_LEVEL) +_Must_inspect_result_ +NTSTATUS +DMF_UdeClient_DevicePlugOutAndDelete( + _In_ DMFMODULE DmfModule, + _In_ UDECXUSBDEVICE UdecxUsbDevice + ); +```` + +This Method is for plugging the virtual USB device out and then deleting it. + +##### Returns + +NTSTATUS + +##### Parameters +Parameter | Description +----|---- +DmfModule | An open DMF_UdeClient Module handle. +UdecxUsbDevice | A handle to a framework device object that represents the Virtual USB Device. + +----------------------------------------------------------------------------------------------------------------------------------- +##### DMF_UdeClient_DeviceSignalFunctionWake + +```` +_IRQL_requires_max_(PASSIVE_LEVEL) +VOID +DMF_UdeClient_DeviceSignalFunctionWake( + _In_ DMFMODULE DmfModule, + _In_ UDECXUSBDEVICE UdecxUsbDevice, + _In_ ULONG Interface + ); +```` + +This Method is for initiating wake up of the USB 2.0 device or a specified function in a USB 3.0 device from a low power state. + +##### Returns + +None + +##### Parameters +Parameter | Description +----|---- +DmfModule | An open DMF_UdeClient Module handle. +UdecxUsbDevice | A handle to a framework device object that represents the Virtual USB Device. +Interface | Specifies the function. This is applicable only for USB 3.0 device. Pass 0 for USB 2.0 device. + +----------------------------------------------------------------------------------------------------------------------------------- +##### DMF_UdeClient_Static_DeviceInitInitialize + +```` +_IRQL_requires_max_(PASSIVE_LEVEL) +_Must_inspect_result_ +NTSTATUS +DMF_UdeClient_Static_DeviceInitInitialize( + _In_ PWDFDEVICE_INIT DeviceInit + ); +```` + +This Method is for initializing the UDECx. This MUST be called before the WdfDevice is created. + +##### Returns + +None + +##### Parameters +Parameter | Description +----|---- +DeviceInit | An opaque structure for Device Initalization. + +----------------------------------------------------------------------------------------------------------------------------------- + +#### Module Children + +* Dmf_IoctlHandler + +----------------------------------------------------------------------------------------------------------------------------------- + +#### Module Implementation Details + +----------------------------------------------------------------------------------------------------------------------------------- + +#### Examples + +* SurfaceTypeCoverV7FprUdeDriver + +----------------------------------------------------------------------------------------------------------------------------------- + +----------------------------------------------------------------------------------------------------------------------------------- +#### Module Category + +----------------------------------------------------------------------------------------------------------------------------------- + +Driver Pattern + +----------------------------------------------------------------------------------------------------------------------------------- + diff --git a/Dmf/Modules.Library/Dmf_VirtualHidAmbientColorSensor.h b/Dmf/Modules.Library/Dmf_VirtualHidAmbientColorSensor.h index 94d98c8f..b2abbcb0 100644 --- a/Dmf/Modules.Library/Dmf_VirtualHidAmbientColorSensor.h +++ b/Dmf/Modules.Library/Dmf_VirtualHidAmbientColorSensor.h @@ -40,7 +40,6 @@ Module Name: #pragma pack(1) typedef struct { - UCHAR ReportId; LONG Illuminance; USHORT ChromaticityX; USHORT ChromaticityY; diff --git a/Dmf/Modules.Library/Dmf_VirtualHidMini.c b/Dmf/Modules.Library/Dmf_VirtualHidMini.c index b20ebdae..c03210fd 100644 --- a/Dmf/Modules.Library/Dmf_VirtualHidMini.c +++ b/Dmf/Modules.Library/Dmf_VirtualHidMini.c @@ -245,8 +245,8 @@ Return Value: WDFMEMORY outputMemory; size_t inputBufferLength; size_t outputBufferLength; - PVOID inputBuffer; - PVOID outputBuffer; + VOID* inputBuffer; + VOID* outputBuffer; // Get report Id from input buffer. // @@ -335,21 +335,37 @@ Return Value: WDFMEMORY outputMemory; size_t inputBufferLength; size_t outputBufferLength; - PVOID inputBuffer; + VOID* inputBuffer; // Get report Id from output buffer length. // ntStatus = WdfRequestRetrieveOutputMemory(Request, &outputMemory); - if ( !NT_SUCCESS(ntStatus) ) + // NOTE: Exception for STATUS_BUFFER_TOO_SMALL is for legacy devices + // under UMDF. + // + if (! NT_SUCCESS(ntStatus) && + (ntStatus != STATUS_BUFFER_TOO_SMALL)) { TraceEvents(TRACE_LEVEL_ERROR, DMF_TRACE, "WdfRequestRetrieveOutputMemory fails: ntStatus=%!STATUS!", ntStatus); goto Exit; } - WdfMemoryGetBuffer(outputMemory, - &outputBufferLength); - Packet->reportId = (UCHAR) outputBufferLength; + if (NT_SUCCESS(ntStatus)) + { + WdfMemoryGetBuffer(outputMemory, + &outputBufferLength); + // NOTE: See comment above in to explain this. + // + Packet->reportId = (UCHAR) outputBufferLength; + } + else + { + // NOTE: Exception for STATUS_BUFFER_TOO_SMALL is for legacy devices + // under UMDF. + // + Packet->reportId = 0; + } // Get report buffer from input buffer. //