mirror of
https://github.com/nefarius/ViGEmBus.git
synced 2025-08-10 00:52:17 +00:00
Ported everything
This commit is contained in:
@@ -28,49 +28,7 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
//
|
||||
// Used to identify children in the device list of the bus.
|
||||
//
|
||||
typedef struct _PDO_IDENTIFICATION_DESCRIPTION
|
||||
{
|
||||
WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER Header; // should contain this header
|
||||
|
||||
//
|
||||
// Unique serial number of the device on the bus
|
||||
//
|
||||
ULONG SerialNo;
|
||||
|
||||
//
|
||||
// PID of the process creating this PDO
|
||||
//
|
||||
DWORD OwnerProcessId;
|
||||
|
||||
//
|
||||
// Device type this PDO is emulating
|
||||
//
|
||||
VIGEM_TARGET_TYPE TargetType;
|
||||
|
||||
//
|
||||
// If set, the vendor ID the emulated device is reporting
|
||||
//
|
||||
USHORT VendorId;
|
||||
|
||||
//
|
||||
// If set, the product ID the emulated device is reporting
|
||||
//
|
||||
USHORT ProductId;
|
||||
|
||||
//
|
||||
// Is the current device owner another driver?
|
||||
//
|
||||
BOOLEAN OwnerIsDriver;
|
||||
|
||||
//
|
||||
// SessionId associated with file handle. Used to map file handles to emulated gamepad devices
|
||||
//
|
||||
LONG SessionId;
|
||||
|
||||
} PDO_IDENTIFICATION_DESCRIPTION, *PPDO_IDENTIFICATION_DESCRIPTION;
|
||||
|
||||
//
|
||||
// The PDO device-extension (context).
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
|
||||
|
||||
#include "busenum.h"
|
||||
#include <wdmguid.h>
|
||||
#include "driver.tmh"
|
||||
#include <wdmguid.h>
|
||||
|
||||
#ifdef ALLOC_PRAGMA
|
||||
#pragma alloc_text (INIT, DriverEntry)
|
||||
@@ -35,10 +35,21 @@
|
||||
#pragma alloc_text (PAGE, Bus_DeviceFileCreate)
|
||||
#pragma alloc_text (PAGE, Bus_FileClose)
|
||||
#pragma alloc_text (PAGE, Bus_EvtDriverContextCleanup)
|
||||
#pragma alloc_text (PAGE, Bus_PdoStageResult)
|
||||
#endif
|
||||
|
||||
|
||||
#include "EmulationTargetPDO.hpp"
|
||||
#include "XusbPdo.hpp"
|
||||
#include "Ds4Pdo.hpp"
|
||||
|
||||
using ViGEm::Bus::Core::PDO_IDENTIFICATION_DESCRIPTION;
|
||||
using ViGEm::Bus::Core::EmulationTargetPDO;
|
||||
using ViGEm::Bus::Targets::EmulationTargetXUSB;
|
||||
using ViGEm::Bus::Targets::EmulationTargetDS4;
|
||||
|
||||
|
||||
EXTERN_C_START
|
||||
|
||||
//
|
||||
// Driver entry routine.
|
||||
//
|
||||
@@ -94,12 +105,7 @@ NTSTATUS Bus_EvtDeviceAdd(IN WDFDRIVER Driver, IN PWDFDEVICE_INIT DeviceInit)
|
||||
WDF_FILEOBJECT_CONFIG foConfig;
|
||||
WDF_OBJECT_ATTRIBUTES fdoAttributes;
|
||||
WDF_OBJECT_ATTRIBUTES fileHandleAttributes;
|
||||
WDF_OBJECT_ATTRIBUTES collectionAttributes;
|
||||
WDF_OBJECT_ATTRIBUTES timerAttributes;
|
||||
PFDO_DEVICE_DATA pFDOData;
|
||||
VIGEM_BUS_INTERFACE busInterface;
|
||||
PINTERFACE interfaceHeader;
|
||||
WDF_TIMER_CONFIG reqTimerCfg;
|
||||
|
||||
UNREFERENCED_PARAMETER(Driver);
|
||||
|
||||
@@ -162,104 +168,11 @@ NTSTATUS Bus_EvtDeviceAdd(IN WDFDRIVER Driver, IN PWDFDEVICE_INIT DeviceInit)
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Create pending requests collection & lock
|
||||
|
||||
WDF_OBJECT_ATTRIBUTES_INIT(&collectionAttributes);
|
||||
collectionAttributes.ParentObject = device;
|
||||
|
||||
status = WdfCollectionCreate(&collectionAttributes, &pFDOData->PendingPluginRequests);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_DRIVER,
|
||||
"WdfCollectionCreate failed with status %!STATUS!",
|
||||
status);
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
WDF_OBJECT_ATTRIBUTES_INIT(&collectionAttributes);
|
||||
collectionAttributes.ParentObject = device;
|
||||
|
||||
status = WdfSpinLockCreate(&collectionAttributes, &pFDOData->PendingPluginRequestsLock);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_DRIVER,
|
||||
"WdfSpinLockCreate failed with status %!STATUS!",
|
||||
status);
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Create timer for sweeping up orphaned requests
|
||||
|
||||
WDF_TIMER_CONFIG_INIT_PERIODIC(
|
||||
&reqTimerCfg,
|
||||
Bus_PlugInRequestCleanUpEvtTimerFunc,
|
||||
ORC_TIMER_START_DELAY
|
||||
);
|
||||
WDF_OBJECT_ATTRIBUTES_INIT(&timerAttributes);
|
||||
timerAttributes.ParentObject = device;
|
||||
|
||||
status = WdfTimerCreate(&reqTimerCfg, &timerAttributes, &pFDOData->PendingPluginRequestsCleanupTimer);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_DRIVER,
|
||||
"WdfTimerCreate failed with status %!STATUS!",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Add query interface
|
||||
|
||||
//
|
||||
// Set up the common interface header
|
||||
//
|
||||
interfaceHeader = &busInterface.InterfaceHeader;
|
||||
|
||||
interfaceHeader->Size = sizeof(VIGEM_BUS_INTERFACE);
|
||||
interfaceHeader->Version = VIGEM_BUS_INTERFACE_VERSION;
|
||||
interfaceHeader->Context = (PVOID)device;
|
||||
|
||||
//
|
||||
// We don't pay any particular attention to the reference
|
||||
// counting of this interface, but we MUST specify routines for
|
||||
// it. Luckily the framework provides dummy routines
|
||||
//
|
||||
interfaceHeader->InterfaceReference = WdfDeviceInterfaceReferenceNoOp;
|
||||
interfaceHeader->InterfaceDereference = WdfDeviceInterfaceDereferenceNoOp;
|
||||
|
||||
busInterface.BusPdoStageResult = Bus_PdoStageResult;
|
||||
|
||||
WDF_QUERY_INTERFACE_CONFIG queryInterfaceConfig;
|
||||
|
||||
WDF_QUERY_INTERFACE_CONFIG_INIT(&queryInterfaceConfig,
|
||||
interfaceHeader,
|
||||
&GUID_VIGEM_INTERFACE_PDO,
|
||||
WDF_NO_EVENT_CALLBACK);
|
||||
|
||||
status = WdfDeviceAddQueryInterface(device,
|
||||
&queryInterfaceConfig);
|
||||
|
||||
if (!NT_SUCCESS(status)) {
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_DRIVER,
|
||||
"WdfDeviceAddQueryInterface failed with status %!STATUS!",
|
||||
status);
|
||||
return(status);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Create default I/O queue for FDO
|
||||
|
||||
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig, WdfIoQueueDispatchParallel);
|
||||
|
||||
queueConfig.EvtIoDeviceControl = Bus_EvtIoDeviceControl;
|
||||
queueConfig.EvtIoInternalDeviceControl = Bus_EvtIoInternalDeviceControl;
|
||||
queueConfig.EvtIoDefault = Bus_EvtIoDefault;
|
||||
|
||||
__analysis_assume(queueConfig.EvtIoStop != 0);
|
||||
@@ -442,21 +355,20 @@ Bus_FileClose(
|
||||
break;
|
||||
}
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_DRIVER,
|
||||
"PDO properties: status = %!STATUS!, pdoPID = %d, curPID = %d, pdoSID = %d, curSID = %d, internal = %d",
|
||||
(int)childInfo.Status,
|
||||
(int)description.OwnerProcessId,
|
||||
(int)CURRENT_PROCESS_ID(),
|
||||
(int)description.SessionId,
|
||||
(int)pFileData->SessionId,
|
||||
(int)description.OwnerIsDriver
|
||||
);
|
||||
//TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
// TRACE_DRIVER,
|
||||
// "PDO properties: status = %!STATUS!, pdoPID = %d, curPID = %d, pdoSID = %d, curSID = %d, internal = %d",
|
||||
// (int)childInfo.Status,
|
||||
// (int)description.OwnerProcessId,
|
||||
// (int)CURRENT_PROCESS_ID(),
|
||||
// (int)description.SessionId,
|
||||
// (int)pFileData->SessionId,
|
||||
// (int)description.OwnerIsDriver
|
||||
//);
|
||||
|
||||
// Only unplug devices with matching session id
|
||||
if (childInfo.Status == WdfChildListRetrieveDeviceSuccess
|
||||
&& description.SessionId == pFileData->SessionId
|
||||
&& !description.OwnerIsDriver)
|
||||
&& description.SessionId == pFileData->SessionId)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION,
|
||||
TRACE_DRIVER,
|
||||
@@ -512,151 +424,4 @@ Return Value:
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
// Called by PDO when a boot-up stage has been completed
|
||||
//
|
||||
_Use_decl_annotations_
|
||||
VOID
|
||||
Bus_PdoStageResult(
|
||||
_In_ PINTERFACE InterfaceHeader,
|
||||
_In_ VIGEM_PDO_STAGE Stage,
|
||||
_In_ ULONG Serial,
|
||||
_In_ NTSTATUS Status
|
||||
)
|
||||
{
|
||||
ULONG i;
|
||||
PFDO_DEVICE_DATA pFdoData;
|
||||
WDFREQUEST curRequest;
|
||||
ULONG curSerial;
|
||||
ULONG items;
|
||||
|
||||
UNREFERENCED_PARAMETER(InterfaceHeader);
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION,
|
||||
TRACE_DRIVER,
|
||||
"%!FUNC! Entry (stage = %d, serial = %d, status = %!STATUS!)",
|
||||
Stage, Serial, Status);
|
||||
|
||||
pFdoData = FdoGetData(InterfaceHeader->Context);
|
||||
|
||||
//
|
||||
// If any stage fails or is last stage, get associated request and complete it
|
||||
//
|
||||
if (!NT_SUCCESS(Status) || Stage == ViGEmPdoInitFinished)
|
||||
{
|
||||
WdfSpinLockAcquire(pFdoData->PendingPluginRequestsLock);
|
||||
|
||||
items = WdfCollectionGetCount(pFdoData->PendingPluginRequests);
|
||||
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION,
|
||||
TRACE_DRIVER,
|
||||
"Items count: %d",
|
||||
items);
|
||||
|
||||
for (i = 0; i < items; i++)
|
||||
{
|
||||
curRequest = WdfCollectionGetItem(pFdoData->PendingPluginRequests, i);
|
||||
curSerial = PluginRequestGetData(curRequest)->Serial;
|
||||
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION,
|
||||
TRACE_DRIVER,
|
||||
"Serial: %d, curSerial: %d",
|
||||
Serial, curSerial);
|
||||
|
||||
if (Serial == curSerial)
|
||||
{
|
||||
WdfRequestComplete(curRequest, Status);
|
||||
|
||||
WdfCollectionRemove(pFdoData->PendingPluginRequests, curRequest);
|
||||
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION,
|
||||
TRACE_DRIVER,
|
||||
"Removed item with serial: %d",
|
||||
curSerial);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
WdfSpinLockRelease(pFdoData->PendingPluginRequestsLock);
|
||||
}
|
||||
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Exit");
|
||||
}
|
||||
|
||||
_Use_decl_annotations_
|
||||
VOID
|
||||
Bus_PlugInRequestCleanUpEvtTimerFunc(
|
||||
WDFTIMER Timer
|
||||
)
|
||||
{
|
||||
ULONG i;
|
||||
PFDO_DEVICE_DATA pFdoData;
|
||||
WDFREQUEST curRequest;
|
||||
ULONG items;
|
||||
WDFDEVICE device;
|
||||
PFDO_PLUGIN_REQUEST_DATA pPluginData;
|
||||
LONGLONG freq;
|
||||
LARGE_INTEGER pcNow;
|
||||
LONGLONG ellapsed;
|
||||
|
||||
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Entry");
|
||||
|
||||
device = WdfTimerGetParentObject(Timer);
|
||||
pFdoData = FdoGetData(device);
|
||||
|
||||
WdfSpinLockAcquire(pFdoData->PendingPluginRequestsLock);
|
||||
|
||||
items = WdfCollectionGetCount(pFdoData->PendingPluginRequests);
|
||||
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION,
|
||||
TRACE_DRIVER,
|
||||
"Items count: %d",
|
||||
items);
|
||||
|
||||
//
|
||||
// Collection is empty; no need to keep timer running
|
||||
//
|
||||
if (items == 0)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_DRIVER,
|
||||
"Collection is empty, stopping periodic timer");
|
||||
WdfTimerStop(Timer, FALSE);
|
||||
}
|
||||
|
||||
for (i = 0; i < items; i++)
|
||||
{
|
||||
curRequest = WdfCollectionGetItem(pFdoData->PendingPluginRequests, i);
|
||||
pPluginData = PluginRequestGetData(curRequest);
|
||||
|
||||
freq = pPluginData->Frequency.QuadPart / ORC_PC_FREQUENCY_DIVIDER;
|
||||
pcNow = KeQueryPerformanceCounter(NULL);
|
||||
ellapsed = (pcNow.QuadPart - pPluginData->Timestamp.QuadPart) / freq;
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_DRIVER,
|
||||
"PDO (serial = %d) plugin request age: %llu ms",
|
||||
pPluginData->Serial, ellapsed);
|
||||
|
||||
if (ellapsed >= ORC_REQUEST_MAX_AGE)
|
||||
{
|
||||
WdfRequestComplete(curRequest, STATUS_SUCCESS);
|
||||
|
||||
WdfCollectionRemove(pFdoData->PendingPluginRequests, curRequest);
|
||||
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION,
|
||||
TRACE_DRIVER,
|
||||
"Removed item with serial: %d",
|
||||
pPluginData->Serial);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
WdfSpinLockRelease(pFdoData->PendingPluginRequestsLock);
|
||||
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Exit");
|
||||
}
|
||||
|
||||
EXTERN_C_END
|
||||
468
sys/Ds4.c
468
sys/Ds4.c
@@ -1,468 +0,0 @@
|
||||
/*
|
||||
* Virtual Gamepad Emulation Framework - Windows kernel-mode bus driver
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2016-2019 Nefarius Software Solutions e.U. and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#include "busenum.h"
|
||||
#include <hidclass.h>
|
||||
#include "ds4.tmh"
|
||||
|
||||
NTSTATUS Ds4_PreparePdo(PWDFDEVICE_INIT DeviceInit, PUNICODE_STRING DeviceId, PUNICODE_STRING DeviceDescription)
|
||||
{
|
||||
NTSTATUS status;
|
||||
UNICODE_STRING buffer;
|
||||
|
||||
// prepare device description
|
||||
status = RtlUnicodeStringInit(DeviceDescription, L"Virtual DualShock 4 Controller");
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_DS4,
|
||||
"RtlUnicodeStringInit failed with status %!STATUS!",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
||||
// Set hardware IDs
|
||||
RtlUnicodeStringInit(&buffer, L"USB\\VID_054C&PID_05C4&REV_0100");
|
||||
|
||||
status = WdfPdoInitAddHardwareID(DeviceInit, &buffer);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_DS4,
|
||||
"WdfPdoInitAddHardwareID failed with status %!STATUS!",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
||||
RtlUnicodeStringCopy(DeviceId, &buffer);
|
||||
|
||||
RtlUnicodeStringInit(&buffer, L"USB\\VID_054C&PID_05C4");
|
||||
|
||||
status = WdfPdoInitAddHardwareID(DeviceInit, &buffer);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_DS4,
|
||||
"WdfPdoInitAddHardwareID failed with status %!STATUS!",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
||||
// Set compatible IDs
|
||||
RtlUnicodeStringInit(&buffer, L"USB\\Class_03&SubClass_00&Prot_00");
|
||||
|
||||
status = WdfPdoInitAddCompatibleID(DeviceInit, &buffer);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_DS4,
|
||||
"WdfPdoInitAddCompatibleID (#01) failed with status %!STATUS!",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
||||
RtlUnicodeStringInit(&buffer, L"USB\\Class_03&SubClass_00");
|
||||
|
||||
status = WdfPdoInitAddCompatibleID(DeviceInit, &buffer);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_DS4,
|
||||
"WdfPdoInitAddCompatibleID (#02) failed with status %!STATUS!",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
||||
RtlUnicodeStringInit(&buffer, L"USB\\Class_03");
|
||||
|
||||
status = WdfPdoInitAddCompatibleID(DeviceInit, &buffer);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_DS4,
|
||||
"WdfPdoInitAddCompatibleID (#03) failed with status %!STATUS!",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS Ds4_PrepareHardware(WDFDEVICE Device)
|
||||
{
|
||||
NTSTATUS status;
|
||||
WDF_QUERY_INTERFACE_CONFIG ifaceCfg;
|
||||
INTERFACE devinterfaceHid;
|
||||
|
||||
devinterfaceHid.Size = sizeof(INTERFACE);
|
||||
devinterfaceHid.Version = 1;
|
||||
devinterfaceHid.Context = (PVOID)Device;
|
||||
|
||||
devinterfaceHid.InterfaceReference = WdfDeviceInterfaceReferenceNoOp;
|
||||
devinterfaceHid.InterfaceDereference = WdfDeviceInterfaceDereferenceNoOp;
|
||||
|
||||
// Expose GUID_DEVINTERFACE_HID so HIDUSB can initialize
|
||||
WDF_QUERY_INTERFACE_CONFIG_INIT(&ifaceCfg, (PINTERFACE)&devinterfaceHid, &GUID_DEVINTERFACE_HID, NULL);
|
||||
|
||||
status = WdfDeviceAddQueryInterface(Device, &ifaceCfg);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_DS4,
|
||||
"WdfDeviceAddQueryInterface failed with status %!STATUS!",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
||||
PDS4_DEVICE_DATA ds4Data = Ds4GetData(Device);
|
||||
|
||||
// Set default HID input report (everything zero`d)
|
||||
UCHAR DefaultHidReport[DS4_REPORT_SIZE] =
|
||||
{
|
||||
0x01, 0x82, 0x7F, 0x7E, 0x80, 0x08, 0x00, 0x58,
|
||||
0x00, 0x00, 0xFD, 0x63, 0x06, 0x03, 0x00, 0xFE,
|
||||
0xFF, 0xFC, 0xFF, 0x79, 0xFD, 0x1B, 0x14, 0xD1,
|
||||
0xE9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00,
|
||||
0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
|
||||
0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
|
||||
0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00
|
||||
};
|
||||
|
||||
// Initialize HID reports to defaults
|
||||
RtlCopyBytes(ds4Data->Report, DefaultHidReport, DS4_REPORT_SIZE);
|
||||
RtlZeroMemory(&ds4Data->OutputReport, sizeof(DS4_OUTPUT_REPORT));
|
||||
|
||||
// Start pending IRP queue flush timer
|
||||
WdfTimerStart(ds4Data->PendingUsbInRequestsTimer, DS4_QUEUE_FLUSH_PERIOD);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS Ds4_AssignPdoContext(WDFDEVICE Device, PPDO_IDENTIFICATION_DESCRIPTION Description)
|
||||
{
|
||||
NTSTATUS status;
|
||||
PDS4_DEVICE_DATA ds4 = Ds4GetData(Device);
|
||||
|
||||
// Initialize periodic timer
|
||||
WDF_TIMER_CONFIG timerConfig;
|
||||
WDF_TIMER_CONFIG_INIT_PERIODIC(&timerConfig, Ds4_PendingUsbRequestsTimerFunc, DS4_QUEUE_FLUSH_PERIOD);
|
||||
|
||||
// Timer object attributes
|
||||
WDF_OBJECT_ATTRIBUTES timerAttribs;
|
||||
WDF_OBJECT_ATTRIBUTES_INIT(&timerAttribs);
|
||||
|
||||
// PDO is parent
|
||||
timerAttribs.ParentObject = Device;
|
||||
|
||||
// Create timer
|
||||
status = WdfTimerCreate(&timerConfig, &timerAttribs, &ds4->PendingUsbInRequestsTimer);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_DS4,
|
||||
"WdfTimerCreate failed with status %!STATUS!",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
||||
// Load/generate MAC address
|
||||
|
||||
// TODO: tidy up this region
|
||||
|
||||
WDFKEY keyParams, keyTargets, keyDS, keySerial;
|
||||
UNICODE_STRING keyName, valueName;
|
||||
|
||||
status = WdfDriverOpenParametersRegistryKey(WdfGetDriver(), STANDARD_RIGHTS_ALL, WDF_NO_OBJECT_ATTRIBUTES, &keyParams);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_DS4,
|
||||
"WdfDriverOpenParametersRegistryKey failed with status %!STATUS!",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
||||
RtlUnicodeStringInit(&keyName, L"Targets");
|
||||
|
||||
status = WdfRegistryCreateKey(
|
||||
keyParams,
|
||||
&keyName,
|
||||
KEY_ALL_ACCESS,
|
||||
REG_OPTION_NON_VOLATILE,
|
||||
NULL,
|
||||
WDF_NO_OBJECT_ATTRIBUTES,
|
||||
&keyTargets
|
||||
);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_DS4,
|
||||
"WdfRegistryCreateKey failed with status %!STATUS!",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
||||
RtlUnicodeStringInit(&keyName, L"DualShock");
|
||||
|
||||
status = WdfRegistryCreateKey(
|
||||
keyTargets,
|
||||
&keyName,
|
||||
KEY_ALL_ACCESS,
|
||||
REG_OPTION_NON_VOLATILE,
|
||||
NULL,
|
||||
WDF_NO_OBJECT_ATTRIBUTES,
|
||||
&keyDS
|
||||
);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_DS4,
|
||||
"WdfRegistryCreateKey failed with status %!STATUS!",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
||||
DECLARE_UNICODE_STRING_SIZE(serialPath, 4);
|
||||
RtlUnicodeStringPrintf(&serialPath, L"%04d", Description->SerialNo);
|
||||
|
||||
status = WdfRegistryCreateKey(
|
||||
keyDS,
|
||||
&serialPath,
|
||||
KEY_ALL_ACCESS,
|
||||
REG_OPTION_NON_VOLATILE,
|
||||
NULL,
|
||||
WDF_NO_OBJECT_ATTRIBUTES,
|
||||
&keySerial
|
||||
);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_DS4,
|
||||
"WdfRegistryCreateKey failed with status %!STATUS!",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
||||
RtlUnicodeStringInit(&valueName, L"TargetMacAddress");
|
||||
|
||||
status = WdfRegistryQueryValue(keySerial, &valueName, sizeof(MAC_ADDRESS), &ds4->TargetMacAddress, NULL, NULL);
|
||||
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION,
|
||||
TRACE_DS4,
|
||||
"MAC-Address: %02X:%02X:%02X:%02X:%02X:%02X\n",
|
||||
ds4->TargetMacAddress.Vendor0,
|
||||
ds4->TargetMacAddress.Vendor1,
|
||||
ds4->TargetMacAddress.Vendor2,
|
||||
ds4->TargetMacAddress.Nic0,
|
||||
ds4->TargetMacAddress.Nic1,
|
||||
ds4->TargetMacAddress.Nic2);
|
||||
|
||||
if (status == STATUS_OBJECT_NAME_NOT_FOUND)
|
||||
{
|
||||
GenerateRandomMacAddress(&ds4->TargetMacAddress);
|
||||
|
||||
status = WdfRegistryAssignValue(keySerial, &valueName, REG_BINARY, sizeof(MAC_ADDRESS), (PVOID)&ds4->TargetMacAddress);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_DS4,
|
||||
"WdfRegistryAssignValue failed with status %!STATUS!",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
else if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_DS4,
|
||||
"WdfRegistryQueryValue failed with status %!STATUS!",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
||||
WdfRegistryClose(keySerial);
|
||||
WdfRegistryClose(keyDS);
|
||||
WdfRegistryClose(keyTargets);
|
||||
WdfRegistryClose(keyParams);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
VOID Ds4_GetConfigurationDescriptorType(PUCHAR Buffer, ULONG Length)
|
||||
{
|
||||
UCHAR Ds4DescriptorData[DS4_DESCRIPTOR_SIZE] =
|
||||
{
|
||||
0x09, // bLength
|
||||
0x02, // bDescriptorType (Configuration)
|
||||
0x29, 0x00, // wTotalLength 41
|
||||
0x01, // bNumInterfaces 1
|
||||
0x01, // bConfigurationValue
|
||||
0x00, // iConfiguration (String Index)
|
||||
0xC0, // bmAttributes Self Powered
|
||||
0xFA, // bMaxPower 500mA
|
||||
|
||||
0x09, // bLength
|
||||
0x04, // bDescriptorType (Interface)
|
||||
0x00, // bInterfaceNumber 0
|
||||
0x00, // bAlternateSetting
|
||||
0x02, // bNumEndpoints 2
|
||||
0x03, // bInterfaceClass
|
||||
0x00, // bInterfaceSubClass
|
||||
0x00, // bInterfaceProtocol
|
||||
0x00, // iInterface (String Index)
|
||||
|
||||
0x09, // bLength
|
||||
0x21, // bDescriptorType (HID)
|
||||
0x11, 0x01, // bcdHID 1.11
|
||||
0x00, // bCountryCode
|
||||
0x01, // bNumDescriptors
|
||||
0x22, // bDescriptorType[0] (HID)
|
||||
0xD3, 0x01, // wDescriptorLength[0] 467
|
||||
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x84, // bEndpointAddress (IN/D2H)
|
||||
0x03, // bmAttributes (Interrupt)
|
||||
0x40, 0x00, // wMaxPacketSize 64
|
||||
0x05, // bInterval 5 (unit depends on device speed)
|
||||
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x03, // bEndpointAddress (OUT/H2D)
|
||||
0x03, // bmAttributes (Interrupt)
|
||||
0x40, 0x00, // wMaxPacketSize 64
|
||||
0x05, // bInterval 5 (unit depends on device speed)
|
||||
|
||||
// 41 bytes
|
||||
|
||||
// best guess: USB Standard Descriptor
|
||||
};
|
||||
|
||||
RtlCopyBytes(Buffer, Ds4DescriptorData, Length);
|
||||
}
|
||||
|
||||
VOID Ds4_GetDeviceDescriptorType(PUSB_DEVICE_DESCRIPTOR pDescriptor, PPDO_DEVICE_DATA pCommon)
|
||||
{
|
||||
pDescriptor->bLength = 0x12;
|
||||
pDescriptor->bDescriptorType = USB_DEVICE_DESCRIPTOR_TYPE;
|
||||
pDescriptor->bcdUSB = 0x0200; // USB v2.0
|
||||
pDescriptor->bDeviceClass = 0x00; // per Interface
|
||||
pDescriptor->bDeviceSubClass = 0x00;
|
||||
pDescriptor->bDeviceProtocol = 0x00;
|
||||
pDescriptor->bMaxPacketSize0 = 0x40;
|
||||
pDescriptor->idVendor = pCommon->VendorId;
|
||||
pDescriptor->idProduct = pCommon->ProductId;
|
||||
pDescriptor->bcdDevice = 0x0100;
|
||||
pDescriptor->iManufacturer = 0x01;
|
||||
pDescriptor->iProduct = 0x02;
|
||||
pDescriptor->iSerialNumber = 0x00;
|
||||
pDescriptor->bNumConfigurations = 0x01;
|
||||
}
|
||||
|
||||
VOID Ds4_SelectConfiguration(PUSBD_INTERFACE_INFORMATION pInfo)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_DS4,
|
||||
">> >> >> URB_FUNCTION_SELECT_CONFIGURATION: Length %d, Interface %d, Alternate %d, Pipes %d",
|
||||
(int)pInfo->Length,
|
||||
(int)pInfo->InterfaceNumber,
|
||||
(int)pInfo->AlternateSetting,
|
||||
pInfo->NumberOfPipes);
|
||||
|
||||
pInfo->Class = 0x03; // HID
|
||||
pInfo->SubClass = 0x00;
|
||||
pInfo->Protocol = 0x00;
|
||||
|
||||
pInfo->InterfaceHandle = (USBD_INTERFACE_HANDLE)0xFFFF0000;
|
||||
|
||||
pInfo->Pipes[0].MaximumTransferSize = 0x00400000;
|
||||
pInfo->Pipes[0].MaximumPacketSize = 0x40;
|
||||
pInfo->Pipes[0].EndpointAddress = 0x84;
|
||||
pInfo->Pipes[0].Interval = 0x05;
|
||||
pInfo->Pipes[0].PipeType = 0x03;
|
||||
pInfo->Pipes[0].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0084;
|
||||
pInfo->Pipes[0].PipeFlags = 0x00;
|
||||
|
||||
pInfo->Pipes[1].MaximumTransferSize = 0x00400000;
|
||||
pInfo->Pipes[1].MaximumPacketSize = 0x40;
|
||||
pInfo->Pipes[1].EndpointAddress = 0x03;
|
||||
pInfo->Pipes[1].Interval = 0x05;
|
||||
pInfo->Pipes[1].PipeType = 0x03;
|
||||
pInfo->Pipes[1].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0003;
|
||||
pInfo->Pipes[1].PipeFlags = 0x00;
|
||||
}
|
||||
|
||||
//
|
||||
// Completes pending I/O requests if feeder is too slow.
|
||||
//
|
||||
VOID Ds4_PendingUsbRequestsTimerFunc(
|
||||
_In_ WDFTIMER Timer
|
||||
)
|
||||
{
|
||||
NTSTATUS status;
|
||||
WDFREQUEST usbRequest;
|
||||
WDFDEVICE hChild;
|
||||
PDS4_DEVICE_DATA ds4Data;
|
||||
PIRP pendingIrp;
|
||||
PIO_STACK_LOCATION irpStack;
|
||||
PPDO_DEVICE_DATA pdoData;
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_DS4, "%!FUNC! Entry");
|
||||
|
||||
hChild = WdfTimerGetParentObject(Timer);
|
||||
pdoData = PdoGetData(hChild);
|
||||
ds4Data = Ds4GetData(hChild);
|
||||
|
||||
// Get pending USB request
|
||||
status = WdfIoQueueRetrieveNextRequest(pdoData->PendingUsbInRequests, &usbRequest);
|
||||
|
||||
if (NT_SUCCESS(status))
|
||||
{
|
||||
// Get pending IRP
|
||||
pendingIrp = WdfRequestWdmGetIrp(usbRequest);
|
||||
irpStack = IoGetCurrentIrpStackLocation(pendingIrp);
|
||||
|
||||
// Get USB request block
|
||||
PURB urb = (PURB)irpStack->Parameters.Others.Argument1;
|
||||
|
||||
// Get transfer buffer
|
||||
PUCHAR Buffer = (PUCHAR)urb->UrbBulkOrInterruptTransfer.TransferBuffer;
|
||||
// Set buffer length to report size
|
||||
urb->UrbBulkOrInterruptTransfer.TransferBufferLength = DS4_REPORT_SIZE;
|
||||
|
||||
// Copy cached report to transfer buffer
|
||||
if (Buffer)
|
||||
RtlCopyBytes(Buffer, ds4Data->Report, DS4_REPORT_SIZE);
|
||||
|
||||
// Complete pending request
|
||||
WdfRequestComplete(usbRequest, status);
|
||||
}
|
||||
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DS4, "%!FUNC! Exit with status %!STATUS!", status);
|
||||
}
|
||||
|
||||
114
sys/Ds4.h
114
sys/Ds4.h
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Virtual Gamepad Emulation Framework - Windows kernel-mode bus driver
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2016-2019 Nefarius Software Solutions e.U. and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#define HID_GET_FEATURE_REPORT_SIZE_0 0x31
|
||||
#define HID_GET_FEATURE_REPORT_SIZE_1 0x25
|
||||
#define HID_GET_FEATURE_REPORT_MAC_ADDRESSES_SIZE 0x10
|
||||
|
||||
#define HID_SET_FEATURE_REPORT_SIZE_0 0x17
|
||||
#define HID_SET_FEATURE_REPORT_SIZE_1 0x11
|
||||
|
||||
#define HID_REPORT_ID_0 0xA3
|
||||
#define HID_REPORT_ID_1 0x02
|
||||
#define HID_REPORT_MAC_ADDRESSES_ID 0x12
|
||||
#define HID_REPORT_ID_3 0x13
|
||||
#define HID_REPORT_ID_4 0x14
|
||||
|
||||
#define DS4_DESCRIPTOR_SIZE 0x0029
|
||||
#if defined(_X86_)
|
||||
#define DS4_CONFIGURATION_SIZE 0x0050
|
||||
#else
|
||||
#define DS4_CONFIGURATION_SIZE 0x0070
|
||||
#endif
|
||||
#define DS4_HID_REPORT_DESCRIPTOR_SIZE 0x01D3
|
||||
|
||||
#define DS4_MANUFACTURER_NAME_LENGTH 0x38
|
||||
#define DS4_PRODUCT_NAME_LENGTH 0x28
|
||||
#define DS4_OUTPUT_BUFFER_OFFSET 0x04
|
||||
#define DS4_OUTPUT_BUFFER_LENGTH 0x05
|
||||
|
||||
#define DS4_REPORT_SIZE 0x40
|
||||
#define DS4_QUEUE_FLUSH_PERIOD 0x05
|
||||
|
||||
|
||||
//
|
||||
// DS4-specific device context data.
|
||||
//
|
||||
typedef struct _DS4_DEVICE_DATA
|
||||
{
|
||||
//
|
||||
// HID Input Report buffer
|
||||
//
|
||||
UCHAR Report[DS4_REPORT_SIZE];
|
||||
|
||||
//
|
||||
// Output report cache
|
||||
//
|
||||
DS4_OUTPUT_REPORT OutputReport;
|
||||
|
||||
//
|
||||
// Timer for dispatching interrupt transfer
|
||||
//
|
||||
WDFTIMER PendingUsbInRequestsTimer;
|
||||
|
||||
//
|
||||
// Auto-generated MAC address of the target device
|
||||
//
|
||||
MAC_ADDRESS TargetMacAddress;
|
||||
|
||||
//
|
||||
// Default MAC address of the host (not used)
|
||||
//
|
||||
MAC_ADDRESS HostMacAddress;
|
||||
|
||||
} DS4_DEVICE_DATA, *PDS4_DEVICE_DATA;
|
||||
|
||||
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DS4_DEVICE_DATA, Ds4GetData)
|
||||
|
||||
|
||||
EVT_WDF_TIMER Ds4_PendingUsbRequestsTimerFunc;
|
||||
|
||||
NTSTATUS
|
||||
Bus_Ds4SubmitReport(
|
||||
WDFDEVICE Device,
|
||||
ULONG SerialNo,
|
||||
PDS4_SUBMIT_REPORT Report,
|
||||
_In_ BOOLEAN FromInterface
|
||||
);
|
||||
|
||||
//
|
||||
// DS4-specific functions
|
||||
//
|
||||
NTSTATUS Ds4_PreparePdo(PWDFDEVICE_INIT DeviceInit, PUNICODE_STRING DeviceId, PUNICODE_STRING DeviceDescription);
|
||||
NTSTATUS Ds4_PrepareHardware(WDFDEVICE Device);
|
||||
NTSTATUS Ds4_AssignPdoContext(WDFDEVICE Device, PPDO_IDENTIFICATION_DESCRIPTION Description);
|
||||
VOID Ds4_GetConfigurationDescriptorType(PUCHAR Buffer, ULONG Length);
|
||||
VOID Ds4_GetDeviceDescriptorType(PUSB_DEVICE_DESCRIPTOR pDescriptor, PPDO_DEVICE_DATA pCommon);
|
||||
VOID Ds4_SelectConfiguration(PUSBD_INTERFACE_INFORMATION pInfo);
|
||||
|
||||
@@ -9,10 +9,12 @@
|
||||
|
||||
PCWSTR ViGEm::Bus::Targets::EmulationTargetDS4::_deviceDescription = L"Virtual DualShock 4 Controller";
|
||||
|
||||
ViGEm::Bus::Targets::EmulationTargetDS4::EmulationTargetDS4() : EmulationTargetPDO(0x054C, 0x05C4)
|
||||
ViGEm::Bus::Targets::EmulationTargetDS4::EmulationTargetDS4(ULONG Serial, LONG SessionId, USHORT VendorId,
|
||||
USHORT ProductId) : EmulationTargetPDO(
|
||||
Serial, SessionId, VendorId, ProductId)
|
||||
{
|
||||
TargetType = DualShock4Wired;
|
||||
UsbConfigurationDescriptionSize = DS4_DESCRIPTOR_SIZE;
|
||||
_TargetType = DualShock4Wired;
|
||||
_UsbConfigurationDescriptionSize = DS4_DESCRIPTOR_SIZE;
|
||||
}
|
||||
|
||||
NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::PdoPrepareDevice(PWDFDEVICE_INIT DeviceInit,
|
||||
@@ -34,7 +36,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::PdoPrepareDevice(PWDFDEVICE_IN
|
||||
|
||||
// Set hardware IDs
|
||||
RtlUnicodeStringPrintf(&buffer, L"USB\\VID_%04X&PID_%04X&REV_0100",
|
||||
this->VendorId, this->ProductId);
|
||||
this->_VendorId, this->_ProductId);
|
||||
|
||||
status = WdfPdoInitAddHardwareID(DeviceInit, &buffer);
|
||||
if (!NT_SUCCESS(status))
|
||||
@@ -49,7 +51,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::PdoPrepareDevice(PWDFDEVICE_IN
|
||||
RtlUnicodeStringCopy(DeviceId, &buffer);
|
||||
|
||||
RtlUnicodeStringPrintf(&buffer, L"USB\\VID_%04X&PID_%04X",
|
||||
this->VendorId, this->ProductId);
|
||||
this->_VendorId, this->_ProductId);
|
||||
|
||||
status = WdfPdoInitAddHardwareID(DeviceInit, &buffer);
|
||||
if (!NT_SUCCESS(status))
|
||||
@@ -108,7 +110,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::PdoPrepareHardware()
|
||||
|
||||
devinterfaceHid.Size = sizeof(INTERFACE);
|
||||
devinterfaceHid.Version = 1;
|
||||
devinterfaceHid.Context = static_cast<PVOID>(this->PdoDevice);
|
||||
devinterfaceHid.Context = static_cast<PVOID>(this->_PdoDevice);
|
||||
|
||||
devinterfaceHid.InterfaceReference = WdfDeviceInterfaceReferenceNoOp;
|
||||
devinterfaceHid.InterfaceDereference = WdfDeviceInterfaceDereferenceNoOp;
|
||||
@@ -121,7 +123,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::PdoPrepareHardware()
|
||||
NULL
|
||||
);
|
||||
|
||||
NTSTATUS status = WdfDeviceAddQueryInterface(this->PdoDevice, &ifaceCfg);
|
||||
NTSTATUS status = WdfDeviceAddQueryInterface(this->_PdoDevice, &ifaceCfg);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
@@ -171,7 +173,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::PdoInitContext()
|
||||
WDF_OBJECT_ATTRIBUTES_INIT(&timerAttribs);
|
||||
|
||||
// PDO is parent
|
||||
timerAttribs.ParentObject = this->PdoDevice;
|
||||
timerAttribs.ParentObject = this->_PdoDevice;
|
||||
|
||||
// Create timer
|
||||
status = WdfTimerCreate(
|
||||
@@ -251,7 +253,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::PdoInitContext()
|
||||
}
|
||||
|
||||
DECLARE_UNICODE_STRING_SIZE(serialPath, 4);
|
||||
RtlUnicodeStringPrintf(&serialPath, L"%04d", this->SerialNo);
|
||||
RtlUnicodeStringPrintf(&serialPath, L"%04d", this->_SerialNo);
|
||||
|
||||
status = WdfRegistryCreateKey(
|
||||
keyDS,
|
||||
@@ -391,8 +393,8 @@ VOID ViGEm::Bus::Targets::EmulationTargetDS4::UsbGetDeviceDescriptorType(PUSB_DE
|
||||
pDescriptor->bDeviceSubClass = 0x00;
|
||||
pDescriptor->bDeviceProtocol = 0x00;
|
||||
pDescriptor->bMaxPacketSize0 = 0x40;
|
||||
pDescriptor->idVendor = this->VendorId;
|
||||
pDescriptor->idProduct = this->ProductId;
|
||||
pDescriptor->idVendor = this->_VendorId;
|
||||
pDescriptor->idProduct = this->_ProductId;
|
||||
pDescriptor->bcdDevice = 0x0100;
|
||||
pDescriptor->iManufacturer = 0x01;
|
||||
pDescriptor->iProduct = 0x02;
|
||||
@@ -872,7 +874,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::UsbGetDescriptorFromInterface(
|
||||
//
|
||||
// Notify client library that PDO is ready
|
||||
//
|
||||
KeSetEvent(&this->PdoBootNotificationEvent, 0, FALSE);
|
||||
KeSetEvent(&this->_PdoBootNotificationEvent, 0, FALSE);
|
||||
}
|
||||
|
||||
return status;
|
||||
@@ -990,7 +992,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::UsbBulkOrInterruptTransfer(_UR
|
||||
/* This request is sent periodically and relies on data the "feeder"
|
||||
has to supply, so we queue this request and return with STATUS_PENDING.
|
||||
The request gets completed as soon as the "feeder" sent an update. */
|
||||
status = WdfRequestForwardToIoQueue(Request, this->PendingUsbInRequests);
|
||||
status = WdfRequestForwardToIoQueue(Request, this->_PendingUsbInRequests);
|
||||
|
||||
return (NT_SUCCESS(status)) ? STATUS_PENDING : status;
|
||||
}
|
||||
@@ -1001,7 +1003,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::UsbBulkOrInterruptTransfer(_UR
|
||||
DS4_OUTPUT_BUFFER_LENGTH);
|
||||
|
||||
// Notify user-mode process that new data is available
|
||||
status = WdfIoQueueRetrieveNextRequest(this->PendingNotificationRequests, ¬ifyRequest);
|
||||
status = WdfIoQueueRetrieveNextRequest(this->_PendingNotificationRequests, ¬ifyRequest);
|
||||
|
||||
if (NT_SUCCESS(status))
|
||||
{
|
||||
@@ -1018,7 +1020,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::UsbBulkOrInterruptTransfer(_UR
|
||||
{
|
||||
// Assign values to output buffer
|
||||
notify->Size = sizeof(DS4_REQUEST_NOTIFICATION);
|
||||
notify->SerialNo = this->SerialNo;
|
||||
notify->SerialNo = this->_SerialNo;
|
||||
notify->Report = this->OutputReport;
|
||||
|
||||
WdfRequestCompleteWithInformation(notifyRequest, status, notify->Size);
|
||||
@@ -1063,6 +1065,40 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::UsbControlTransfer(PURB Urb)
|
||||
return status;
|
||||
}
|
||||
|
||||
NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::SubmitReport(PVOID NewReport)
|
||||
{
|
||||
NTSTATUS status;
|
||||
WDFREQUEST usbRequest;
|
||||
|
||||
status = WdfIoQueueRetrieveNextRequest(this->_PendingUsbInRequests, &usbRequest);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
return status;
|
||||
|
||||
// Get pending IRP
|
||||
PIRP pendingIrp = WdfRequestWdmGetIrp(usbRequest);
|
||||
|
||||
// Get USB request block
|
||||
PURB urb = static_cast<PURB>(URB_FROM_IRP(pendingIrp));
|
||||
|
||||
// Get transfer buffer
|
||||
auto Buffer = static_cast<PUCHAR>(urb->UrbBulkOrInterruptTransfer.TransferBuffer);
|
||||
|
||||
urb->UrbBulkOrInterruptTransfer.TransferBufferLength = DS4_REPORT_SIZE;
|
||||
|
||||
/* Copy report to cache and transfer buffer
|
||||
* Skip first byte as it contains the never changing report id */
|
||||
RtlCopyBytes(this->Report + 1, &(static_cast<PDS4_SUBMIT_REPORT>(NewReport))->Report, sizeof(DS4_REPORT));
|
||||
|
||||
if (Buffer)
|
||||
RtlCopyBytes(Buffer, this->Report, DS4_REPORT_SIZE);
|
||||
|
||||
// Complete pending request
|
||||
WdfRequestComplete(usbRequest, status);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
VOID ViGEm::Bus::Targets::EmulationTargetDS4::PendingUsbRequestsTimerFunc(
|
||||
_In_ WDFTIMER Timer
|
||||
)
|
||||
@@ -1076,7 +1112,7 @@ VOID ViGEm::Bus::Targets::EmulationTargetDS4::PendingUsbRequestsTimerFunc(
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_DS4, "%!FUNC! Entry");
|
||||
|
||||
// Get pending USB request
|
||||
NTSTATUS status = WdfIoQueueRetrieveNextRequest(ctx->PendingUsbInRequests, &usbRequest);
|
||||
NTSTATUS status = WdfIoQueueRetrieveNextRequest(ctx->_PendingUsbInRequests, &usbRequest);
|
||||
|
||||
if (NT_SUCCESS(status))
|
||||
{
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace ViGEm::Bus::Targets
|
||||
class EmulationTargetDS4 : public Core::EmulationTargetPDO
|
||||
{
|
||||
public:
|
||||
EmulationTargetDS4();
|
||||
EmulationTargetDS4(ULONG Serial, LONG SessionId, USHORT VendorId = 0x054C, USHORT ProductId = 0x05C4);
|
||||
~EmulationTargetDS4() = default;
|
||||
|
||||
NTSTATUS PdoPrepareDevice(PWDFDEVICE_INIT DeviceInit,
|
||||
@@ -45,6 +45,7 @@ namespace ViGEm::Bus::Targets
|
||||
NTSTATUS UsbGetStringDescriptorType(PURB Urb) override;
|
||||
NTSTATUS UsbBulkOrInterruptTransfer(_URB_BULK_OR_INTERRUPT_TRANSFER* pTransfer, WDFREQUEST Request) override;
|
||||
NTSTATUS UsbControlTransfer(PURB Urb) override;
|
||||
NTSTATUS SubmitReport(PVOID NewReport) override;
|
||||
private:
|
||||
static PCWSTR _deviceDescription;
|
||||
|
||||
|
||||
@@ -10,28 +10,27 @@
|
||||
|
||||
PCWSTR ViGEm::Bus::Core::EmulationTargetPDO::_deviceLocation = L"Virtual Gamepad Emulation Bus";
|
||||
|
||||
NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE Device, PWDFDEVICE_INIT DeviceInit, PPDO_IDENTIFICATION_DESCRIPTION Description)
|
||||
NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE ParentDevice, PWDFDEVICE_INIT DeviceInit)
|
||||
{
|
||||
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
||||
WDF_DEVICE_PNP_CAPABILITIES pnpCaps;
|
||||
WDF_DEVICE_POWER_CAPABILITIES powerCaps;
|
||||
WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;
|
||||
WDF_OBJECT_ATTRIBUTES pdoAttributes;
|
||||
WDF_IO_QUEUE_CONFIG defaultPdoQueueConfig;
|
||||
WDFQUEUE defaultPdoQueue;
|
||||
UNICODE_STRING deviceDescription;
|
||||
WDF_OBJECT_ATTRIBUTES attributes;
|
||||
WDF_IO_QUEUE_CONFIG usbInQueueConfig;
|
||||
WDF_IO_QUEUE_CONFIG notificationsQueueConfig;
|
||||
PEMULATION_TARGET_PDO_CONTEXT pPdoContext;
|
||||
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
||||
WDF_DEVICE_PNP_CAPABILITIES pnpCaps;
|
||||
WDF_DEVICE_POWER_CAPABILITIES powerCaps;
|
||||
WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;
|
||||
WDF_OBJECT_ATTRIBUTES pdoAttributes;
|
||||
WDF_IO_QUEUE_CONFIG defaultPdoQueueConfig;
|
||||
WDFQUEUE defaultPdoQueue;
|
||||
UNICODE_STRING deviceDescription;
|
||||
WDF_OBJECT_ATTRIBUTES attributes;
|
||||
WDF_IO_QUEUE_CONFIG usbInQueueConfig;
|
||||
WDF_IO_QUEUE_CONFIG notificationsQueueConfig;
|
||||
PEMULATION_TARGET_PDO_CONTEXT pPdoContext;
|
||||
|
||||
DECLARE_CONST_UNICODE_STRING(deviceLocation, L"Virtual Gamepad Emulation Bus");
|
||||
DECLARE_UNICODE_STRING_SIZE(buffer, MAX_INSTANCE_ID_LEN);
|
||||
// reserve space for device id
|
||||
DECLARE_UNICODE_STRING_SIZE(deviceId, MAX_INSTANCE_ID_LEN);
|
||||
|
||||
UNREFERENCED_PARAMETER(Description);
|
||||
UNREFERENCED_PARAMETER(Device);
|
||||
UNREFERENCED_PARAMETER(ParentDevice);
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
@@ -48,9 +47,9 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE Device,
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSPDO,
|
||||
"WdfPdoInitAssignRawDevice failed with status %!STATUS!",
|
||||
status);
|
||||
TRACE_BUSPDO,
|
||||
"WdfPdoInitAssignRawDevice failed with status %!STATUS!",
|
||||
status);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -60,9 +59,9 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE Device,
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSPDO,
|
||||
"WdfDeviceInitAssignSDDLString failed with status %!STATUS!",
|
||||
status);
|
||||
TRACE_BUSPDO,
|
||||
"WdfDeviceInitAssignSDDLString failed with status %!STATUS!",
|
||||
status);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -80,20 +79,20 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE Device,
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSPDO,
|
||||
"WdfPdoInitAssignDeviceID failed with status %!STATUS!",
|
||||
status);
|
||||
TRACE_BUSPDO,
|
||||
"WdfPdoInitAssignDeviceID failed with status %!STATUS!",
|
||||
status);
|
||||
break;
|
||||
}
|
||||
|
||||
// prepare instance id
|
||||
status = RtlUnicodeStringPrintf(&buffer, L"%02d", this->SerialNo);
|
||||
status = RtlUnicodeStringPrintf(&buffer, L"%02d", this->_SerialNo);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSPDO,
|
||||
"RtlUnicodeStringPrintf failed with status %!STATUS!",
|
||||
status);
|
||||
TRACE_BUSPDO,
|
||||
"RtlUnicodeStringPrintf failed with status %!STATUS!",
|
||||
status);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -102,9 +101,9 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE Device,
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSPDO,
|
||||
"WdfPdoInitAssignInstanceID failed with status %!STATUS!",
|
||||
status);
|
||||
TRACE_BUSPDO,
|
||||
"WdfPdoInitAssignInstanceID failed with status %!STATUS!",
|
||||
status);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -113,9 +112,9 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE Device,
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSPDO,
|
||||
"WdfPdoInitAddDeviceText failed with status %!STATUS!",
|
||||
status);
|
||||
TRACE_BUSPDO,
|
||||
"WdfPdoInitAddDeviceText failed with status %!STATUS!",
|
||||
status);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -143,36 +142,36 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE Device,
|
||||
// Add common device data context
|
||||
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&pdoAttributes, EMULATION_TARGET_PDO_CONTEXT);
|
||||
|
||||
status = WdfDeviceCreate(&DeviceInit, &pdoAttributes, &this->PdoDevice);
|
||||
status = WdfDeviceCreate(&DeviceInit, &pdoAttributes, &this->_PdoDevice);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSPDO,
|
||||
"WdfDeviceCreate failed with status %!STATUS!",
|
||||
status);
|
||||
TRACE_BUSPDO,
|
||||
"WdfDeviceCreate failed with status %!STATUS!",
|
||||
status);
|
||||
break;
|
||||
}
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
"Created PDO 0x%p",
|
||||
this->PdoDevice);
|
||||
TRACE_BUSPDO,
|
||||
"Created PDO 0x%p",
|
||||
this->_PdoDevice);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Expose USB Interface
|
||||
|
||||
status = WdfDeviceCreateDeviceInterface(
|
||||
Device,
|
||||
ParentDevice,
|
||||
const_cast<LPGUID>(&GUID_DEVINTERFACE_USB_DEVICE),
|
||||
NULL
|
||||
nullptr
|
||||
);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSPDO,
|
||||
"WdfDeviceCreateDeviceInterface failed with status %!STATUS!",
|
||||
status);
|
||||
TRACE_BUSPDO,
|
||||
"WdfDeviceCreateDeviceInterface failed with status %!STATUS!",
|
||||
status);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -183,17 +182,17 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE Device,
|
||||
//
|
||||
// Bind this object and device context together
|
||||
//
|
||||
pPdoContext = EmulationTargetPdoGetContext(this->PdoDevice);
|
||||
pPdoContext = EmulationTargetPdoGetContext(this->_PdoDevice);
|
||||
pPdoContext->Target = this;
|
||||
|
||||
|
||||
status = this->PdoInitContext();
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSPDO,
|
||||
"Couldn't initialize additional contexts: %!STATUS!",
|
||||
status);
|
||||
TRACE_BUSPDO,
|
||||
"Couldn't initialize additional contexts: %!STATUS!",
|
||||
status);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -202,23 +201,23 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE Device,
|
||||
#pragma region Create Queues & Locks
|
||||
|
||||
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
|
||||
attributes.ParentObject = this->PdoDevice;
|
||||
attributes.ParentObject = this->_PdoDevice;
|
||||
|
||||
// Create and assign queue for incoming interrupt transfer
|
||||
WDF_IO_QUEUE_CONFIG_INIT(&usbInQueueConfig, WdfIoQueueDispatchManual);
|
||||
|
||||
status = WdfIoQueueCreate(
|
||||
this->PdoDevice,
|
||||
this->_PdoDevice,
|
||||
&usbInQueueConfig,
|
||||
WDF_NO_OBJECT_ATTRIBUTES,
|
||||
&this->PendingUsbInRequests
|
||||
&this->_PendingUsbInRequests
|
||||
);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSPDO,
|
||||
"WdfIoQueueCreate (PendingUsbInRequests) failed with status %!STATUS!",
|
||||
status);
|
||||
TRACE_BUSPDO,
|
||||
"WdfIoQueueCreate (PendingUsbInRequests) failed with status %!STATUS!",
|
||||
status);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -226,21 +225,21 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE Device,
|
||||
WDF_IO_QUEUE_CONFIG_INIT(¬ificationsQueueConfig, WdfIoQueueDispatchManual);
|
||||
|
||||
status = WdfIoQueueCreate(
|
||||
Device,
|
||||
ParentDevice,
|
||||
¬ificationsQueueConfig,
|
||||
WDF_NO_OBJECT_ATTRIBUTES,
|
||||
&this->PendingNotificationRequests
|
||||
&this->_PendingNotificationRequests
|
||||
);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSPDO,
|
||||
"WdfIoQueueCreate (PendingNotificationRequests) failed with status %!STATUS!",
|
||||
status);
|
||||
TRACE_BUSPDO,
|
||||
"WdfIoQueueCreate (PendingNotificationRequests) failed with status %!STATUS!",
|
||||
status);
|
||||
break;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Default I/O queue setup
|
||||
|
||||
@@ -249,7 +248,7 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE Device,
|
||||
defaultPdoQueueConfig.EvtIoInternalDeviceControl = EvtIoInternalDeviceControl;
|
||||
|
||||
status = WdfIoQueueCreate(
|
||||
this->PdoDevice,
|
||||
this->_PdoDevice,
|
||||
&defaultPdoQueueConfig,
|
||||
WDF_NO_OBJECT_ATTRIBUTES,
|
||||
&defaultPdoQueue
|
||||
@@ -257,9 +256,9 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE Device,
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSPDO,
|
||||
"WdfIoQueueCreate (Default) failed with status %!STATUS!",
|
||||
status);
|
||||
TRACE_BUSPDO,
|
||||
"WdfIoQueueCreate (Default) failed with status %!STATUS!",
|
||||
status);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -273,10 +272,10 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE Device,
|
||||
pnpCaps.EjectSupported = WdfTrue;
|
||||
pnpCaps.SurpriseRemovalOK = WdfTrue;
|
||||
|
||||
pnpCaps.Address = this->SerialNo;
|
||||
pnpCaps.UINumber = this->SerialNo;
|
||||
pnpCaps.Address = this->_SerialNo;
|
||||
pnpCaps.UINumber = this->_SerialNo;
|
||||
|
||||
WdfDeviceSetPnpCapabilities(this->PdoDevice, &pnpCaps);
|
||||
WdfDeviceSetPnpCapabilities(this->_PdoDevice, &pnpCaps);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
@@ -295,11 +294,11 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE Device,
|
||||
powerCaps.DeviceState[PowerSystemHibernate] = PowerDeviceD3;
|
||||
powerCaps.DeviceState[PowerSystemShutdown] = PowerDeviceD3;
|
||||
|
||||
WdfDeviceSetPowerCapabilities(this->PdoDevice, &powerCaps);
|
||||
WdfDeviceSetPowerCapabilities(this->_PdoDevice, &powerCaps);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} while (FALSE);
|
||||
}
|
||||
while (FALSE);
|
||||
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSPDO, "%!FUNC! Exit with status %!STATUS!", status);
|
||||
|
||||
@@ -308,12 +307,29 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE Device,
|
||||
|
||||
VOID ViGEm::Bus::Core::EmulationTargetPDO::SetSerial(ULONG Serial)
|
||||
{
|
||||
this->SerialNo = Serial;
|
||||
this->_SerialNo = Serial;
|
||||
}
|
||||
|
||||
ULONG ViGEm::Bus::Core::EmulationTargetPDO::GetSerial() const
|
||||
{
|
||||
return this->SerialNo;
|
||||
return this->_SerialNo;
|
||||
}
|
||||
|
||||
NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::EnqueueNotification(WDFREQUEST Request) const
|
||||
{
|
||||
return (this->IsOwnerProcess())
|
||||
? WdfRequestForwardToIoQueue(Request, this->_PendingNotificationRequests)
|
||||
: STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
bool ViGEm::Bus::Core::EmulationTargetPDO::IsOwnerProcess() const
|
||||
{
|
||||
return this->_OwnerProcessId == current_process_id();
|
||||
}
|
||||
|
||||
unsigned long ViGEm::Bus::Core::EmulationTargetPDO::current_process_id()
|
||||
{
|
||||
return static_cast<DWORD>((DWORD_PTR)PsGetCurrentProcessId() & 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
#pragma region USB Interface Functions
|
||||
@@ -325,7 +341,8 @@ BOOLEAN USB_BUSIFFN ViGEm::Bus::Core::EmulationTargetPDO::UsbInterfaceIsDeviceHi
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
NTSTATUS USB_BUSIFFN ViGEm::Bus::Core::EmulationTargetPDO::UsbInterfaceQueryBusInformation(IN PVOID BusContext, IN ULONG Level,
|
||||
NTSTATUS USB_BUSIFFN ViGEm::Bus::Core::EmulationTargetPDO::UsbInterfaceQueryBusInformation(
|
||||
IN PVOID BusContext, IN ULONG Level,
|
||||
IN OUT PVOID BusInformationBuffer,
|
||||
IN OUT PULONG BusInformationBufferLength,
|
||||
OUT PULONG BusInformationActualLength)
|
||||
@@ -347,7 +364,8 @@ NTSTATUS USB_BUSIFFN ViGEm::Bus::Core::EmulationTargetPDO::UsbInterfaceSubmitIso
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
NTSTATUS USB_BUSIFFN ViGEm::Bus::Core::EmulationTargetPDO::UsbInterfaceQueryBusTime(IN PVOID BusContext, IN OUT PULONG CurrentUsbFrame)
|
||||
NTSTATUS USB_BUSIFFN ViGEm::Bus::Core::EmulationTargetPDO::UsbInterfaceQueryBusTime(
|
||||
IN PVOID BusContext, IN OUT PULONG CurrentUsbFrame)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(BusContext);
|
||||
UNREFERENCED_PARAMETER(CurrentUsbFrame);
|
||||
@@ -356,8 +374,9 @@ NTSTATUS USB_BUSIFFN ViGEm::Bus::Core::EmulationTargetPDO::UsbInterfaceQueryBusT
|
||||
}
|
||||
|
||||
VOID USB_BUSIFFN ViGEm::Bus::Core::EmulationTargetPDO::UsbInterfaceGetUSBDIVersion(IN PVOID BusContext,
|
||||
IN OUT PUSBD_VERSION_INFORMATION VersionInformation,
|
||||
IN OUT PULONG HcdCapabilities)
|
||||
IN OUT PUSBD_VERSION_INFORMATION
|
||||
VersionInformation,
|
||||
IN OUT PULONG HcdCapabilities)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(BusContext);
|
||||
|
||||
@@ -380,13 +399,13 @@ void ViGEm::Bus::Core::EmulationTargetPDO::UsbAbortPipe()
|
||||
this->AbortPipe();
|
||||
|
||||
// Higher driver shutting down, emptying PDOs queues
|
||||
WdfIoQueuePurge(this->PendingUsbInRequests, NULL, NULL);
|
||||
WdfIoQueuePurge(this->PendingNotificationRequests, NULL, NULL);
|
||||
WdfIoQueuePurge(this->_PendingUsbInRequests, nullptr, nullptr);
|
||||
WdfIoQueuePurge(this->_PendingNotificationRequests, nullptr, nullptr);
|
||||
}
|
||||
|
||||
NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::UsbGetConfigurationDescriptorType(PURB Urb)
|
||||
{
|
||||
const PUCHAR Buffer = (PUCHAR)Urb->UrbControlDescriptorRequest.TransferBuffer;
|
||||
const PUCHAR Buffer = static_cast<PUCHAR>(Urb->UrbControlDescriptorRequest.TransferBuffer);
|
||||
|
||||
// First request just gets required buffer size back
|
||||
if (Urb->UrbControlDescriptorRequest.TransferBufferLength == sizeof(USB_CONFIGURATION_DESCRIPTOR))
|
||||
@@ -400,9 +419,9 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::UsbGetConfigurationDescriptorType
|
||||
const ULONG length = Urb->UrbControlDescriptorRequest.TransferBufferLength;
|
||||
|
||||
// Second request can store the whole descriptor
|
||||
if (length >= UsbConfigurationDescriptionSize)
|
||||
if (length >= _UsbConfigurationDescriptionSize)
|
||||
{
|
||||
this->GetConfigurationDescriptorType(Buffer, UsbConfigurationDescriptionSize);
|
||||
this->GetConfigurationDescriptorType(Buffer, _UsbConfigurationDescriptionSize);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -412,24 +431,52 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::UsbGetConfigurationDescriptorType
|
||||
NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::UsbSelectConfiguration(PURB Urb)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_USBPDO,
|
||||
">> >> >> URB_FUNCTION_SELECT_CONFIGURATION: TotalLength %d",
|
||||
Urb->UrbHeader.Length);
|
||||
TRACE_USBPDO,
|
||||
">> >> >> URB_FUNCTION_SELECT_CONFIGURATION: TotalLength %d",
|
||||
Urb->UrbHeader.Length);
|
||||
|
||||
if (Urb->UrbHeader.Length == sizeof(struct _URB_SELECT_CONFIGURATION))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_USBPDO,
|
||||
">> >> >> URB_FUNCTION_SELECT_CONFIGURATION: NULL ConfigurationDescriptor");
|
||||
TRACE_USBPDO,
|
||||
">> >> >> URB_FUNCTION_SELECT_CONFIGURATION: NULL ConfigurationDescriptor");
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
return this->SelectConfiguration(Urb);
|
||||
}
|
||||
|
||||
ViGEm::Bus::Core::EmulationTargetPDO::EmulationTargetPDO(USHORT VID, USHORT PID) : VendorId(VID), ProductId(PID)
|
||||
ViGEm::Bus::Core::EmulationTargetPDO::
|
||||
EmulationTargetPDO(ULONG Serial, LONG SessionId, USHORT VendorId, USHORT ProductId) : _SerialNo(Serial),
|
||||
_SessionId(SessionId),
|
||||
_VendorId(VendorId),
|
||||
_ProductId(ProductId)
|
||||
{
|
||||
KeInitializeEvent(&this->PdoBootNotificationEvent, NotificationEvent, FALSE);
|
||||
this->_OwnerProcessId = current_process_id();
|
||||
KeInitializeEvent(&this->_PdoBootNotificationEvent, NotificationEvent, FALSE);
|
||||
}
|
||||
|
||||
ViGEm::Bus::Core::EmulationTargetPDO* ViGEm::Bus::Core::EmulationTargetPDO::GetPdoBySerial(
|
||||
IN WDFDEVICE ParentDevice, IN ULONG SerialNo)
|
||||
{
|
||||
WDF_CHILD_RETRIEVE_INFO info;
|
||||
|
||||
const WDFCHILDLIST list = WdfFdoGetDefaultChildList(ParentDevice);
|
||||
|
||||
PDO_IDENTIFICATION_DESCRIPTION description;
|
||||
|
||||
WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT(&description.Header, sizeof(description));
|
||||
|
||||
description.SerialNo = SerialNo;
|
||||
|
||||
WDF_CHILD_RETRIEVE_INFO_INIT(&info, &description.Header);
|
||||
|
||||
const WDFDEVICE pdoDevice = WdfChildListRetrievePdo(list, &info);
|
||||
|
||||
if (pdoDevice == nullptr)
|
||||
return nullptr;
|
||||
|
||||
return EmulationTargetPdoGetContext(pdoDevice)->Target;
|
||||
}
|
||||
|
||||
NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::EvtDevicePrepareHardware(
|
||||
@@ -455,14 +502,14 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::EvtIoInternalDeviceControl(
|
||||
)
|
||||
{
|
||||
const auto ctx = EmulationTargetPdoGetContext(WdfIoQueueGetDevice(Queue));
|
||||
|
||||
|
||||
UNREFERENCED_PARAMETER(OutputBufferLength);
|
||||
UNREFERENCED_PARAMETER(InputBufferLength);
|
||||
|
||||
NTSTATUS status = STATUS_INVALID_PARAMETER;
|
||||
PIRP irp;
|
||||
PURB urb;
|
||||
PIO_STACK_LOCATION irpStack;
|
||||
NTSTATUS status = STATUS_INVALID_PARAMETER;
|
||||
PIRP irp;
|
||||
PURB urb;
|
||||
PIO_STACK_LOCATION irpStack;
|
||||
|
||||
TraceDbg(TRACE_BUSPDO, "%!FUNC! Entry");
|
||||
|
||||
@@ -475,8 +522,8 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::EvtIoInternalDeviceControl(
|
||||
case IOCTL_INTERNAL_USB_SUBMIT_URB:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> IOCTL_INTERNAL_USB_SUBMIT_URB");
|
||||
TRACE_BUSPDO,
|
||||
">> IOCTL_INTERNAL_USB_SUBMIT_URB");
|
||||
|
||||
urb = static_cast<PURB>(URB_FROM_IRP(irp));
|
||||
|
||||
@@ -485,8 +532,8 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::EvtIoInternalDeviceControl(
|
||||
case URB_FUNCTION_CONTROL_TRANSFER:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> >> URB_FUNCTION_CONTROL_TRANSFER");
|
||||
TRACE_BUSPDO,
|
||||
">> >> URB_FUNCTION_CONTROL_TRANSFER");
|
||||
|
||||
status = ctx->Target->UsbControlTransfer(urb);
|
||||
|
||||
@@ -495,8 +542,8 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::EvtIoInternalDeviceControl(
|
||||
case URB_FUNCTION_CONTROL_TRANSFER_EX:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> >> URB_FUNCTION_CONTROL_TRANSFER_EX");
|
||||
TRACE_BUSPDO,
|
||||
">> >> URB_FUNCTION_CONTROL_TRANSFER_EX");
|
||||
|
||||
status = STATUS_UNSUCCESSFUL;
|
||||
|
||||
@@ -515,8 +562,8 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::EvtIoInternalDeviceControl(
|
||||
case URB_FUNCTION_SELECT_CONFIGURATION:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> >> URB_FUNCTION_SELECT_CONFIGURATION");
|
||||
TRACE_BUSPDO,
|
||||
">> >> URB_FUNCTION_SELECT_CONFIGURATION");
|
||||
|
||||
status = ctx->Target->UsbSelectConfiguration(urb);
|
||||
|
||||
@@ -525,8 +572,8 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::EvtIoInternalDeviceControl(
|
||||
case URB_FUNCTION_SELECT_INTERFACE:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> >> URB_FUNCTION_SELECT_INTERFACE");
|
||||
TRACE_BUSPDO,
|
||||
">> >> URB_FUNCTION_SELECT_INTERFACE");
|
||||
|
||||
status = ctx->Target->UsbSelectInterface(urb);
|
||||
|
||||
@@ -535,26 +582,27 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::EvtIoInternalDeviceControl(
|
||||
case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> >> URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE");
|
||||
TRACE_BUSPDO,
|
||||
">> >> URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE");
|
||||
|
||||
switch (urb->UrbControlDescriptorRequest.DescriptorType)
|
||||
{
|
||||
case USB_DEVICE_DESCRIPTOR_TYPE:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> >> >> USB_DEVICE_DESCRIPTOR_TYPE");
|
||||
TRACE_BUSPDO,
|
||||
">> >> >> USB_DEVICE_DESCRIPTOR_TYPE");
|
||||
|
||||
ctx->Target->UsbGetDeviceDescriptorType(static_cast<PUSB_DEVICE_DESCRIPTOR>(urb->UrbControlDescriptorRequest.TransferBuffer));
|
||||
ctx->Target->UsbGetDeviceDescriptorType(
|
||||
static_cast<PUSB_DEVICE_DESCRIPTOR>(urb->UrbControlDescriptorRequest.TransferBuffer));
|
||||
|
||||
break;
|
||||
|
||||
case USB_CONFIGURATION_DESCRIPTOR_TYPE:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> >> >> USB_CONFIGURATION_DESCRIPTOR_TYPE");
|
||||
TRACE_BUSPDO,
|
||||
">> >> >> USB_CONFIGURATION_DESCRIPTOR_TYPE");
|
||||
|
||||
status = ctx->Target->UsbGetConfigurationDescriptorType(urb);
|
||||
|
||||
@@ -563,8 +611,8 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::EvtIoInternalDeviceControl(
|
||||
case USB_STRING_DESCRIPTOR_TYPE:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> >> >> USB_STRING_DESCRIPTOR_TYPE");
|
||||
TRACE_BUSPDO,
|
||||
">> >> >> USB_STRING_DESCRIPTOR_TYPE");
|
||||
|
||||
status = ctx->Target->UsbGetStringDescriptorType(urb);
|
||||
|
||||
@@ -573,23 +621,23 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::EvtIoInternalDeviceControl(
|
||||
default:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> >> >> Unknown descriptor type");
|
||||
TRACE_BUSPDO,
|
||||
">> >> >> Unknown descriptor type");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
"<< <<");
|
||||
TRACE_BUSPDO,
|
||||
"<< <<");
|
||||
|
||||
break;
|
||||
|
||||
case URB_FUNCTION_GET_STATUS_FROM_DEVICE:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> >> URB_FUNCTION_GET_STATUS_FROM_DEVICE");
|
||||
TRACE_BUSPDO,
|
||||
">> >> URB_FUNCTION_GET_STATUS_FROM_DEVICE");
|
||||
|
||||
// Defaults always succeed
|
||||
status = STATUS_SUCCESS;
|
||||
@@ -599,8 +647,8 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::EvtIoInternalDeviceControl(
|
||||
case URB_FUNCTION_ABORT_PIPE:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> >> URB_FUNCTION_ABORT_PIPE");
|
||||
TRACE_BUSPDO,
|
||||
">> >> URB_FUNCTION_ABORT_PIPE");
|
||||
|
||||
ctx->Target->UsbAbortPipe();
|
||||
|
||||
@@ -609,8 +657,8 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::EvtIoInternalDeviceControl(
|
||||
case URB_FUNCTION_CLASS_INTERFACE:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> >> URB_FUNCTION_CLASS_INTERFACE");
|
||||
TRACE_BUSPDO,
|
||||
">> >> URB_FUNCTION_CLASS_INTERFACE");
|
||||
|
||||
status = ctx->Target->UsbClassInterface(urb);
|
||||
|
||||
@@ -619,8 +667,8 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::EvtIoInternalDeviceControl(
|
||||
case URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> >> URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE");
|
||||
TRACE_BUSPDO,
|
||||
">> >> URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE");
|
||||
|
||||
status = ctx->Target->UsbGetDescriptorFromInterface(urb);
|
||||
|
||||
@@ -629,24 +677,24 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::EvtIoInternalDeviceControl(
|
||||
default:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> >> Unknown function: 0x%X",
|
||||
urb->UrbHeader.Function);
|
||||
TRACE_BUSPDO,
|
||||
">> >> Unknown function: 0x%X",
|
||||
urb->UrbHeader.Function);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
"<<");
|
||||
TRACE_BUSPDO,
|
||||
"<<");
|
||||
|
||||
break;
|
||||
|
||||
case IOCTL_INTERNAL_USB_GET_PORT_STATUS:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> IOCTL_INTERNAL_USB_GET_PORT_STATUS");
|
||||
TRACE_BUSPDO,
|
||||
">> IOCTL_INTERNAL_USB_GET_PORT_STATUS");
|
||||
|
||||
// We report the (virtual) port as always active
|
||||
*static_cast<unsigned long*>(irpStack->Parameters.Others.Argument1) = USBD_PORT_ENABLED | USBD_PORT_CONNECTED;
|
||||
@@ -658,8 +706,8 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::EvtIoInternalDeviceControl(
|
||||
case IOCTL_INTERNAL_USB_RESET_PORT:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> IOCTL_INTERNAL_USB_RESET_PORT");
|
||||
TRACE_BUSPDO,
|
||||
">> IOCTL_INTERNAL_USB_RESET_PORT");
|
||||
|
||||
// Sure, why not ;)
|
||||
status = STATUS_SUCCESS;
|
||||
@@ -669,8 +717,8 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::EvtIoInternalDeviceControl(
|
||||
case IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION");
|
||||
TRACE_BUSPDO,
|
||||
">> IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION");
|
||||
|
||||
// TODO: implement?
|
||||
// This happens if the I/O latency is too high so HIDUSB aborts communication.
|
||||
@@ -681,9 +729,9 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::EvtIoInternalDeviceControl(
|
||||
default:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> Unknown I/O control code 0x%X",
|
||||
IoControlCode);
|
||||
TRACE_BUSPDO,
|
||||
">> Unknown I/O control code 0x%X",
|
||||
IoControlCode);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <usbbusif.h>
|
||||
|
||||
#include <ViGEm/Common.h>
|
||||
#include <initguid.h>
|
||||
|
||||
//
|
||||
// Some insane macro-magic =3
|
||||
@@ -27,21 +28,22 @@ namespace ViGEm::Bus::Core
|
||||
class EmulationTargetPDO
|
||||
{
|
||||
public:
|
||||
EmulationTargetPDO(USHORT VID, USHORT PID);
|
||||
EmulationTargetPDO(ULONG Serial, LONG SessionId, USHORT VendorId, USHORT ProductId);
|
||||
|
||||
virtual ~EmulationTargetPDO() = default;
|
||||
|
||||
static EmulationTargetPDO* GetPdoBySerial(IN WDFDEVICE ParentDevice, IN ULONG SerialNo);
|
||||
|
||||
virtual NTSTATUS PdoPrepareDevice(PWDFDEVICE_INIT DeviceInit,
|
||||
PUNICODE_STRING DeviceId,
|
||||
PUNICODE_STRING DeviceDescription) = 0;
|
||||
PUNICODE_STRING DeviceId,
|
||||
PUNICODE_STRING DeviceDescription) = 0;
|
||||
|
||||
virtual NTSTATUS PdoPrepareHardware() = 0;
|
||||
|
||||
virtual NTSTATUS PdoInitContext() = 0;
|
||||
|
||||
NTSTATUS PdoCreateDevice(_In_ WDFDEVICE Device,
|
||||
_In_ PWDFDEVICE_INIT DeviceInit,
|
||||
_In_ PPDO_IDENTIFICATION_DESCRIPTION Description);
|
||||
|
||||
NTSTATUS PdoCreateDevice(_In_ WDFDEVICE ParentDevice,
|
||||
_In_ PWDFDEVICE_INIT DeviceInit);
|
||||
|
||||
VOID SetSerial(ULONG Serial);
|
||||
|
||||
@@ -49,11 +51,11 @@ namespace ViGEm::Bus::Core
|
||||
|
||||
bool operator==(EmulationTargetPDO& other) const
|
||||
{
|
||||
return (other.SerialNo == this->SerialNo);
|
||||
return (other._SerialNo == this->_SerialNo);
|
||||
}
|
||||
|
||||
virtual VOID UsbGetDeviceDescriptorType(PUSB_DEVICE_DESCRIPTOR pDescriptor) = 0;
|
||||
|
||||
|
||||
NTSTATUS UsbSelectConfiguration(PURB Urb);
|
||||
|
||||
void UsbAbortPipe();
|
||||
@@ -68,10 +70,20 @@ namespace ViGEm::Bus::Core
|
||||
|
||||
virtual NTSTATUS UsbGetStringDescriptorType(PURB Urb) = 0;
|
||||
|
||||
virtual NTSTATUS UsbBulkOrInterruptTransfer(struct _URB_BULK_OR_INTERRUPT_TRANSFER* pTransfer, WDFREQUEST Request) = 0;
|
||||
virtual NTSTATUS UsbBulkOrInterruptTransfer(struct _URB_BULK_OR_INTERRUPT_TRANSFER* pTransfer,
|
||||
WDFREQUEST Request) = 0;
|
||||
|
||||
virtual NTSTATUS UsbControlTransfer(PURB Urb) = 0;
|
||||
|
||||
|
||||
virtual NTSTATUS SubmitReport(PVOID NewReport) = 0;
|
||||
|
||||
NTSTATUS EnqueueNotification(WDFREQUEST Request) const;
|
||||
|
||||
bool IsOwnerProcess() const;
|
||||
|
||||
private:
|
||||
static unsigned long current_process_id();
|
||||
|
||||
protected:
|
||||
static const ULONG _maxHardwareIdLength = 0xFF;
|
||||
|
||||
@@ -112,58 +124,79 @@ namespace ViGEm::Bus::Core
|
||||
//
|
||||
// Unique serial number of the device on the bus
|
||||
//
|
||||
ULONG SerialNo{};
|
||||
ULONG _SerialNo{};
|
||||
|
||||
//
|
||||
// PID of the process creating this PDO
|
||||
//
|
||||
DWORD OwnerProcessId{};
|
||||
DWORD _OwnerProcessId{};
|
||||
|
||||
//
|
||||
// File object session ID
|
||||
//
|
||||
LONG _SessionId{};
|
||||
|
||||
//
|
||||
// Device type this PDO is emulating
|
||||
//
|
||||
VIGEM_TARGET_TYPE TargetType;
|
||||
VIGEM_TARGET_TYPE _TargetType;
|
||||
|
||||
//
|
||||
// If set, the vendor ID the emulated device is reporting
|
||||
//
|
||||
USHORT VendorId{};
|
||||
USHORT _VendorId{};
|
||||
|
||||
//
|
||||
// If set, the product ID the emulated device is reporting
|
||||
//
|
||||
USHORT ProductId{};
|
||||
USHORT _ProductId{};
|
||||
|
||||
//
|
||||
// Queue for incoming data interrupt transfer
|
||||
//
|
||||
WDFQUEUE PendingUsbInRequests{};
|
||||
WDFQUEUE _PendingUsbInRequests{};
|
||||
|
||||
//
|
||||
// Queue for inverted calls
|
||||
//
|
||||
WDFQUEUE PendingNotificationRequests{};
|
||||
WDFQUEUE _PendingNotificationRequests{};
|
||||
|
||||
//
|
||||
// This child objects' device object
|
||||
//
|
||||
WDFDEVICE PdoDevice{};
|
||||
WDFDEVICE _PdoDevice{};
|
||||
|
||||
//
|
||||
// Signals the bus that PDO is ready to receive data
|
||||
//
|
||||
KEVENT PdoBootNotificationEvent;
|
||||
KEVENT _PdoBootNotificationEvent;
|
||||
|
||||
//
|
||||
// Configuration descriptor size
|
||||
//
|
||||
ULONG UsbConfigurationDescriptionSize{};
|
||||
ULONG _UsbConfigurationDescriptionSize{};
|
||||
};
|
||||
|
||||
typedef struct _PDO_IDENTIFICATION_DESCRIPTION
|
||||
{
|
||||
//
|
||||
// List entity header
|
||||
//
|
||||
WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER Header;
|
||||
|
||||
//
|
||||
// Primary key to identify PDO
|
||||
//
|
||||
ULONG SerialNo;
|
||||
|
||||
//
|
||||
// Session ID
|
||||
//
|
||||
LONG SessionId;
|
||||
|
||||
//
|
||||
// Context object of PDO
|
||||
//
|
||||
EmulationTargetPDO* Target;
|
||||
} PDO_IDENTIFICATION_DESCRIPTION, *PPDO_IDENTIFICATION_DESCRIPTION;
|
||||
|
||||
|
||||
@@ -28,10 +28,24 @@
|
||||
#include "busenum.h"
|
||||
#include "queue.tmh"
|
||||
|
||||
#include "EmulationTargetPDO.hpp"
|
||||
#include "XusbPdo.hpp"
|
||||
#include "Ds4Pdo.hpp"
|
||||
|
||||
|
||||
|
||||
#ifdef ALLOC_PRAGMA
|
||||
#pragma alloc_text (PAGE, Bus_EvtIoDefault)
|
||||
#endif
|
||||
|
||||
using ViGEm::Bus::Core::PDO_IDENTIFICATION_DESCRIPTION;
|
||||
using ViGEm::Bus::Core::EmulationTargetPDO;
|
||||
using ViGEm::Bus::Targets::EmulationTargetXUSB;
|
||||
using ViGEm::Bus::Targets::EmulationTargetDS4;
|
||||
|
||||
|
||||
EXTERN_C_START
|
||||
|
||||
//
|
||||
// Responds to I/O control requests sent to the FDO.
|
||||
//
|
||||
@@ -52,6 +66,7 @@ VOID Bus_EvtIoDeviceControl(
|
||||
PDS4_REQUEST_NOTIFICATION ds4Notify = NULL;
|
||||
PVIGEM_CHECK_VERSION pCheckVersion = NULL;
|
||||
PXUSB_GET_USER_INDEX pXusbGetUserIndex = NULL;
|
||||
EmulationTargetPDO *pdo;
|
||||
|
||||
Device = WdfIoQueueGetDevice(Queue);
|
||||
|
||||
@@ -66,7 +81,12 @@ VOID Bus_EvtIoDeviceControl(
|
||||
TRACE_QUEUE,
|
||||
"IOCTL_VIGEM_CHECK_VERSION");
|
||||
|
||||
status = WdfRequestRetrieveInputBuffer(Request, sizeof(VIGEM_CHECK_VERSION), (PVOID)&pCheckVersion, &length);
|
||||
status = WdfRequestRetrieveInputBuffer(
|
||||
Request,
|
||||
sizeof(VIGEM_CHECK_VERSION),
|
||||
(PVOID*)&pCheckVersion,
|
||||
&length
|
||||
);
|
||||
|
||||
if (!NT_SUCCESS(status) || length != sizeof(VIGEM_CHECK_VERSION))
|
||||
{
|
||||
@@ -115,7 +135,12 @@ VOID Bus_EvtIoDeviceControl(
|
||||
TRACE_QUEUE,
|
||||
"IOCTL_XUSB_SUBMIT_REPORT");
|
||||
|
||||
status = WdfRequestRetrieveInputBuffer(Request, sizeof(XUSB_SUBMIT_REPORT), (PVOID)&xusbSubmit, &length);
|
||||
status = WdfRequestRetrieveInputBuffer(
|
||||
Request,
|
||||
sizeof(XUSB_SUBMIT_REPORT),
|
||||
(PVOID*)&xusbSubmit,
|
||||
&length
|
||||
);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
@@ -139,7 +164,12 @@ VOID Bus_EvtIoDeviceControl(
|
||||
break;
|
||||
}
|
||||
|
||||
status = Bus_XusbSubmitReport(Device, xusbSubmit->SerialNo, xusbSubmit, FALSE);
|
||||
pdo = EmulationTargetPDO::GetPdoBySerial(Device, xusbSubmit->SerialNo);
|
||||
|
||||
if (pdo == nullptr)
|
||||
status = STATUS_DEVICE_DOES_NOT_EXIST;
|
||||
else
|
||||
status = pdo->SubmitReport(xusbSubmit);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -162,7 +192,12 @@ VOID Bus_EvtIoDeviceControl(
|
||||
break;
|
||||
}
|
||||
|
||||
status = WdfRequestRetrieveInputBuffer(Request, sizeof(XUSB_REQUEST_NOTIFICATION), (PVOID)&xusbNotify, &length);
|
||||
status = WdfRequestRetrieveInputBuffer(
|
||||
Request,
|
||||
sizeof(XUSB_REQUEST_NOTIFICATION),
|
||||
(PVOID*)&xusbNotify,
|
||||
&length
|
||||
);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
@@ -199,7 +234,12 @@ VOID Bus_EvtIoDeviceControl(
|
||||
TRACE_QUEUE,
|
||||
"IOCTL_DS4_SUBMIT_REPORT");
|
||||
|
||||
status = WdfRequestRetrieveInputBuffer(Request, sizeof(DS4_SUBMIT_REPORT), (PVOID)&ds4Submit, &length);
|
||||
status = WdfRequestRetrieveInputBuffer(
|
||||
Request,
|
||||
sizeof(DS4_SUBMIT_REPORT),
|
||||
(PVOID*)&ds4Submit,
|
||||
&length
|
||||
);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
@@ -223,7 +263,12 @@ VOID Bus_EvtIoDeviceControl(
|
||||
break;
|
||||
}
|
||||
|
||||
status = Bus_Ds4SubmitReport(Device, ds4Submit->SerialNo, ds4Submit, FALSE);
|
||||
pdo = EmulationTargetPDO::GetPdoBySerial(Device, ds4Submit->SerialNo);
|
||||
|
||||
if (pdo == nullptr)
|
||||
status = STATUS_DEVICE_DOES_NOT_EXIST;
|
||||
else
|
||||
status = pdo->SubmitReport(ds4Submit);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -246,7 +291,12 @@ VOID Bus_EvtIoDeviceControl(
|
||||
break;
|
||||
}
|
||||
|
||||
status = WdfRequestRetrieveInputBuffer(Request, sizeof(DS4_REQUEST_NOTIFICATION), (PVOID)&ds4Notify, &length);
|
||||
status = WdfRequestRetrieveInputBuffer(
|
||||
Request,
|
||||
sizeof(DS4_REQUEST_NOTIFICATION),
|
||||
(PVOID*)&ds4Notify,
|
||||
&length
|
||||
);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
@@ -291,7 +341,7 @@ VOID Bus_EvtIoDeviceControl(
|
||||
status = WdfRequestRetrieveInputBuffer(
|
||||
Request,
|
||||
sizeof(XUSB_GET_USER_INDEX),
|
||||
(PVOID)&pXusbGetUserIndex,
|
||||
(PVOID*)&pXusbGetUserIndex,
|
||||
&length);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
@@ -309,7 +359,7 @@ VOID Bus_EvtIoDeviceControl(
|
||||
break;
|
||||
}
|
||||
|
||||
status = Xusb_GetUserIndex(Device, pXusbGetUserIndex);
|
||||
//status = Xusb_GetUserIndex(Device, pXusbGetUserIndex);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -332,55 +382,6 @@ VOID Bus_EvtIoDeviceControl(
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_QUEUE, "%!FUNC! Exit with status %!STATUS!", status);
|
||||
}
|
||||
|
||||
//
|
||||
// Gets called upon driver-to-driver communication.
|
||||
//
|
||||
// TODO: incomplete and unused currently
|
||||
//
|
||||
VOID Bus_EvtIoInternalDeviceControl(
|
||||
_In_ WDFQUEUE Queue,
|
||||
_In_ WDFREQUEST Request,
|
||||
_In_ size_t OutputBufferLength,
|
||||
_In_ size_t InputBufferLength,
|
||||
_In_ ULONG IoControlCode
|
||||
)
|
||||
{
|
||||
NTSTATUS status = STATUS_INVALID_PARAMETER;
|
||||
WDFDEVICE Device;
|
||||
size_t length = 0;
|
||||
|
||||
UNREFERENCED_PARAMETER(OutputBufferLength);
|
||||
UNREFERENCED_PARAMETER(InputBufferLength);
|
||||
|
||||
Device = WdfIoQueueGetDevice(Queue);
|
||||
|
||||
KdPrint((DRIVERNAME "Bus_EvtIoInternalDeviceControl: 0x%p\n", Device));
|
||||
|
||||
switch (IoControlCode)
|
||||
{
|
||||
case IOCTL_VIGEM_PLUGIN_TARGET:
|
||||
|
||||
KdPrint((DRIVERNAME "IOCTL_VIGEM_PLUGIN_TARGET\n"));
|
||||
|
||||
status = Bus_PlugInDevice(Device, Request, TRUE, &length);
|
||||
|
||||
break;
|
||||
|
||||
case IOCTL_VIGEM_UNPLUG_TARGET:
|
||||
|
||||
KdPrint((DRIVERNAME "IOCTL_VIGEM_UNPLUG_TARGET\n"));
|
||||
|
||||
status = Bus_UnPlugDevice(Device, Request, TRUE, &length);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (status != STATUS_PENDING)
|
||||
{
|
||||
WdfRequestCompleteWithInformation(Request, status, length);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Catches unsupported requests.
|
||||
//
|
||||
@@ -397,4 +398,6 @@ VOID Bus_EvtIoDefault(
|
||||
WdfRequestComplete(Request, STATUS_INVALID_DEVICE_REQUEST);
|
||||
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_QUEUE, "%!FUNC! Exit");
|
||||
}
|
||||
}
|
||||
|
||||
EXTERN_C_END
|
||||
@@ -27,6 +27,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
EXTERN_C_START
|
||||
|
||||
EVT_WDF_IO_QUEUE_IO_DEFAULT Bus_EvtIoDefault;
|
||||
EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL Bus_EvtIoDeviceControl;
|
||||
EVT_WDF_IO_QUEUE_IO_INTERNAL_DEVICE_CONTROL Bus_EvtIoInternalDeviceControl;
|
||||
|
||||
EXTERN_C_END
|
||||
|
||||
53
sys/UsbPdo.h
53
sys/UsbPdo.h
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Virtual Gamepad Emulation Framework - Windows kernel-mode bus driver
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2016-2019 Nefarius Software Solutions e.U. and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
BOOLEAN USB_BUSIFFN UsbPdo_IsDeviceHighSpeed(IN PVOID BusContext);
|
||||
NTSTATUS USB_BUSIFFN UsbPdo_QueryBusInformation(
|
||||
IN PVOID BusContext,
|
||||
IN ULONG Level,
|
||||
IN OUT PVOID BusInformationBuffer,
|
||||
IN OUT PULONG BusInformationBufferLength,
|
||||
OUT PULONG BusInformationActualLength
|
||||
);
|
||||
NTSTATUS USB_BUSIFFN UsbPdo_SubmitIsoOutUrb(IN PVOID BusContext, IN PURB Urb);
|
||||
NTSTATUS USB_BUSIFFN UsbPdo_QueryBusTime(IN PVOID BusContext, IN OUT PULONG CurrentUsbFrame);
|
||||
VOID USB_BUSIFFN UsbPdo_GetUSBDIVersion(
|
||||
IN PVOID BusContext,
|
||||
IN OUT PUSBD_VERSION_INFORMATION VersionInformation,
|
||||
IN OUT PULONG HcdCapabilities
|
||||
);
|
||||
NTSTATUS UsbPdo_GetDeviceDescriptorType(PURB urb, PPDO_DEVICE_DATA pCommon);
|
||||
NTSTATUS UsbPdo_GetConfigurationDescriptorType(PURB urb, PPDO_DEVICE_DATA pCommon);
|
||||
NTSTATUS UsbPdo_GetStringDescriptorType(PURB urb, PPDO_DEVICE_DATA pCommon);
|
||||
NTSTATUS UsbPdo_SelectConfiguration(PURB urb, PPDO_DEVICE_DATA pCommon);
|
||||
NTSTATUS UsbPdo_SelectInterface(PURB urb, PPDO_DEVICE_DATA pCommon);
|
||||
NTSTATUS UsbPdo_BulkOrInterruptTransfer(PURB urb, WDFDEVICE Device, WDFREQUEST Request);
|
||||
NTSTATUS UsbPdo_AbortPipe(WDFDEVICE Device);
|
||||
NTSTATUS UsbPdo_ClassInterface(PURB urb, WDFDEVICE Device, PPDO_DEVICE_DATA pCommon);
|
||||
NTSTATUS UsbPdo_GetDescriptorFromInterface(PURB urb, PPDO_DEVICE_DATA pCommon);
|
||||
@@ -191,32 +191,26 @@
|
||||
<ClInclude Include="ByteArray.h" />
|
||||
<ClInclude Include="Context.h" />
|
||||
<ClInclude Include="CRTCPP.hpp" />
|
||||
<ClInclude Include="Ds4.h" />
|
||||
<ClInclude Include="Ds4Pdo.hpp" />
|
||||
<ClInclude Include="EmulationTargetPDO.hpp" />
|
||||
<ClInclude Include="Queue.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="trace.h" />
|
||||
<ClInclude Include="UsbPdo.h" />
|
||||
<ClInclude Include="Util.h" />
|
||||
<ClInclude Include="Xusb.h" />
|
||||
<ClInclude Include="XusbPdo.hpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="ViGEmBus.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="busenum.c" />
|
||||
<ClCompile Include="buspdo.c" />
|
||||
<ClCompile Include="busenum.cpp" />
|
||||
<ClCompile Include="buspdo.cpp" />
|
||||
<ClCompile Include="ByteArray.c" />
|
||||
<ClCompile Include="Driver.c" />
|
||||
<ClCompile Include="Ds4.c" />
|
||||
<ClCompile Include="Driver.cpp" />
|
||||
<ClCompile Include="Ds4Pdo.cpp" />
|
||||
<ClCompile Include="EmulationTargetPDO.cpp" />
|
||||
<ClCompile Include="Queue.c" />
|
||||
<ClCompile Include="UsbPdo.c" />
|
||||
<ClCompile Include="Queue.cpp" />
|
||||
<ClCompile Include="Util.c" />
|
||||
<ClCompile Include="xusb.c" />
|
||||
<ClCompile Include="XusbPdo.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
|
||||
@@ -42,18 +42,9 @@
|
||||
<ClInclude Include="Queue.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="UsbPdo.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Context.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Xusb.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Ds4.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Util.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
@@ -86,30 +77,9 @@
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="busenum.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="buspdo.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="xusb.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Queue.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Ds4.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="UsbPdo.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Util.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Driver.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ByteArray.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
@@ -122,6 +92,18 @@
|
||||
<ClCompile Include="Ds4Pdo.cpp">
|
||||
<Filter>Source Files\Targets</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="buspdo.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="busenum.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Queue.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Driver.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="ViGEmBus.rc">
|
||||
|
||||
141
sys/Xusb.h
141
sys/Xusb.h
@@ -1,141 +0,0 @@
|
||||
/*
|
||||
* Virtual Gamepad Emulation Framework - Windows kernel-mode bus driver
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2016-2019 Nefarius Software Solutions e.U. and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
//
|
||||
// For children emulating XUSB devices, the following dummy interfaces
|
||||
// have to be exposed by the PDO or else the child devices won't start
|
||||
//
|
||||
// TODO: that statement might be obsolete, further testing required
|
||||
//
|
||||
|
||||
// Below these interfaces are from wdmguid.h being used as dummies
|
||||
// {70211B0E-0AFB-47DB-AFC1-410BF842497A} PNP_LOCATION_INTERFACE
|
||||
// {B38290E5-3CD0-4F9D-9937-F5FE2B44D47A} D3COLD_SUPPORT_INTERFACE
|
||||
// {2AEB0243-6A6E-486B-82FC-D815F6B97006} REENUMERATE_SELF_INTERFACE_STANDARD
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(_X86_)
|
||||
#define XUSB_CONFIGURATION_SIZE 0x00E4
|
||||
#else
|
||||
#define XUSB_CONFIGURATION_SIZE 0x0130
|
||||
#endif
|
||||
#define XUSB_DESCRIPTOR_SIZE 0x0099
|
||||
#define XUSB_RUMBLE_SIZE 0x08
|
||||
#define XUSB_LEDSET_SIZE 0x03
|
||||
#define XUSB_LEDNUM_SIZE 0x01
|
||||
#define XUSB_INIT_STAGE_SIZE 0x03
|
||||
#define XUSB_BLOB_STORAGE_SIZE 0x2A
|
||||
|
||||
#define XUSB_BLOB_00_OFFSET 0x00
|
||||
#define XUSB_BLOB_01_OFFSET 0x03
|
||||
#define XUSB_BLOB_02_OFFSET 0x06
|
||||
#define XUSB_BLOB_03_OFFSET 0x09
|
||||
#define XUSB_BLOB_04_OFFSET 0x0C
|
||||
#define XUSB_BLOB_05_OFFSET 0x20
|
||||
#define XUSB_BLOB_06_OFFSET 0x23
|
||||
#define XUSB_BLOB_07_OFFSET 0x26
|
||||
|
||||
#define XUSB_IS_DATA_PIPE(_x_) ((BOOLEAN)(_x_->PipeHandle == (USBD_PIPE_HANDLE)0xFFFF0081))
|
||||
#define XUSB_IS_CONTROL_PIPE(_x_) ((BOOLEAN)(_x_->PipeHandle == (USBD_PIPE_HANDLE)0xFFFF0083))
|
||||
|
||||
typedef struct _XUSB_INTERRUPT_IN_PACKET
|
||||
{
|
||||
UCHAR Id;
|
||||
|
||||
UCHAR Size;
|
||||
|
||||
XUSB_REPORT Report;
|
||||
|
||||
} XUSB_INTERRUPT_IN_PACKET, *PXUSB_INTERRUPT_IN_PACKET;
|
||||
|
||||
//
|
||||
// XUSB-specific device context data.
|
||||
//
|
||||
typedef struct _XUSB_DEVICE_DATA
|
||||
{
|
||||
//
|
||||
// Rumble buffer
|
||||
//
|
||||
UCHAR Rumble[XUSB_RUMBLE_SIZE];
|
||||
|
||||
//
|
||||
// LED number (represents XInput slot index)
|
||||
//
|
||||
CHAR LedNumber;
|
||||
|
||||
//
|
||||
// Report packet
|
||||
//
|
||||
XUSB_INTERRUPT_IN_PACKET Packet;
|
||||
|
||||
//
|
||||
// Queue for incoming control interrupt transfer
|
||||
//
|
||||
WDFQUEUE HoldingUsbInRequests;
|
||||
|
||||
//
|
||||
// Required for XInputGetCapabilities to work
|
||||
//
|
||||
BOOLEAN ReportedCapabilities;
|
||||
|
||||
//
|
||||
// Required for XInputGetCapabilities to work
|
||||
//
|
||||
ULONG InterruptInitStage;
|
||||
|
||||
//
|
||||
// Storage of binary blobs (packets) for PDO initialization
|
||||
//
|
||||
WDFMEMORY InterruptBlobStorage;
|
||||
|
||||
} XUSB_DEVICE_DATA, *PXUSB_DEVICE_DATA;
|
||||
|
||||
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(XUSB_DEVICE_DATA, XusbGetData)
|
||||
|
||||
EXTERN_C_START
|
||||
|
||||
NTSTATUS
|
||||
Bus_XusbSubmitReport(
|
||||
WDFDEVICE Device,
|
||||
ULONG SerialNo,
|
||||
PXUSB_SUBMIT_REPORT Report,
|
||||
_In_ BOOLEAN FromInterface
|
||||
);
|
||||
|
||||
//
|
||||
// XUSB-specific functions
|
||||
//
|
||||
NTSTATUS Xusb_PreparePdo(PWDFDEVICE_INIT DeviceInit, USHORT VendorId, USHORT ProductId, PUNICODE_STRING DeviceId, PUNICODE_STRING DeviceDescription);
|
||||
NTSTATUS Xusb_PrepareHardware(WDFDEVICE Device);
|
||||
NTSTATUS Xusb_AssignPdoContext(WDFDEVICE Device);
|
||||
VOID Xusb_GetConfigurationDescriptorType(PUCHAR Buffer, ULONG Length);
|
||||
VOID Xusb_GetDeviceDescriptorType(PUSB_DEVICE_DESCRIPTOR pDescriptor, PPDO_DEVICE_DATA pCommon);
|
||||
VOID Xusb_SelectConfiguration(PUSBD_INTERFACE_INFORMATION pInfo);
|
||||
NTSTATUS Xusb_GetUserIndex(WDFDEVICE Device, PXUSB_GET_USER_INDEX Request);
|
||||
|
||||
EXTERN_C_END
|
||||
@@ -11,10 +11,12 @@
|
||||
|
||||
PCWSTR ViGEm::Bus::Targets::EmulationTargetXUSB::_deviceDescription = L"Virtual Xbox 360 Controller";
|
||||
|
||||
ViGEm::Bus::Targets::EmulationTargetXUSB::EmulationTargetXUSB() : EmulationTargetPDO(0x045E, 0x028E)
|
||||
ViGEm::Bus::Targets::EmulationTargetXUSB::EmulationTargetXUSB(ULONG Serial, LONG SessionId, USHORT VendorId,
|
||||
USHORT ProductId) : EmulationTargetPDO(
|
||||
Serial, SessionId, VendorId, ProductId)
|
||||
{
|
||||
TargetType = Xbox360Wired;
|
||||
UsbConfigurationDescriptionSize = XUSB_DESCRIPTOR_SIZE;
|
||||
_TargetType = Xbox360Wired;
|
||||
_UsbConfigurationDescriptionSize = XUSB_DESCRIPTOR_SIZE;
|
||||
}
|
||||
|
||||
NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::PdoPrepareDevice(PWDFDEVICE_INIT DeviceInit, PUNICODE_STRING DeviceId,
|
||||
@@ -35,7 +37,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::PdoPrepareDevice(PWDFDEVICE_I
|
||||
}
|
||||
|
||||
// Set hardware ID
|
||||
RtlUnicodeStringPrintf(&buffer, L"USB\\VID_%04X&PID_%04X", this->VendorId, this->ProductId);
|
||||
RtlUnicodeStringPrintf(&buffer, L"USB\\VID_%04X&PID_%04X", this->_VendorId, this->_ProductId);
|
||||
|
||||
RtlUnicodeStringCopy(DeviceId, &buffer);
|
||||
|
||||
@@ -110,7 +112,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::PdoPrepareHardware()
|
||||
|
||||
dummyIface.Size = sizeof(INTERFACE);
|
||||
dummyIface.Version = 1;
|
||||
dummyIface.Context = static_cast<PVOID>(this->PdoDevice);
|
||||
dummyIface.Context = static_cast<PVOID>(this->_PdoDevice);
|
||||
|
||||
dummyIface.InterfaceReference = WdfDeviceInterfaceReferenceNoOp;
|
||||
dummyIface.InterfaceDereference = WdfDeviceInterfaceDereferenceNoOp;
|
||||
@@ -128,7 +130,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::PdoPrepareHardware()
|
||||
nullptr
|
||||
);
|
||||
|
||||
NTSTATUS status = WdfDeviceAddQueryInterface(this->PdoDevice, &ifaceCfg);
|
||||
NTSTATUS status = WdfDeviceAddQueryInterface(this->_PdoDevice, &ifaceCfg);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
@@ -149,7 +151,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::PdoPrepareHardware()
|
||||
nullptr
|
||||
);
|
||||
|
||||
status = WdfDeviceAddQueryInterface(this->PdoDevice, &ifaceCfg);
|
||||
status = WdfDeviceAddQueryInterface(this->_PdoDevice, &ifaceCfg);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
@@ -170,7 +172,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::PdoPrepareHardware()
|
||||
nullptr
|
||||
);
|
||||
|
||||
status = WdfDeviceAddQueryInterface(this->PdoDevice, &ifaceCfg);
|
||||
status = WdfDeviceAddQueryInterface(this->_PdoDevice, &ifaceCfg);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
@@ -189,7 +191,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::PdoPrepareHardware()
|
||||
|
||||
xusbInterface.Size = sizeof(USB_BUS_INTERFACE_USBDI_V1);
|
||||
xusbInterface.Version = USB_BUSIF_USBDI_VERSION_1;
|
||||
xusbInterface.BusContext = static_cast<PVOID>(this->PdoDevice);
|
||||
xusbInterface.BusContext = static_cast<PVOID>(this->_PdoDevice);
|
||||
|
||||
xusbInterface.InterfaceReference = WdfDeviceInterfaceReferenceNoOp;
|
||||
xusbInterface.InterfaceDereference = WdfDeviceInterfaceDereferenceNoOp;
|
||||
@@ -207,7 +209,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::PdoPrepareHardware()
|
||||
nullptr
|
||||
);
|
||||
|
||||
status = WdfDeviceAddQueryInterface(this->PdoDevice, &ifaceCfg);
|
||||
status = WdfDeviceAddQueryInterface(this->_PdoDevice, &ifaceCfg);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
@@ -226,7 +228,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::PdoInitContext()
|
||||
PUCHAR blobBuffer;
|
||||
|
||||
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
|
||||
attributes.ParentObject = this->PdoDevice;
|
||||
attributes.ParentObject = this->_PdoDevice;
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_XUSB, "Initializing XUSB context...");
|
||||
|
||||
@@ -290,7 +292,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::PdoInitContext()
|
||||
WDF_IO_QUEUE_CONFIG_INIT(&holdingInQueueConfig, WdfIoQueueDispatchManual);
|
||||
|
||||
status = WdfIoQueueCreate(
|
||||
this->PdoDevice,
|
||||
this->_PdoDevice,
|
||||
&holdingInQueueConfig,
|
||||
WDF_NO_OBJECT_ATTRIBUTES,
|
||||
&this->HoldingUsbInRequests
|
||||
@@ -458,8 +460,8 @@ VOID ViGEm::Bus::Targets::EmulationTargetXUSB::UsbGetDeviceDescriptorType(PUSB_D
|
||||
pDescriptor->bDeviceSubClass = 0xFF;
|
||||
pDescriptor->bDeviceProtocol = 0xFF;
|
||||
pDescriptor->bMaxPacketSize0 = 0x08;
|
||||
pDescriptor->idVendor = this->VendorId;
|
||||
pDescriptor->idProduct = this->ProductId;
|
||||
pDescriptor->idVendor = this->_VendorId;
|
||||
pDescriptor->idProduct = this->_ProductId;
|
||||
pDescriptor->bcdDevice = 0x0114;
|
||||
pDescriptor->iManufacturer = 0x01;
|
||||
pDescriptor->iProduct = 0x02;
|
||||
@@ -790,7 +792,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::UsbBulkOrInterruptTransfer(_U
|
||||
/* This request is sent periodically and relies on data the "feeder"
|
||||
* has to supply, so we queue this request and return with STATUS_PENDING.
|
||||
* The request gets completed as soon as the "feeder" sent an update. */
|
||||
status = WdfRequestForwardToIoQueue(Request, this->PendingUsbInRequests);
|
||||
status = WdfRequestForwardToIoQueue(Request, this->_PendingUsbInRequests);
|
||||
|
||||
return (NT_SUCCESS(status)) ? STATUS_PENDING : status;
|
||||
}
|
||||
@@ -850,7 +852,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::UsbBulkOrInterruptTransfer(_U
|
||||
//
|
||||
// Notify client library that PDO is ready
|
||||
//
|
||||
KeSetEvent(&this->PdoBootNotificationEvent, 0, FALSE);
|
||||
KeSetEvent(&this->_PdoBootNotificationEvent, 0, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -875,7 +877,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::UsbBulkOrInterruptTransfer(_U
|
||||
}
|
||||
|
||||
// Notify user-mode process that new data is available
|
||||
status = WdfIoQueueRetrieveNextRequest(this->PendingNotificationRequests, ¬ifyRequest);
|
||||
status = WdfIoQueueRetrieveNextRequest(this->_PendingNotificationRequests, ¬ifyRequest);
|
||||
|
||||
if (NT_SUCCESS(status))
|
||||
{
|
||||
@@ -892,7 +894,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::UsbBulkOrInterruptTransfer(_U
|
||||
{
|
||||
// Assign values to output buffer
|
||||
notify->Size = sizeof(XUSB_REQUEST_NOTIFICATION);
|
||||
notify->SerialNo = this->SerialNo;
|
||||
notify->SerialNo = this->_SerialNo;
|
||||
notify->LedNumber = this->LedNumber;
|
||||
notify->LargeMotor = this->Rumble[3];
|
||||
notify->SmallMotor = this->Rumble[4];
|
||||
@@ -960,3 +962,54 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::UsbControlTransfer(PURB Urb)
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::SubmitReport(PVOID NewReport)
|
||||
{
|
||||
NTSTATUS status = STATUS_SUCCESS;
|
||||
BOOLEAN changed;
|
||||
WDFREQUEST usbRequest;
|
||||
|
||||
changed = (RtlCompareMemory(&this->Packet.Report,
|
||||
&static_cast<PXUSB_SUBMIT_REPORT>(NewReport)->Report,
|
||||
sizeof(XUSB_REPORT)) != sizeof(XUSB_REPORT));
|
||||
|
||||
// Don't waste pending IRP if input hasn't changed
|
||||
if (!changed)
|
||||
{
|
||||
TraceDbg(
|
||||
TRACE_BUSENUM,
|
||||
"Input report hasn't changed since last update, aborting with %!STATUS!",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
||||
TraceDbg(
|
||||
TRACE_BUSENUM,
|
||||
"Received new report, processing");
|
||||
|
||||
status = WdfIoQueueRetrieveNextRequest(this->_PendingUsbInRequests, &usbRequest);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
return status;
|
||||
|
||||
// Get pending IRP
|
||||
PIRP pendingIrp = WdfRequestWdmGetIrp(usbRequest);
|
||||
|
||||
// Get USB request block
|
||||
PURB urb = static_cast<PURB>(URB_FROM_IRP(pendingIrp));
|
||||
|
||||
// Get transfer buffer
|
||||
auto Buffer = static_cast<PUCHAR>(urb->UrbBulkOrInterruptTransfer.TransferBuffer);
|
||||
|
||||
urb->UrbBulkOrInterruptTransfer.TransferBufferLength = sizeof(XUSB_INTERRUPT_IN_PACKET);
|
||||
|
||||
// Copy submitted report to cache
|
||||
RtlCopyBytes(&this->Packet.Report, &(static_cast<PXUSB_SUBMIT_REPORT>(NewReport))->Report, sizeof(XUSB_REPORT));
|
||||
// Copy cached report to URB transfer buffer
|
||||
RtlCopyBytes(Buffer, &this->Packet, sizeof(XUSB_INTERRUPT_IN_PACKET));
|
||||
|
||||
// Complete pending request
|
||||
WdfRequestComplete(usbRequest, status);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -24,16 +24,16 @@ namespace ViGEm::Bus::Targets
|
||||
{
|
||||
return (pTransfer->PipeHandle == reinterpret_cast<USBD_PIPE_HANDLE>(0xFFFF0083));
|
||||
}
|
||||
|
||||
|
||||
class EmulationTargetXUSB : public Core::EmulationTargetPDO
|
||||
{
|
||||
public:
|
||||
EmulationTargetXUSB();
|
||||
EmulationTargetXUSB(ULONG Serial, LONG SessionId, USHORT VendorId = 0x045E, USHORT ProductId = 0x028E);
|
||||
~EmulationTargetXUSB() = default;
|
||||
|
||||
NTSTATUS PdoPrepareDevice(PWDFDEVICE_INIT DeviceInit,
|
||||
PUNICODE_STRING DeviceId,
|
||||
PUNICODE_STRING DeviceDescription) override;
|
||||
PUNICODE_STRING DeviceId,
|
||||
PUNICODE_STRING DeviceDescription) override;
|
||||
|
||||
NTSTATUS PdoPrepareHardware() override;
|
||||
|
||||
@@ -46,13 +46,14 @@ namespace ViGEm::Bus::Targets
|
||||
NTSTATUS SelectConfiguration(PURB Urb) override;
|
||||
|
||||
void AbortPipe() override;
|
||||
|
||||
|
||||
NTSTATUS UsbClassInterface(PURB Urb) override;
|
||||
NTSTATUS UsbGetDescriptorFromInterface(PURB Urb) override;
|
||||
NTSTATUS UsbSelectInterface(PURB Urb) override;
|
||||
NTSTATUS UsbGetStringDescriptorType(PURB Urb) override;
|
||||
NTSTATUS UsbBulkOrInterruptTransfer(_URB_BULK_OR_INTERRUPT_TRANSFER* pTransfer, WDFREQUEST Request) override;
|
||||
NTSTATUS UsbControlTransfer(PURB Urb) override;
|
||||
NTSTATUS SubmitReport(PVOID NewReport) override;
|
||||
private:
|
||||
static PCWSTR _deviceDescription;
|
||||
|
||||
|
||||
727
sys/busenum.c
727
sys/busenum.c
@@ -1,727 +0,0 @@
|
||||
/*
|
||||
* Virtual Gamepad Emulation Framework - Windows kernel-mode bus driver
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2016-2019 Nefarius Software Solutions e.U. and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#include "busenum.h"
|
||||
#include <wdmguid.h>
|
||||
#include <usb.h>
|
||||
#include "busenum.tmh"
|
||||
|
||||
#ifdef ALLOC_PRAGMA
|
||||
#pragma alloc_text (PAGE, Bus_PlugInDevice)
|
||||
#pragma alloc_text (PAGE, Bus_UnPlugDevice)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Simulates a device plug-in event.
|
||||
//
|
||||
NTSTATUS Bus_PlugInDevice(
|
||||
_In_ WDFDEVICE Device,
|
||||
_In_ WDFREQUEST Request,
|
||||
_In_ BOOLEAN IsInternal,
|
||||
_Out_ size_t* Transferred)
|
||||
{
|
||||
PDO_IDENTIFICATION_DESCRIPTION description;
|
||||
NTSTATUS status;
|
||||
PVIGEM_PLUGIN_TARGET plugIn;
|
||||
WDFFILEOBJECT fileObject;
|
||||
PFDO_FILE_DATA pFileData;
|
||||
size_t length = 0;
|
||||
WDF_OBJECT_ATTRIBUTES requestAttribs;
|
||||
PFDO_PLUGIN_REQUEST_DATA pReqData;
|
||||
PFDO_DEVICE_DATA pFdoData;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSENUM, "%!FUNC! Entry");
|
||||
|
||||
pFdoData = FdoGetData(Device);
|
||||
|
||||
status = WdfRequestRetrieveInputBuffer(Request, sizeof(VIGEM_PLUGIN_TARGET), (PVOID)&plugIn, &length);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"WdfRequestRetrieveInputBuffer failed with status %!STATUS!", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
if ((sizeof(VIGEM_PLUGIN_TARGET) != plugIn->Size) || (length != plugIn->Size))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"sizeof(VIGEM_PLUGIN_TARGET) buffer size mismatch [%d != %d]",
|
||||
sizeof(VIGEM_PLUGIN_TARGET), plugIn->Size);
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (plugIn->SerialNo == 0)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"Serial no. 0 not allowed");
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
*Transferred = length;
|
||||
|
||||
fileObject = WdfRequestGetFileObject(Request);
|
||||
if (fileObject == NULL)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"WdfRequestGetFileObject failed to fetch WDFFILEOBJECT from request 0x%p",
|
||||
Request);
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
pFileData = FileObjectGetData(fileObject);
|
||||
if (pFileData == NULL)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"FileObjectGetData failed to get context data for 0x%p",
|
||||
fileObject);
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
//
|
||||
// Initialize the description with the information about the newly
|
||||
// plugged in device.
|
||||
//
|
||||
WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT(&description.Header, sizeof(description));
|
||||
|
||||
description.SerialNo = plugIn->SerialNo;
|
||||
description.TargetType = plugIn->TargetType;
|
||||
description.OwnerProcessId = CURRENT_PROCESS_ID();
|
||||
description.SessionId = pFileData->SessionId;
|
||||
description.OwnerIsDriver = IsInternal;
|
||||
|
||||
// Set default IDs if supplied values are invalid
|
||||
if (plugIn->VendorId == 0 || plugIn->ProductId == 0)
|
||||
{
|
||||
switch (plugIn->TargetType)
|
||||
{
|
||||
case Xbox360Wired:
|
||||
|
||||
description.VendorId = 0x045E;
|
||||
description.ProductId = 0x028E;
|
||||
|
||||
break;
|
||||
case DualShock4Wired:
|
||||
|
||||
description.VendorId = 0x054C;
|
||||
description.ProductId = 0x05C4;
|
||||
|
||||
break;
|
||||
default:
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
description.VendorId = plugIn->VendorId;
|
||||
description.ProductId = plugIn->ProductId;
|
||||
}
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSENUM,
|
||||
"New PDO properties: serial = %d, type = %d, pid = %d, session = %d, internal = %d, vid = 0x%04X, pid = 0x%04X",
|
||||
description.SerialNo,
|
||||
description.TargetType,
|
||||
description.OwnerProcessId,
|
||||
description.SessionId,
|
||||
description.OwnerIsDriver,
|
||||
description.VendorId,
|
||||
description.ProductId
|
||||
);
|
||||
|
||||
WdfSpinLockAcquire(pFdoData->PendingPluginRequestsLock);
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSENUM,
|
||||
"Current pending requests count: %d",
|
||||
WdfCollectionGetCount(pFdoData->PendingPluginRequests));
|
||||
|
||||
status = WdfChildListAddOrUpdateChildDescriptionAsPresent(
|
||||
WdfFdoGetDefaultChildList(Device),
|
||||
&description.Header,
|
||||
NULL
|
||||
);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"WdfChildListAddOrUpdateChildDescriptionAsPresent failed with status %!STATUS!",
|
||||
status);
|
||||
|
||||
goto pluginEnd;
|
||||
}
|
||||
|
||||
//
|
||||
// The requested serial number is already in use
|
||||
//
|
||||
if (status == STATUS_OBJECT_NAME_EXISTS)
|
||||
{
|
||||
status = STATUS_INVALID_PARAMETER;
|
||||
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"The described PDO already exists (%!STATUS!)",
|
||||
status);
|
||||
|
||||
goto pluginEnd;
|
||||
}
|
||||
|
||||
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&requestAttribs, FDO_PLUGIN_REQUEST_DATA);
|
||||
|
||||
//
|
||||
// Allocate context data to request
|
||||
//
|
||||
status = WdfObjectAllocateContext(Request, &requestAttribs, (PVOID)&pReqData);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"WdfObjectAllocateContext failed with status %!STATUS!",
|
||||
status);
|
||||
|
||||
goto pluginEnd;
|
||||
}
|
||||
|
||||
//
|
||||
// Glue current serial to request
|
||||
//
|
||||
pReqData->Serial = plugIn->SerialNo;
|
||||
|
||||
//
|
||||
// Timestamp the request to track its age
|
||||
//
|
||||
pReqData->Timestamp = KeQueryPerformanceCounter(&pReqData->Frequency);
|
||||
|
||||
//
|
||||
// Keep track of pending request in collection
|
||||
//
|
||||
status = WdfCollectionAdd(pFdoData->PendingPluginRequests, Request);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"WdfCollectionAdd failed with status %!STATUS!",
|
||||
status);
|
||||
|
||||
goto pluginEnd;
|
||||
}
|
||||
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION,
|
||||
TRACE_BUSENUM,
|
||||
"Added item with serial: %d",
|
||||
plugIn->SerialNo);
|
||||
|
||||
//
|
||||
// At least one request present in the collection; start clean-up timer
|
||||
//
|
||||
WdfTimerStart(
|
||||
pFdoData->PendingPluginRequestsCleanupTimer,
|
||||
WDF_REL_TIMEOUT_IN_MS(ORC_TIMER_PERIODIC_DUE_TIME)
|
||||
);
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_DRIVER,
|
||||
"Started periodic timer");
|
||||
|
||||
status = NT_SUCCESS(status) ? STATUS_PENDING : status;
|
||||
|
||||
pluginEnd:
|
||||
|
||||
WdfSpinLockRelease(pFdoData->PendingPluginRequestsLock);
|
||||
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSENUM, "%!FUNC! Exit with status %!STATUS!", status);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
//
|
||||
// Simulates a device unplug event.
|
||||
//
|
||||
NTSTATUS Bus_UnPlugDevice(
|
||||
_In_ WDFDEVICE Device,
|
||||
_In_ WDFREQUEST Request,
|
||||
_In_ BOOLEAN IsInternal,
|
||||
_Out_ size_t* Transferred)
|
||||
{
|
||||
NTSTATUS status;
|
||||
WDFDEVICE hChild;
|
||||
WDFCHILDLIST list;
|
||||
WDF_CHILD_LIST_ITERATOR iterator;
|
||||
WDF_CHILD_RETRIEVE_INFO childInfo;
|
||||
PDO_IDENTIFICATION_DESCRIPTION description;
|
||||
BOOLEAN unplugAll;
|
||||
PVIGEM_UNPLUG_TARGET unPlug;
|
||||
WDFFILEOBJECT fileObject;
|
||||
PFDO_FILE_DATA pFileData = NULL;
|
||||
size_t length = 0;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSENUM, "%!FUNC! Entry");
|
||||
|
||||
status = WdfRequestRetrieveInputBuffer(Request, sizeof(VIGEM_UNPLUG_TARGET), (PVOID)&unPlug, &length);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"WdfRequestRetrieveInputBuffer failed with status %!STATUS!",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
||||
if ((sizeof(VIGEM_UNPLUG_TARGET) != unPlug->Size) || (length != unPlug->Size))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"sizeof(VIGEM_UNPLUG_TARGET) buffer size mismatch [%d != %d]",
|
||||
sizeof(VIGEM_UNPLUG_TARGET), unPlug->Size);
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
*Transferred = length;
|
||||
unplugAll = (unPlug->SerialNo == 0);
|
||||
|
||||
if (!IsInternal)
|
||||
{
|
||||
fileObject = WdfRequestGetFileObject(Request);
|
||||
if (fileObject == NULL)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"WdfRequestGetFileObject failed to fetch WDFFILEOBJECT from request 0x%p",
|
||||
Request);
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
pFileData = FileObjectGetData(fileObject);
|
||||
if (pFileData == NULL)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"FileObjectGetData failed to get context data for 0x%p",
|
||||
fileObject);
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSENUM,
|
||||
"Starting child list traversal");
|
||||
|
||||
list = WdfFdoGetDefaultChildList(Device);
|
||||
|
||||
WDF_CHILD_LIST_ITERATOR_INIT(&iterator, WdfRetrievePresentChildren);
|
||||
|
||||
WdfChildListBeginIteration(list, &iterator);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
WDF_CHILD_RETRIEVE_INFO_INIT(&childInfo, &description.Header);
|
||||
WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT(&description.Header, sizeof(description));
|
||||
|
||||
status = WdfChildListRetrieveNextDevice(list, &iterator, &hChild, &childInfo);
|
||||
|
||||
// Error or no more children, end loop
|
||||
if (!NT_SUCCESS(status) || status == STATUS_NO_MORE_ENTRIES)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSENUM,
|
||||
"WdfChildListRetrieveNextDevice returned with status %!STATUS!",
|
||||
status);
|
||||
break;
|
||||
}
|
||||
|
||||
// If unable to retrieve device
|
||||
if (childInfo.Status != WdfChildListRetrieveDeviceSuccess)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSENUM,
|
||||
"childInfo.Status = %d",
|
||||
childInfo.Status);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Child isn't the one we looked for, skip
|
||||
if (!unplugAll && description.SerialNo != unPlug->SerialNo)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSENUM,
|
||||
"Seeking serial mismatch: %d != %d",
|
||||
description.SerialNo,
|
||||
unPlug->SerialNo);
|
||||
continue;
|
||||
}
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSENUM,
|
||||
"description.SessionId = %d, pFileData->SessionId = %d",
|
||||
description.SessionId,
|
||||
pFileData->SessionId);
|
||||
|
||||
// Only unplug owned children
|
||||
if (IsInternal || description.SessionId == pFileData->SessionId)
|
||||
{
|
||||
// Unplug child
|
||||
status = WdfChildListUpdateChildDescriptionAsMissing(list, &description.Header);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"WdfChildListUpdateChildDescriptionAsMissing failed with status %!STATUS!",
|
||||
status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WdfChildListEndIteration(list, &iterator);
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSENUM,
|
||||
"Finished child list traversal");
|
||||
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSENUM, "%!FUNC! Exit with status %!STATUS!", STATUS_SUCCESS);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
//
|
||||
// Sends a report update to an XUSB PDO.
|
||||
//
|
||||
NTSTATUS Bus_XusbSubmitReport(WDFDEVICE Device, ULONG SerialNo, PXUSB_SUBMIT_REPORT Report, BOOLEAN FromInterface)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSENUM, "%!FUNC! Entry");
|
||||
|
||||
return Bus_SubmitReport(Device, SerialNo, Report, FromInterface);
|
||||
}
|
||||
|
||||
//
|
||||
// Queues an inverted call to receive XUSB-specific updates.
|
||||
//
|
||||
NTSTATUS Bus_QueueNotification(WDFDEVICE Device, ULONG SerialNo, WDFREQUEST Request)
|
||||
{
|
||||
NTSTATUS status = STATUS_INVALID_PARAMETER;
|
||||
WDFDEVICE hChild;
|
||||
PPDO_DEVICE_DATA pdoData;
|
||||
PXUSB_DEVICE_DATA xusbData;
|
||||
PDS4_DEVICE_DATA ds4Data;
|
||||
|
||||
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSENUM, "%!FUNC! Entry");
|
||||
|
||||
hChild = Bus_GetPdo(Device, SerialNo);
|
||||
|
||||
// Validate child
|
||||
if (hChild == NULL)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"Bus_GetPdo: PDO with serial %d not found",
|
||||
SerialNo);
|
||||
return STATUS_NO_SUCH_DEVICE;
|
||||
}
|
||||
|
||||
// Check common context
|
||||
pdoData = PdoGetData(hChild);
|
||||
if (pdoData == NULL)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"PdoGetData failed");
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// Check if caller owns this PDO
|
||||
if (!IS_OWNER(pdoData))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"PDO & Request ownership mismatch: %d != %d",
|
||||
pdoData->OwnerProcessId,
|
||||
CURRENT_PROCESS_ID());
|
||||
return STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
// Queue the request for later completion by the PDO and return STATUS_PENDING
|
||||
switch (pdoData->TargetType)
|
||||
{
|
||||
case Xbox360Wired:
|
||||
|
||||
xusbData = XusbGetData(hChild);
|
||||
|
||||
if (xusbData == NULL)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"XusbGetData failed");
|
||||
break;
|
||||
}
|
||||
|
||||
status = WdfRequestForwardToIoQueue(Request, pdoData->PendingNotificationRequests);
|
||||
|
||||
break;
|
||||
case DualShock4Wired:
|
||||
|
||||
ds4Data = Ds4GetData(hChild);
|
||||
|
||||
if (ds4Data == NULL)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"Ds4GetData failed");
|
||||
break;
|
||||
}
|
||||
|
||||
status = WdfRequestForwardToIoQueue(Request, pdoData->PendingNotificationRequests);
|
||||
|
||||
break;
|
||||
default:
|
||||
status = STATUS_NOT_SUPPORTED;
|
||||
TraceEvents(TRACE_LEVEL_WARNING,
|
||||
TRACE_BUSENUM,
|
||||
"Unknown target type: %d (%!STATUS!)",
|
||||
pdoData->TargetType,
|
||||
status);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"WdfRequestForwardToIoQueue failed with status %!STATUS!",
|
||||
status);
|
||||
}
|
||||
|
||||
status = (NT_SUCCESS(status)) ? STATUS_PENDING : status;
|
||||
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSENUM, "%!FUNC! Exit with status %!STATUS!", status);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
//
|
||||
// Sends a report update to a DS4 PDO.
|
||||
//
|
||||
NTSTATUS Bus_Ds4SubmitReport(WDFDEVICE Device, ULONG SerialNo, PDS4_SUBMIT_REPORT Report, BOOLEAN FromInterface)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSENUM, "%!FUNC! Entry");
|
||||
|
||||
return Bus_SubmitReport(Device, SerialNo, Report, FromInterface);
|
||||
}
|
||||
|
||||
WDFDEVICE Bus_GetPdo(IN WDFDEVICE Device, IN ULONG SerialNo)
|
||||
{
|
||||
WDFCHILDLIST list;
|
||||
WDF_CHILD_RETRIEVE_INFO info;
|
||||
|
||||
list = WdfFdoGetDefaultChildList(Device);
|
||||
|
||||
PDO_IDENTIFICATION_DESCRIPTION description;
|
||||
|
||||
WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT(&description.Header, sizeof(description));
|
||||
|
||||
description.SerialNo = SerialNo;
|
||||
|
||||
WDF_CHILD_RETRIEVE_INFO_INIT(&info, &description.Header);
|
||||
|
||||
return WdfChildListRetrievePdo(list, &info);
|
||||
}
|
||||
|
||||
NTSTATUS Bus_SubmitReport(WDFDEVICE Device, ULONG SerialNo, PVOID Report, BOOLEAN FromInterface)
|
||||
{
|
||||
NTSTATUS status = STATUS_SUCCESS;
|
||||
WDFDEVICE hChild;
|
||||
PPDO_DEVICE_DATA pdoData;
|
||||
WDFREQUEST usbRequest;
|
||||
PIRP pendingIrp;
|
||||
BOOLEAN changed;
|
||||
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSENUM, "%!FUNC! Entry");
|
||||
|
||||
hChild = Bus_GetPdo(Device, SerialNo);
|
||||
|
||||
// Validate child
|
||||
if (hChild == NULL)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"Bus_GetPdo: PDO with serial %d not found",
|
||||
SerialNo);
|
||||
return STATUS_NO_SUCH_DEVICE;
|
||||
}
|
||||
|
||||
// Check common context
|
||||
pdoData = PdoGetData(hChild);
|
||||
if (pdoData == NULL)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"PdoGetData failed");
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// Check if caller owns this PDO
|
||||
if (!FromInterface && !IS_OWNER(pdoData))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"PDO & Request ownership mismatch: %d != %d",
|
||||
pdoData->OwnerProcessId,
|
||||
CURRENT_PROCESS_ID());
|
||||
return STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
// Check if input is different from previous value
|
||||
switch (pdoData->TargetType)
|
||||
{
|
||||
case Xbox360Wired:
|
||||
|
||||
changed = (RtlCompareMemory(&XusbGetData(hChild)->Packet.Report,
|
||||
&((PXUSB_SUBMIT_REPORT)Report)->Report,
|
||||
sizeof(XUSB_REPORT)) != sizeof(XUSB_REPORT));
|
||||
|
||||
break;
|
||||
case DualShock4Wired:
|
||||
|
||||
changed = TRUE;
|
||||
|
||||
break;
|
||||
default:
|
||||
|
||||
changed = FALSE;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Don't waste pending IRP if input hasn't changed
|
||||
if (!changed)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSENUM,
|
||||
"Input report hasn't changed since last update, aborting with %!STATUS!",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSENUM,
|
||||
"Received new report, processing");
|
||||
|
||||
// Get pending USB request
|
||||
switch (pdoData->TargetType)
|
||||
{
|
||||
case Xbox360Wired:
|
||||
|
||||
status = WdfIoQueueRetrieveNextRequest(pdoData->PendingUsbInRequests, &usbRequest);
|
||||
|
||||
break;
|
||||
case DualShock4Wired:
|
||||
|
||||
status = WdfIoQueueRetrieveNextRequest(pdoData->PendingUsbInRequests, &usbRequest);
|
||||
|
||||
break;
|
||||
default:
|
||||
|
||||
status = STATUS_NOT_SUPPORTED;
|
||||
|
||||
TraceEvents(TRACE_LEVEL_WARNING,
|
||||
TRACE_BUSENUM,
|
||||
"Unknown target type: %d (%!STATUS!)",
|
||||
pdoData->TargetType,
|
||||
status);
|
||||
|
||||
goto endSubmitReport;
|
||||
}
|
||||
|
||||
if (status == STATUS_PENDING)
|
||||
goto endSubmitReport;
|
||||
else if (!NT_SUCCESS(status))
|
||||
goto endSubmitReport;
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSENUM,
|
||||
"Processing pending IRP");
|
||||
|
||||
// Get pending IRP
|
||||
pendingIrp = WdfRequestWdmGetIrp(usbRequest);
|
||||
|
||||
// Get USB request block
|
||||
PURB urb = (PURB)URB_FROM_IRP(pendingIrp);
|
||||
|
||||
// Get transfer buffer
|
||||
PUCHAR Buffer = (PUCHAR)urb->UrbBulkOrInterruptTransfer.TransferBuffer;
|
||||
|
||||
switch (pdoData->TargetType)
|
||||
{
|
||||
case Xbox360Wired:
|
||||
|
||||
urb->UrbBulkOrInterruptTransfer.TransferBufferLength = sizeof(XUSB_INTERRUPT_IN_PACKET);
|
||||
|
||||
// Copy submitted report to cache
|
||||
RtlCopyBytes(&XusbGetData(hChild)->Packet.Report, &((PXUSB_SUBMIT_REPORT)Report)->Report, sizeof(XUSB_REPORT));
|
||||
// Copy cached report to URB transfer buffer
|
||||
RtlCopyBytes(Buffer, &XusbGetData(hChild)->Packet, sizeof(XUSB_INTERRUPT_IN_PACKET));
|
||||
|
||||
break;
|
||||
case DualShock4Wired:
|
||||
|
||||
urb->UrbBulkOrInterruptTransfer.TransferBufferLength = DS4_REPORT_SIZE;
|
||||
|
||||
/* Copy report to cache and transfer buffer
|
||||
* Skip first byte as it contains the never changing report id */
|
||||
RtlCopyBytes(Ds4GetData(hChild)->Report + 1, &((PDS4_SUBMIT_REPORT)Report)->Report, sizeof(DS4_REPORT));
|
||||
|
||||
if (Buffer)
|
||||
RtlCopyBytes(Buffer, Ds4GetData(hChild)->Report, DS4_REPORT_SIZE);
|
||||
|
||||
break;
|
||||
default:
|
||||
status = STATUS_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
|
||||
// Complete pending request
|
||||
WdfRequestComplete(usbRequest, status);
|
||||
|
||||
endSubmitReport:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSENUM, "%!FUNC! Exit with status %!STATUS!", status);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
438
sys/busenum.cpp
Normal file
438
sys/busenum.cpp
Normal file
@@ -0,0 +1,438 @@
|
||||
/*
|
||||
* Virtual Gamepad Emulation Framework - Windows kernel-mode bus driver
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2016-2019 Nefarius Software Solutions e.U. and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#include "busenum.h"
|
||||
#include "busenum.tmh"
|
||||
|
||||
#include "EmulationTargetPDO.hpp"
|
||||
#include "XusbPdo.hpp"
|
||||
#include "Ds4Pdo.hpp"
|
||||
|
||||
#ifdef ALLOC_PRAGMA
|
||||
#pragma alloc_text (PAGE, Bus_PlugInDevice)
|
||||
#pragma alloc_text (PAGE, Bus_UnPlugDevice)
|
||||
#endif
|
||||
|
||||
using ViGEm::Bus::Core::PDO_IDENTIFICATION_DESCRIPTION;
|
||||
using ViGEm::Bus::Core::EmulationTargetPDO;
|
||||
using ViGEm::Bus::Targets::EmulationTargetXUSB;
|
||||
using ViGEm::Bus::Targets::EmulationTargetDS4;
|
||||
|
||||
//
|
||||
// Simulates a device plug-in event.
|
||||
//
|
||||
EXTERN_C NTSTATUS Bus_PlugInDevice(
|
||||
_In_ WDFDEVICE Device,
|
||||
_In_ WDFREQUEST Request,
|
||||
_In_ BOOLEAN IsInternal,
|
||||
_Out_ size_t* Transferred)
|
||||
{
|
||||
PDO_IDENTIFICATION_DESCRIPTION description;
|
||||
NTSTATUS status;
|
||||
PVIGEM_PLUGIN_TARGET plugIn;
|
||||
WDFFILEOBJECT fileObject;
|
||||
PFDO_FILE_DATA pFileData;
|
||||
size_t length = 0;
|
||||
|
||||
UNREFERENCED_PARAMETER(IsInternal);
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSENUM, "%!FUNC! Entry");
|
||||
|
||||
status = WdfRequestRetrieveInputBuffer(
|
||||
Request,
|
||||
sizeof(VIGEM_PLUGIN_TARGET),
|
||||
reinterpret_cast<PVOID*>(&plugIn),
|
||||
&length
|
||||
);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"WdfRequestRetrieveInputBuffer failed with status %!STATUS!", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
if ((sizeof(VIGEM_PLUGIN_TARGET) != plugIn->Size) || (length != plugIn->Size))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"sizeof(VIGEM_PLUGIN_TARGET) buffer size mismatch [%d != %d]",
|
||||
sizeof(VIGEM_PLUGIN_TARGET), plugIn->Size);
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (plugIn->SerialNo == 0)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"Serial no. 0 not allowed");
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
*Transferred = length;
|
||||
|
||||
fileObject = WdfRequestGetFileObject(Request);
|
||||
if (fileObject == NULL)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"WdfRequestGetFileObject failed to fetch WDFFILEOBJECT from request 0x%p",
|
||||
Request);
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
pFileData = FileObjectGetData(fileObject);
|
||||
if (pFileData == NULL)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"FileObjectGetData failed to get context data for 0x%p",
|
||||
fileObject);
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
//
|
||||
// Initialize the description with the information about the newly
|
||||
// plugged in device.
|
||||
//
|
||||
WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT(&description.Header, sizeof(description));
|
||||
|
||||
description.SerialNo = plugIn->SerialNo;
|
||||
description.SessionId = pFileData->SessionId;
|
||||
|
||||
// Set default IDs if supplied values are invalid
|
||||
if (plugIn->VendorId == 0 || plugIn->ProductId == 0)
|
||||
{
|
||||
switch (plugIn->TargetType)
|
||||
{
|
||||
case Xbox360Wired:
|
||||
|
||||
description.Target = new EmulationTargetXUSB(plugIn->SerialNo, pFileData->SessionId);
|
||||
|
||||
break;
|
||||
case DualShock4Wired:
|
||||
|
||||
description.Target = new EmulationTargetDS4(plugIn->SerialNo, pFileData->SessionId);
|
||||
|
||||
break;
|
||||
default:
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (plugIn->TargetType)
|
||||
{
|
||||
case Xbox360Wired:
|
||||
|
||||
description.Target = new EmulationTargetXUSB(
|
||||
plugIn->SerialNo,
|
||||
pFileData->SessionId,
|
||||
plugIn->VendorId,
|
||||
plugIn->ProductId
|
||||
);
|
||||
|
||||
break;
|
||||
case DualShock4Wired:
|
||||
|
||||
description.Target = new EmulationTargetDS4(
|
||||
plugIn->SerialNo,
|
||||
pFileData->SessionId,
|
||||
plugIn->VendorId,
|
||||
plugIn->ProductId
|
||||
);
|
||||
|
||||
break;
|
||||
default:
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
}
|
||||
|
||||
status = WdfChildListAddOrUpdateChildDescriptionAsPresent(
|
||||
WdfFdoGetDefaultChildList(Device),
|
||||
&description.Header,
|
||||
NULL
|
||||
);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"WdfChildListAddOrUpdateChildDescriptionAsPresent failed with status %!STATUS!",
|
||||
status);
|
||||
|
||||
goto pluginEnd;
|
||||
}
|
||||
|
||||
//
|
||||
// The requested serial number is already in use
|
||||
//
|
||||
if (status == STATUS_OBJECT_NAME_EXISTS)
|
||||
{
|
||||
status = STATUS_INVALID_PARAMETER;
|
||||
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"The described PDO already exists (%!STATUS!)",
|
||||
status);
|
||||
|
||||
goto pluginEnd;
|
||||
}
|
||||
|
||||
//status = NT_SUCCESS(status) ? STATUS_PENDING : status;
|
||||
|
||||
pluginEnd:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSENUM, "%!FUNC! Exit with status %!STATUS!", status);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
//
|
||||
// Simulates a device unplug event.
|
||||
//
|
||||
EXTERN_C NTSTATUS Bus_UnPlugDevice(
|
||||
_In_ WDFDEVICE Device,
|
||||
_In_ WDFREQUEST Request,
|
||||
_In_ BOOLEAN IsInternal,
|
||||
_Out_ size_t* Transferred)
|
||||
{
|
||||
NTSTATUS status;
|
||||
WDFDEVICE hChild;
|
||||
WDFCHILDLIST list;
|
||||
WDF_CHILD_LIST_ITERATOR iterator;
|
||||
WDF_CHILD_RETRIEVE_INFO childInfo;
|
||||
PDO_IDENTIFICATION_DESCRIPTION description;
|
||||
BOOLEAN unplugAll;
|
||||
PVIGEM_UNPLUG_TARGET unPlug;
|
||||
WDFFILEOBJECT fileObject;
|
||||
PFDO_FILE_DATA pFileData = NULL;
|
||||
size_t length = 0;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSENUM, "%!FUNC! Entry");
|
||||
|
||||
status = WdfRequestRetrieveInputBuffer(
|
||||
Request,
|
||||
sizeof(VIGEM_UNPLUG_TARGET),
|
||||
(PVOID*)&unPlug,
|
||||
&length
|
||||
);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"WdfRequestRetrieveInputBuffer failed with status %!STATUS!",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
||||
if ((sizeof(VIGEM_UNPLUG_TARGET) != unPlug->Size) || (length != unPlug->Size))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"sizeof(VIGEM_UNPLUG_TARGET) buffer size mismatch [%d != %d]",
|
||||
sizeof(VIGEM_UNPLUG_TARGET), unPlug->Size);
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
*Transferred = length;
|
||||
unplugAll = (unPlug->SerialNo == 0);
|
||||
|
||||
fileObject = WdfRequestGetFileObject(Request);
|
||||
if (fileObject == NULL)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"WdfRequestGetFileObject failed to fetch WDFFILEOBJECT from request 0x%p",
|
||||
Request);
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
pFileData = FileObjectGetData(fileObject);
|
||||
if (pFileData == NULL)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"FileObjectGetData failed to get context data for 0x%p",
|
||||
fileObject);
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSENUM,
|
||||
"Starting child list traversal");
|
||||
|
||||
list = WdfFdoGetDefaultChildList(Device);
|
||||
|
||||
WDF_CHILD_LIST_ITERATOR_INIT(&iterator, WdfRetrievePresentChildren);
|
||||
|
||||
WdfChildListBeginIteration(list, &iterator);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
WDF_CHILD_RETRIEVE_INFO_INIT(&childInfo, &description.Header);
|
||||
WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT(&description.Header, sizeof(description));
|
||||
|
||||
status = WdfChildListRetrieveNextDevice(list, &iterator, &hChild, &childInfo);
|
||||
|
||||
// Error or no more children, end loop
|
||||
if (!NT_SUCCESS(status) || status == STATUS_NO_MORE_ENTRIES)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSENUM,
|
||||
"WdfChildListRetrieveNextDevice returned with status %!STATUS!",
|
||||
status);
|
||||
break;
|
||||
}
|
||||
|
||||
// If unable to retrieve device
|
||||
if (childInfo.Status != WdfChildListRetrieveDeviceSuccess)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSENUM,
|
||||
"childInfo.Status = %d",
|
||||
childInfo.Status);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Child isn't the one we looked for, skip
|
||||
if (!unplugAll && description.SerialNo != unPlug->SerialNo)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSENUM,
|
||||
"Seeking serial mismatch: %d != %d",
|
||||
description.SerialNo,
|
||||
unPlug->SerialNo);
|
||||
continue;
|
||||
}
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSENUM,
|
||||
"description.SessionId = %d, pFileData->SessionId = %d",
|
||||
description.SessionId,
|
||||
pFileData->SessionId);
|
||||
|
||||
// Only unplug owned children
|
||||
if (IsInternal || description.SessionId == pFileData->SessionId)
|
||||
{
|
||||
// Unplug child
|
||||
status = WdfChildListUpdateChildDescriptionAsMissing(list, &description.Header);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"WdfChildListUpdateChildDescriptionAsMissing failed with status %!STATUS!",
|
||||
status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WdfChildListEndIteration(list, &iterator);
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSENUM,
|
||||
"Finished child list traversal");
|
||||
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSENUM, "%!FUNC! Exit with status %!STATUS!", STATUS_SUCCESS);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
//
|
||||
// Sends a report update to an XUSB PDO.
|
||||
//
|
||||
EXTERN_C NTSTATUS Bus_XusbSubmitReport(WDFDEVICE Device, ULONG SerialNo, PXUSB_SUBMIT_REPORT Report, BOOLEAN FromInterface)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSENUM, "%!FUNC! Entry");
|
||||
|
||||
return Bus_SubmitReport(Device, SerialNo, Report, FromInterface);
|
||||
}
|
||||
|
||||
//
|
||||
// Queues an inverted call to receive XUSB-specific updates.
|
||||
//
|
||||
EXTERN_C NTSTATUS Bus_QueueNotification(WDFDEVICE Device, ULONG SerialNo, WDFREQUEST Request)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
TraceDbg(TRACE_BUSENUM, "%!FUNC! Entry");
|
||||
|
||||
const auto pdo = EmulationTargetPDO::GetPdoBySerial(Device, SerialNo);
|
||||
|
||||
// Validate child
|
||||
if (pdo == nullptr)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"PDO with serial %d not found",
|
||||
SerialNo);
|
||||
return STATUS_NO_SUCH_DEVICE;
|
||||
}
|
||||
|
||||
status = pdo->EnqueueNotification(Request);
|
||||
|
||||
status = (NT_SUCCESS(status)) ? STATUS_PENDING : status;
|
||||
|
||||
TraceDbg(TRACE_BUSENUM, "%!FUNC! Exit with status %!STATUS!", status);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
//
|
||||
// Sends a report update to a DS4 PDO.
|
||||
//
|
||||
NTSTATUS Bus_Ds4SubmitReport(WDFDEVICE Device, ULONG SerialNo, PDS4_SUBMIT_REPORT Report, BOOLEAN FromInterface)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSENUM, "%!FUNC! Entry");
|
||||
|
||||
return Bus_SubmitReport(Device, SerialNo, Report, FromInterface);
|
||||
}
|
||||
|
||||
EXTERN_C NTSTATUS Bus_SubmitReport(WDFDEVICE Device, ULONG SerialNo, PVOID Report, BOOLEAN FromInterface)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(FromInterface);
|
||||
|
||||
const auto pdo = EmulationTargetPDO::GetPdoBySerial(Device, SerialNo);
|
||||
|
||||
// Validate child
|
||||
if (pdo == nullptr)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSENUM,
|
||||
"PDO with serial %d not found",
|
||||
SerialNo);
|
||||
return STATUS_NO_SUCH_DEVICE;
|
||||
}
|
||||
|
||||
return pdo->SubmitReport(Report);
|
||||
}
|
||||
@@ -41,24 +41,17 @@
|
||||
#include <usbbusif.h>
|
||||
#include "Context.h"
|
||||
#include "Util.h"
|
||||
#include "UsbPdo.h"
|
||||
#include "Xusb.h"
|
||||
#include "Ds4.h"
|
||||
|
||||
|
||||
|
||||
#pragma region Macros
|
||||
|
||||
#define MAX_INSTANCE_ID_LEN 80
|
||||
#define HID_LANGUAGE_ID_LENGTH 0x04
|
||||
|
||||
#define HID_REQUEST_GET_REPORT 0x01
|
||||
#define HID_REQUEST_SET_REPORT 0x09
|
||||
#define HID_REPORT_TYPE_FEATURE 0x03
|
||||
|
||||
|
||||
#define VIGEM_POOL_TAG 0x45476956 // "EGiV"
|
||||
#define XUSB_POOL_TAG 'BSUX'
|
||||
#define DRIVERNAME "ViGEm: "
|
||||
#define MAX_HARDWARE_ID_LENGTH 0xFF
|
||||
|
||||
#define ORC_PC_FREQUENCY_DIVIDER 1000
|
||||
#define ORC_TIMER_START_DELAY 500 // ms
|
||||
@@ -67,27 +60,9 @@
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Helpers
|
||||
|
||||
//
|
||||
// Extracts the HID Report ID from the supplied class request.
|
||||
//
|
||||
#define HID_GET_REPORT_ID(_req_) ((_req_->Value) & 0xFF)
|
||||
|
||||
//
|
||||
// Extracts the HID Report type from the supplied class request.
|
||||
//
|
||||
#define HID_GET_REPORT_TYPE(_req_) ((_req_->Value >> 8) & 0xFF)
|
||||
|
||||
//
|
||||
// Some insane macro-magic =3
|
||||
//
|
||||
#define P99_PROTECT(...) __VA_ARGS__
|
||||
#define COPY_BYTE_ARRAY(_dst_, _bytes_) do {BYTE b[] = _bytes_; \
|
||||
RtlCopyMemory(_dst_, b, RTL_NUMBER_OF_V1(b)); } while (0)
|
||||
|
||||
#pragma endregion
|
||||
|
||||
EXTERN_C_START
|
||||
|
||||
#pragma region WDF callback prototypes
|
||||
|
||||
@@ -107,8 +82,6 @@ EVT_WDF_IO_QUEUE_IO_INTERNAL_DEVICE_CONTROL Pdo_EvtIoInternalDeviceControl;
|
||||
|
||||
EVT_WDF_OBJECT_CONTEXT_CLEANUP Bus_EvtDriverContextCleanup;
|
||||
|
||||
EVT_WDF_TIMER Bus_PlugInRequestCleanUpEvtTimerFunc;
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Bus enumeration-specific functions
|
||||
@@ -129,13 +102,6 @@ Bus_UnPlugDevice(
|
||||
_Out_ size_t* Transferred
|
||||
);
|
||||
|
||||
NTSTATUS
|
||||
Bus_CreatePdo(
|
||||
_In_ WDFDEVICE Device,
|
||||
_In_ PWDFDEVICE_INIT ChildInit,
|
||||
_In_ PPDO_IDENTIFICATION_DESCRIPTION Description
|
||||
);
|
||||
|
||||
NTSTATUS
|
||||
Bus_QueueNotification(
|
||||
WDFDEVICE Device,
|
||||
@@ -151,11 +117,6 @@ Bus_SubmitReport(
|
||||
_In_ BOOLEAN FromInterface
|
||||
);
|
||||
|
||||
WDFDEVICE
|
||||
Bus_GetPdo(
|
||||
IN WDFDEVICE Device,
|
||||
IN ULONG SerialNo);
|
||||
|
||||
VOID
|
||||
Bus_PdoStageResult(
|
||||
_In_ PINTERFACE InterfaceHeader,
|
||||
@@ -166,3 +127,4 @@ Bus_PdoStageResult(
|
||||
|
||||
#pragma endregion
|
||||
|
||||
EXTERN_C_END
|
||||
|
||||
870
sys/buspdo.c
870
sys/buspdo.c
@@ -1,870 +0,0 @@
|
||||
/*
|
||||
* Virtual Gamepad Emulation Framework - Windows kernel-mode bus driver
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2016-2019 Nefarius Software Solutions e.U. and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#include "busenum.h"
|
||||
#include <wdmsec.h>
|
||||
#include <usbioctl.h>
|
||||
#include "buspdo.tmh"
|
||||
|
||||
#ifdef ALLOC_PRAGMA
|
||||
#pragma alloc_text(PAGE, Bus_CreatePdo)
|
||||
#pragma alloc_text(PAGE, Bus_EvtDeviceListCreatePdo)
|
||||
#pragma alloc_text(PAGE, Pdo_EvtDevicePrepareHardware)
|
||||
#endif
|
||||
|
||||
NTSTATUS Bus_EvtDeviceListCreatePdo(
|
||||
WDFCHILDLIST DeviceList,
|
||||
PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER IdentificationDescription,
|
||||
PWDFDEVICE_INIT ChildInit)
|
||||
{
|
||||
PPDO_IDENTIFICATION_DESCRIPTION pDesc;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
pDesc = CONTAINING_RECORD(IdentificationDescription, PDO_IDENTIFICATION_DESCRIPTION, Header);
|
||||
|
||||
return Bus_CreatePdo(WdfChildListGetDevice(DeviceList), ChildInit, pDesc);
|
||||
}
|
||||
|
||||
//
|
||||
// Compares two children on the bus based on their serial numbers.
|
||||
//
|
||||
BOOLEAN Bus_EvtChildListIdentificationDescriptionCompare(
|
||||
WDFCHILDLIST DeviceList,
|
||||
PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER FirstIdentificationDescription,
|
||||
PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER SecondIdentificationDescription)
|
||||
{
|
||||
PPDO_IDENTIFICATION_DESCRIPTION lhs, rhs;
|
||||
|
||||
UNREFERENCED_PARAMETER(DeviceList);
|
||||
|
||||
lhs = CONTAINING_RECORD(FirstIdentificationDescription,
|
||||
PDO_IDENTIFICATION_DESCRIPTION,
|
||||
Header);
|
||||
rhs = CONTAINING_RECORD(SecondIdentificationDescription,
|
||||
PDO_IDENTIFICATION_DESCRIPTION,
|
||||
Header);
|
||||
|
||||
return (lhs->SerialNo == rhs->SerialNo) ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
//
|
||||
// Creates and initializes a PDO (child).
|
||||
//
|
||||
NTSTATUS Bus_CreatePdo(
|
||||
_In_ WDFDEVICE Device,
|
||||
_In_ PWDFDEVICE_INIT DeviceInit,
|
||||
_In_ PPDO_IDENTIFICATION_DESCRIPTION Description)
|
||||
{
|
||||
NTSTATUS status;
|
||||
PPDO_DEVICE_DATA pdoData;
|
||||
WDFDEVICE hChild = NULL;
|
||||
WDF_DEVICE_PNP_CAPABILITIES pnpCaps;
|
||||
WDF_DEVICE_POWER_CAPABILITIES powerCaps;
|
||||
WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;
|
||||
WDF_OBJECT_ATTRIBUTES pdoAttributes;
|
||||
WDF_IO_QUEUE_CONFIG defaultPdoQueueConfig;
|
||||
WDFQUEUE defaultPdoQueue;
|
||||
UNICODE_STRING deviceDescription;
|
||||
VIGEM_BUS_INTERFACE busInterface;
|
||||
WDF_OBJECT_ATTRIBUTES attributes;
|
||||
WDF_IO_QUEUE_CONFIG usbInQueueConfig;
|
||||
WDF_IO_QUEUE_CONFIG notificationsQueueConfig;
|
||||
|
||||
DECLARE_CONST_UNICODE_STRING(deviceLocation, L"Virtual Gamepad Emulation Bus");
|
||||
DECLARE_UNICODE_STRING_SIZE(buffer, MAX_INSTANCE_ID_LEN);
|
||||
// reserve space for device id
|
||||
DECLARE_UNICODE_STRING_SIZE(deviceId, MAX_INSTANCE_ID_LEN);
|
||||
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Entry");
|
||||
|
||||
//
|
||||
// Get the FDO interface ASAP to report progress to bus
|
||||
//
|
||||
status = WdfFdoQueryForInterface(Device,
|
||||
&GUID_VIGEM_INTERFACE_PDO,
|
||||
(PINTERFACE)&busInterface,
|
||||
sizeof(VIGEM_BUS_INTERFACE),
|
||||
VIGEM_BUS_INTERFACE_VERSION,
|
||||
NULL);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSPDO,
|
||||
"WdfFdoQueryForInterface failed with status %!STATUS!",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
||||
// set device type
|
||||
WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_BUS_EXTENDER);
|
||||
// Bus is power policy owner
|
||||
WdfDeviceInitSetPowerPolicyOwnership(DeviceInit, FALSE);
|
||||
|
||||
#pragma region Enter RAW device mode
|
||||
|
||||
status = WdfPdoInitAssignRawDevice(DeviceInit, &GUID_DEVCLASS_VIGEM_RAWPDO);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSPDO,
|
||||
"WdfPdoInitAssignRawDevice failed with status %!STATUS!",
|
||||
status);
|
||||
goto endCreatePdo;
|
||||
}
|
||||
|
||||
WdfDeviceInitSetCharacteristics(DeviceInit, FILE_AUTOGENERATED_DEVICE_NAME, TRUE);
|
||||
|
||||
status = WdfDeviceInitAssignSDDLString(DeviceInit, &SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RWX_RES_RWX);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSPDO,
|
||||
"WdfDeviceInitAssignSDDLString failed with status %!STATUS!",
|
||||
status);
|
||||
goto endCreatePdo;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Prepare PDO
|
||||
|
||||
// set parameters matching desired target device
|
||||
switch (Description->TargetType)
|
||||
{
|
||||
//
|
||||
// A Xbox 360 device was requested
|
||||
//
|
||||
case Xbox360Wired:
|
||||
|
||||
status = Xusb_PreparePdo(
|
||||
DeviceInit,
|
||||
Description->VendorId,
|
||||
Description->ProductId,
|
||||
&deviceId,
|
||||
&deviceDescription);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
goto endCreatePdo;
|
||||
|
||||
break;
|
||||
|
||||
//
|
||||
// A Sony DualShock 4 device was requested
|
||||
//
|
||||
case DualShock4Wired:
|
||||
|
||||
status = Ds4_PreparePdo(DeviceInit, &deviceId, &deviceDescription);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
goto endCreatePdo;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
status = STATUS_INVALID_PARAMETER;
|
||||
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSPDO,
|
||||
"Unknown target type: %d (%!STATUS!)",
|
||||
Description->TargetType,
|
||||
status);
|
||||
|
||||
goto endCreatePdo;
|
||||
}
|
||||
|
||||
// set device id
|
||||
status = WdfPdoInitAssignDeviceID(DeviceInit, &deviceId);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSPDO,
|
||||
"WdfPdoInitAssignDeviceID failed with status %!STATUS!",
|
||||
status);
|
||||
goto endCreatePdo;
|
||||
}
|
||||
|
||||
// prepare instance id
|
||||
status = RtlUnicodeStringPrintf(&buffer, L"%02d", Description->SerialNo);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSPDO,
|
||||
"RtlUnicodeStringPrintf failed with status %!STATUS!",
|
||||
status);
|
||||
goto endCreatePdo;
|
||||
}
|
||||
|
||||
// set instance id
|
||||
status = WdfPdoInitAssignInstanceID(DeviceInit, &buffer);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSPDO,
|
||||
"WdfPdoInitAssignInstanceID failed with status %!STATUS!",
|
||||
status);
|
||||
goto endCreatePdo;
|
||||
}
|
||||
|
||||
// set device description (for English operating systems)
|
||||
status = WdfPdoInitAddDeviceText(DeviceInit, &deviceDescription, &deviceLocation, 0x409);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSPDO,
|
||||
"WdfPdoInitAddDeviceText failed with status %!STATUS!",
|
||||
status);
|
||||
goto endCreatePdo;
|
||||
}
|
||||
|
||||
// default locale is English
|
||||
// TODO: add more locales
|
||||
WdfPdoInitSetDefaultLocale(DeviceInit, 0x409);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region PNP/Power event callbacks
|
||||
|
||||
WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);
|
||||
|
||||
pnpPowerCallbacks.EvtDevicePrepareHardware = Pdo_EvtDevicePrepareHardware;
|
||||
|
||||
WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
// NOTE: not utilized at the moment
|
||||
WdfPdoInitAllowForwardingRequestToParent(DeviceInit);
|
||||
|
||||
#pragma region Create PDO
|
||||
|
||||
// Add common device data context
|
||||
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&pdoAttributes, PDO_DEVICE_DATA);
|
||||
|
||||
status = WdfDeviceCreate(&DeviceInit, &pdoAttributes, &hChild);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSPDO,
|
||||
"WdfDeviceCreate failed with status %!STATUS!",
|
||||
status);
|
||||
goto endCreatePdo;
|
||||
}
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
"Created PDO 0x%p",
|
||||
hChild);
|
||||
|
||||
switch (Description->TargetType)
|
||||
{
|
||||
// Add XUSB-specific device data context
|
||||
case Xbox360Wired:
|
||||
{
|
||||
PXUSB_DEVICE_DATA xusbData = NULL;
|
||||
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&pdoAttributes, XUSB_DEVICE_DATA);
|
||||
|
||||
status = WdfObjectAllocateContext(hChild, &pdoAttributes, (PVOID)&xusbData);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSPDO,
|
||||
"WdfObjectAllocateContext failed with status %!STATUS!",
|
||||
status);
|
||||
goto endCreatePdo;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case DualShock4Wired:
|
||||
{
|
||||
PDS4_DEVICE_DATA ds4Data = NULL;
|
||||
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&pdoAttributes, DS4_DEVICE_DATA);
|
||||
|
||||
status = WdfObjectAllocateContext(hChild, &pdoAttributes, (PVOID)&ds4Data);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSPDO,
|
||||
"WdfObjectAllocateContext failed with status %!STATUS!",
|
||||
status);
|
||||
goto endCreatePdo;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Expose USB Interface
|
||||
|
||||
status = WdfDeviceCreateDeviceInterface(Device, (LPGUID)&GUID_DEVINTERFACE_USB_DEVICE, NULL);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSPDO,
|
||||
"WdfDeviceCreateDeviceInterface failed with status %!STATUS!",
|
||||
status);
|
||||
goto endCreatePdo;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Set PDO contexts
|
||||
|
||||
pdoData = PdoGetData(hChild);
|
||||
|
||||
pdoData->BusInterface = busInterface;
|
||||
|
||||
pdoData->SerialNo = Description->SerialNo;
|
||||
pdoData->TargetType = Description->TargetType;
|
||||
pdoData->OwnerProcessId = Description->OwnerProcessId;
|
||||
pdoData->VendorId = Description->VendorId;
|
||||
pdoData->ProductId = Description->ProductId;
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
"PDO Context properties: serial = %d, type = %d, pid = %d, vid = 0x%04X, pid = 0x%04X",
|
||||
pdoData->SerialNo,
|
||||
pdoData->TargetType,
|
||||
pdoData->OwnerProcessId,
|
||||
pdoData->VendorId,
|
||||
pdoData->ProductId);
|
||||
|
||||
// Initialize additional contexts (if available)
|
||||
switch (Description->TargetType)
|
||||
{
|
||||
case Xbox360Wired:
|
||||
|
||||
status = Xusb_AssignPdoContext(hChild);
|
||||
|
||||
break;
|
||||
|
||||
case DualShock4Wired:
|
||||
|
||||
status = Ds4_AssignPdoContext(hChild, Description);
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSPDO,
|
||||
"Couldn't initialize additional contexts: %!STATUS!",
|
||||
status);
|
||||
|
||||
goto endCreatePdo;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Create Queues & Locks
|
||||
|
||||
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
|
||||
attributes.ParentObject = hChild;
|
||||
|
||||
// Create and assign queue for incoming interrupt transfer
|
||||
WDF_IO_QUEUE_CONFIG_INIT(&usbInQueueConfig, WdfIoQueueDispatchManual);
|
||||
|
||||
status = WdfIoQueueCreate(hChild, &usbInQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, &pdoData->PendingUsbInRequests);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSPDO,
|
||||
"WdfIoQueueCreate (PendingUsbInRequests) failed with status %!STATUS!",
|
||||
status);
|
||||
goto endCreatePdo;
|
||||
}
|
||||
|
||||
// Create and assign queue for user-land notification requests
|
||||
WDF_IO_QUEUE_CONFIG_INIT(¬ificationsQueueConfig, WdfIoQueueDispatchManual);
|
||||
|
||||
status = WdfIoQueueCreate(Device, ¬ificationsQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, &pdoData->PendingNotificationRequests);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSPDO,
|
||||
"WdfIoQueueCreate (PendingNotificationRequests) failed with status %!STATUS!",
|
||||
status);
|
||||
goto endCreatePdo;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Default I/O queue setup
|
||||
|
||||
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&defaultPdoQueueConfig, WdfIoQueueDispatchParallel);
|
||||
|
||||
defaultPdoQueueConfig.EvtIoInternalDeviceControl = Pdo_EvtIoInternalDeviceControl;
|
||||
|
||||
status = WdfIoQueueCreate(hChild, &defaultPdoQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, &defaultPdoQueue);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_BUSPDO,
|
||||
"WdfIoQueueCreate (Default) failed with status %!STATUS!",
|
||||
status);
|
||||
goto endCreatePdo;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region PNP capabilities
|
||||
|
||||
WDF_DEVICE_PNP_CAPABILITIES_INIT(&pnpCaps);
|
||||
|
||||
pnpCaps.Removable = WdfTrue;
|
||||
pnpCaps.EjectSupported = WdfTrue;
|
||||
pnpCaps.SurpriseRemovalOK = WdfTrue;
|
||||
|
||||
pnpCaps.Address = Description->SerialNo;
|
||||
pnpCaps.UINumber = Description->SerialNo;
|
||||
|
||||
WdfDeviceSetPnpCapabilities(hChild, &pnpCaps);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Power capabilities
|
||||
|
||||
WDF_DEVICE_POWER_CAPABILITIES_INIT(&powerCaps);
|
||||
|
||||
powerCaps.DeviceD1 = WdfTrue;
|
||||
powerCaps.WakeFromD1 = WdfTrue;
|
||||
powerCaps.DeviceWake = PowerDeviceD1;
|
||||
|
||||
powerCaps.DeviceState[PowerSystemWorking] = PowerDeviceD0;
|
||||
powerCaps.DeviceState[PowerSystemSleeping1] = PowerDeviceD1;
|
||||
powerCaps.DeviceState[PowerSystemSleeping2] = PowerDeviceD3;
|
||||
powerCaps.DeviceState[PowerSystemSleeping3] = PowerDeviceD3;
|
||||
powerCaps.DeviceState[PowerSystemHibernate] = PowerDeviceD3;
|
||||
powerCaps.DeviceState[PowerSystemShutdown] = PowerDeviceD3;
|
||||
|
||||
WdfDeviceSetPowerCapabilities(hChild, &powerCaps);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
endCreatePdo:
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION,
|
||||
TRACE_BUSPDO,
|
||||
"BUS_PDO_REPORT_STAGE_RESULT Stage: ViGEmPdoCreate [serial: %d, status: %!STATUS!]",
|
||||
Description->SerialNo, status);
|
||||
|
||||
BUS_PDO_REPORT_STAGE_RESULT(busInterface, ViGEmPdoCreate, Description->SerialNo, status);
|
||||
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSPDO, "%!FUNC! Exit with status %!STATUS!", status);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
//
|
||||
// PDO power-up.
|
||||
//
|
||||
NTSTATUS Pdo_EvtDevicePrepareHardware(
|
||||
_In_ WDFDEVICE Device,
|
||||
_In_ WDFCMRESLIST ResourcesRaw,
|
||||
_In_ WDFCMRESLIST ResourcesTranslated
|
||||
)
|
||||
{
|
||||
PPDO_DEVICE_DATA pdoData;
|
||||
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
UNREFERENCED_PARAMETER(ResourcesRaw);
|
||||
UNREFERENCED_PARAMETER(ResourcesTranslated);
|
||||
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSENUM, "%!FUNC! Entry");
|
||||
|
||||
pdoData = PdoGetData(Device);
|
||||
|
||||
switch (pdoData->TargetType)
|
||||
{
|
||||
// Expose XUSB interfaces
|
||||
case Xbox360Wired:
|
||||
|
||||
status = Xusb_PrepareHardware(Device);
|
||||
|
||||
break;
|
||||
|
||||
case DualShock4Wired:
|
||||
|
||||
status = Ds4_PrepareHardware(Device);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION,
|
||||
TRACE_BUSPDO,
|
||||
"BUS_PDO_REPORT_STAGE_RESULT Stage: ViGEmPdoCreate [serial: %d, status: %!STATUS!]",
|
||||
pdoData->SerialNo, status);
|
||||
|
||||
BUS_PDO_REPORT_STAGE_RESULT(pdoData->BusInterface, ViGEmPdoPrepareHardware, pdoData->SerialNo, status);
|
||||
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSPDO, "%!FUNC! Exit with status %!STATUS!", status);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
//
|
||||
// Responds to IRP_MJ_INTERNAL_DEVICE_CONTROL requests sent to PDO.
|
||||
//
|
||||
VOID Pdo_EvtIoInternalDeviceControl(
|
||||
_In_ WDFQUEUE Queue,
|
||||
_In_ WDFREQUEST Request,
|
||||
_In_ size_t OutputBufferLength,
|
||||
_In_ size_t InputBufferLength,
|
||||
_In_ ULONG IoControlCode
|
||||
)
|
||||
{
|
||||
// Regular buffers not used in USB communication
|
||||
UNREFERENCED_PARAMETER(OutputBufferLength);
|
||||
UNREFERENCED_PARAMETER(InputBufferLength);
|
||||
|
||||
NTSTATUS status = STATUS_INVALID_PARAMETER;
|
||||
WDFDEVICE hDevice;
|
||||
PIRP irp;
|
||||
PURB urb;
|
||||
PPDO_DEVICE_DATA pdoData;
|
||||
PIO_STACK_LOCATION irpStack;
|
||||
PXUSB_DEVICE_DATA pXusbData;
|
||||
PUCHAR blobBuffer;
|
||||
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSPDO, "%!FUNC! Entry");
|
||||
|
||||
hDevice = WdfIoQueueGetDevice(Queue);
|
||||
pdoData = PdoGetData(hDevice);
|
||||
// No help from the framework available from here on
|
||||
irp = WdfRequestWdmGetIrp(Request);
|
||||
irpStack = IoGetCurrentIrpStackLocation(irp);
|
||||
|
||||
switch (IoControlCode)
|
||||
{
|
||||
case IOCTL_INTERNAL_USB_SUBMIT_URB:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> IOCTL_INTERNAL_USB_SUBMIT_URB");
|
||||
|
||||
urb = (PURB)URB_FROM_IRP(irp);
|
||||
|
||||
switch (urb->UrbHeader.Function)
|
||||
{
|
||||
case URB_FUNCTION_CONTROL_TRANSFER:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> >> URB_FUNCTION_CONTROL_TRANSFER");
|
||||
|
||||
switch (urb->UrbControlTransfer.SetupPacket[6])
|
||||
{
|
||||
case 0x04:
|
||||
if (pdoData->TargetType == Xbox360Wired)
|
||||
{
|
||||
pXusbData = XusbGetData(hDevice);
|
||||
blobBuffer = WdfMemoryGetBuffer(pXusbData->InterruptBlobStorage, NULL);
|
||||
//
|
||||
// Xenon magic
|
||||
//
|
||||
RtlCopyMemory(
|
||||
urb->UrbControlTransfer.TransferBuffer,
|
||||
&blobBuffer[XUSB_BLOB_07_OFFSET],
|
||||
0x04
|
||||
);
|
||||
status = STATUS_SUCCESS;
|
||||
}
|
||||
break;
|
||||
case 0x14:
|
||||
//
|
||||
// This is some weird USB 1.0 condition and _must fail_
|
||||
//
|
||||
urb->UrbControlTransfer.Hdr.Status = USBD_STATUS_STALL_PID;
|
||||
status = STATUS_UNSUCCESSFUL;
|
||||
break;
|
||||
case 0x08:
|
||||
//
|
||||
// This is some weird USB 1.0 condition and _must fail_
|
||||
//
|
||||
urb->UrbControlTransfer.Hdr.Status = USBD_STATUS_STALL_PID;
|
||||
status = STATUS_UNSUCCESSFUL;
|
||||
break;
|
||||
default:
|
||||
status = STATUS_SUCCESS;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case URB_FUNCTION_CONTROL_TRANSFER_EX:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> >> URB_FUNCTION_CONTROL_TRANSFER_EX");
|
||||
|
||||
status = STATUS_UNSUCCESSFUL;
|
||||
|
||||
break;
|
||||
|
||||
case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> >> URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER");
|
||||
|
||||
status = UsbPdo_BulkOrInterruptTransfer(urb, hDevice, Request);
|
||||
|
||||
break;
|
||||
|
||||
case URB_FUNCTION_SELECT_CONFIGURATION:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> >> URB_FUNCTION_SELECT_CONFIGURATION");
|
||||
|
||||
status = UsbPdo_SelectConfiguration(urb, pdoData);
|
||||
|
||||
break;
|
||||
|
||||
case URB_FUNCTION_SELECT_INTERFACE:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> >> URB_FUNCTION_SELECT_INTERFACE");
|
||||
|
||||
status = UsbPdo_SelectInterface(urb, pdoData);
|
||||
|
||||
break;
|
||||
|
||||
case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> >> URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE");
|
||||
|
||||
switch (urb->UrbControlDescriptorRequest.DescriptorType)
|
||||
{
|
||||
case USB_DEVICE_DESCRIPTOR_TYPE:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> >> >> USB_DEVICE_DESCRIPTOR_TYPE");
|
||||
|
||||
status = UsbPdo_GetDeviceDescriptorType(urb, pdoData);
|
||||
|
||||
break;
|
||||
|
||||
case USB_CONFIGURATION_DESCRIPTOR_TYPE:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> >> >> USB_CONFIGURATION_DESCRIPTOR_TYPE");
|
||||
|
||||
status = UsbPdo_GetConfigurationDescriptorType(urb, pdoData);
|
||||
|
||||
break;
|
||||
|
||||
case USB_STRING_DESCRIPTOR_TYPE:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> >> >> USB_STRING_DESCRIPTOR_TYPE");
|
||||
|
||||
status = UsbPdo_GetStringDescriptorType(urb, pdoData);
|
||||
|
||||
break;
|
||||
case USB_INTERFACE_DESCRIPTOR_TYPE:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> >> >> USB_INTERFACE_DESCRIPTOR_TYPE");
|
||||
|
||||
break;
|
||||
|
||||
case USB_ENDPOINT_DESCRIPTOR_TYPE:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> >> >> USB_ENDPOINT_DESCRIPTOR_TYPE");
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> >> >> Unknown descriptor type");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
"<< <<");
|
||||
|
||||
break;
|
||||
|
||||
case URB_FUNCTION_GET_STATUS_FROM_DEVICE:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> >> URB_FUNCTION_GET_STATUS_FROM_DEVICE");
|
||||
|
||||
// Defaults always succeed
|
||||
status = STATUS_SUCCESS;
|
||||
|
||||
break;
|
||||
|
||||
case URB_FUNCTION_ABORT_PIPE:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> >> URB_FUNCTION_ABORT_PIPE");
|
||||
|
||||
status = UsbPdo_AbortPipe(hDevice);
|
||||
|
||||
break;
|
||||
|
||||
case URB_FUNCTION_CLASS_INTERFACE:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> >> URB_FUNCTION_CLASS_INTERFACE");
|
||||
|
||||
status = UsbPdo_ClassInterface(urb, hDevice, pdoData);
|
||||
|
||||
break;
|
||||
|
||||
case URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> >> URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE");
|
||||
|
||||
status = UsbPdo_GetDescriptorFromInterface(urb, pdoData);
|
||||
|
||||
//
|
||||
// The DS4 is basically ready to operate at this stage
|
||||
//
|
||||
if (pdoData->TargetType == DualShock4Wired)
|
||||
{
|
||||
//
|
||||
// Report back to FDO that we are ready to operate
|
||||
//
|
||||
BUS_PDO_REPORT_STAGE_RESULT(
|
||||
pdoData->BusInterface,
|
||||
ViGEmPdoInitFinished,
|
||||
pdoData->SerialNo,
|
||||
STATUS_SUCCESS
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> >> Unknown function: 0x%X",
|
||||
urb->UrbHeader.Function);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
"<<");
|
||||
|
||||
break;
|
||||
|
||||
case IOCTL_INTERNAL_USB_GET_PORT_STATUS:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> IOCTL_INTERNAL_USB_GET_PORT_STATUS");
|
||||
|
||||
// We report the (virtual) port as always active
|
||||
*(unsigned long *)irpStack->Parameters.Others.Argument1 = USBD_PORT_ENABLED | USBD_PORT_CONNECTED;
|
||||
|
||||
status = STATUS_SUCCESS;
|
||||
|
||||
break;
|
||||
|
||||
case IOCTL_INTERNAL_USB_RESET_PORT:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> IOCTL_INTERNAL_USB_RESET_PORT");
|
||||
|
||||
// Sure, why not ;)
|
||||
status = STATUS_SUCCESS;
|
||||
|
||||
break;
|
||||
|
||||
case IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION");
|
||||
|
||||
// TODO: implement
|
||||
// This happens if the I/O latency is too high so HIDUSB aborts communication.
|
||||
status = STATUS_SUCCESS;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_BUSPDO,
|
||||
">> Unknown I/O control code 0x%X",
|
||||
IoControlCode);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (status != STATUS_PENDING)
|
||||
{
|
||||
WdfRequestComplete(Request, status);
|
||||
}
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSPDO, "%!FUNC! Exit with status %!STATUS!", status);
|
||||
}
|
||||
|
||||
68
sys/buspdo.cpp
Normal file
68
sys/buspdo.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Virtual Gamepad Emulation Framework - Windows kernel-mode bus driver
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2016-2019 Nefarius Software Solutions e.U. and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "busenum.h"
|
||||
#include "EmulationTargetPDO.hpp"
|
||||
|
||||
#ifdef ALLOC_PRAGMA
|
||||
#pragma alloc_text(PAGE, Bus_EvtDeviceListCreatePdo)
|
||||
#endif
|
||||
|
||||
EXTERN_C NTSTATUS Bus_EvtDeviceListCreatePdo(
|
||||
WDFCHILDLIST DeviceList,
|
||||
PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER IdentificationDescription,
|
||||
PWDFDEVICE_INIT ChildInit)
|
||||
{
|
||||
ViGEm::Bus::Core::PPDO_IDENTIFICATION_DESCRIPTION pDesc;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
pDesc = CONTAINING_RECORD(IdentificationDescription, ViGEm::Bus::Core::PDO_IDENTIFICATION_DESCRIPTION, Header);
|
||||
|
||||
return pDesc->Target->PdoCreateDevice(WdfChildListGetDevice(DeviceList), ChildInit);
|
||||
}
|
||||
|
||||
//
|
||||
// Compares two children on the bus based on their serial numbers.
|
||||
//
|
||||
EXTERN_C BOOLEAN Bus_EvtChildListIdentificationDescriptionCompare(
|
||||
WDFCHILDLIST DeviceList,
|
||||
PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER FirstIdentificationDescription,
|
||||
PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER SecondIdentificationDescription)
|
||||
{
|
||||
ViGEm::Bus::Core::PPDO_IDENTIFICATION_DESCRIPTION lhs, rhs;
|
||||
|
||||
UNREFERENCED_PARAMETER(DeviceList);
|
||||
|
||||
lhs = CONTAINING_RECORD(FirstIdentificationDescription,
|
||||
ViGEm::Bus::Core::PDO_IDENTIFICATION_DESCRIPTION,
|
||||
Header);
|
||||
rhs = CONTAINING_RECORD(SecondIdentificationDescription,
|
||||
ViGEm::Bus::Core::PDO_IDENTIFICATION_DESCRIPTION,
|
||||
Header);
|
||||
|
||||
return (lhs->Target == rhs->Target) ? TRUE : FALSE;
|
||||
}
|
||||
1263
sys/usbpdo.c
1263
sys/usbpdo.c
File diff suppressed because it is too large
Load Diff
643
sys/xusb.c
643
sys/xusb.c
@@ -1,643 +0,0 @@
|
||||
/*
|
||||
* Virtual Gamepad Emulation Framework - Windows kernel-mode bus driver
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2016-2019 Nefarius Software Solutions e.U. and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#include "busenum.h"
|
||||
#include <wdmguid.h>
|
||||
#include "xusb.tmh"
|
||||
|
||||
EXTERN_C_START
|
||||
|
||||
NTSTATUS Xusb_PreparePdo(
|
||||
PWDFDEVICE_INIT DeviceInit,
|
||||
USHORT VendorId,
|
||||
USHORT ProductId,
|
||||
PUNICODE_STRING DeviceId,
|
||||
PUNICODE_STRING DeviceDescription)
|
||||
{
|
||||
NTSTATUS status;
|
||||
DECLARE_UNICODE_STRING_SIZE(buffer, MAX_HARDWARE_ID_LENGTH);
|
||||
|
||||
// prepare device description
|
||||
status = RtlUnicodeStringInit(DeviceDescription, L"Virtual Xbox 360 Controller");
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_XUSB,
|
||||
"RtlUnicodeStringInit failed with status %!STATUS!",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
||||
// Set hardware ID
|
||||
RtlUnicodeStringPrintf(&buffer, L"USB\\VID_%04X&PID_%04X", VendorId, ProductId);
|
||||
|
||||
RtlUnicodeStringCopy(DeviceId, &buffer);
|
||||
|
||||
status = WdfPdoInitAddHardwareID(DeviceInit, &buffer);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_XUSB,
|
||||
"WdfPdoInitAddHardwareID failed with status %!STATUS!",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
// Set compatible IDs
|
||||
RtlUnicodeStringInit(&buffer, L"USB\\MS_COMP_XUSB10");
|
||||
|
||||
status = WdfPdoInitAddCompatibleID(DeviceInit, &buffer);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_XUSB,
|
||||
"WdfPdoInitAddCompatibleID #1 failed with status %!STATUS!",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
||||
RtlUnicodeStringInit(&buffer, L"USB\\Class_FF&SubClass_5D&Prot_01");
|
||||
|
||||
status = WdfPdoInitAddCompatibleID(DeviceInit, &buffer);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_XUSB,
|
||||
"WdfPdoInitAddCompatibleID #2 failed with status %!STATUS!",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
||||
RtlUnicodeStringInit(&buffer, L"USB\\Class_FF&SubClass_5D");
|
||||
|
||||
status = WdfPdoInitAddCompatibleID(DeviceInit, &buffer);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_XUSB,
|
||||
"WdfPdoInitAddCompatibleID #3 failed with status %!STATUS!",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
||||
RtlUnicodeStringInit(&buffer, L"USB\\Class_FF");
|
||||
|
||||
status = WdfPdoInitAddCompatibleID(DeviceInit, &buffer);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_XUSB,
|
||||
"WdfPdoInitAddCompatibleID #4 failed with status %!STATUS!",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS Xusb_PrepareHardware(WDFDEVICE Device)
|
||||
{
|
||||
NTSTATUS status;
|
||||
WDF_QUERY_INTERFACE_CONFIG ifaceCfg;
|
||||
|
||||
INTERFACE dummyIface;
|
||||
|
||||
dummyIface.Size = sizeof(INTERFACE);
|
||||
dummyIface.Version = 1;
|
||||
dummyIface.Context = (PVOID)Device;
|
||||
|
||||
dummyIface.InterfaceReference = WdfDeviceInterfaceReferenceNoOp;
|
||||
dummyIface.InterfaceDereference = WdfDeviceInterfaceDereferenceNoOp;
|
||||
|
||||
/* XUSB.sys will query for the following three "dummy" interfaces
|
||||
* BUT WONT USE IT so we just expose them to satisfy initialization. (TODO: Check if still valid!)
|
||||
*/
|
||||
|
||||
// Dummy PNP_LOCATION
|
||||
|
||||
WDF_QUERY_INTERFACE_CONFIG_INIT(&ifaceCfg, (PINTERFACE)&dummyIface, &GUID_PNP_LOCATION_INTERFACE, NULL);
|
||||
|
||||
status = WdfDeviceAddQueryInterface(Device, &ifaceCfg);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_XUSB,
|
||||
"Couldn't register PNP_LOCATION dummy interface %!GUID! (WdfDeviceAddQueryInterface failed with status %!STATUS!)",
|
||||
&GUID_PNP_LOCATION_INTERFACE,
|
||||
status);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
// Dummy D3COLD_SUPPORT
|
||||
|
||||
WDF_QUERY_INTERFACE_CONFIG_INIT(&ifaceCfg, (PINTERFACE)&dummyIface, &GUID_D3COLD_SUPPORT_INTERFACE, NULL);
|
||||
|
||||
status = WdfDeviceAddQueryInterface(Device, &ifaceCfg);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_XUSB,
|
||||
"Couldn't register D3COLD_SUPPORT dummy interface %!GUID! (WdfDeviceAddQueryInterface failed with status %!STATUS!)",
|
||||
&GUID_D3COLD_SUPPORT_INTERFACE,
|
||||
status);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
// Dummy REENUMERATE_SELF_INTERFACE_STANDARD
|
||||
|
||||
WDF_QUERY_INTERFACE_CONFIG_INIT(&ifaceCfg, (PINTERFACE)&dummyIface, &GUID_REENUMERATE_SELF_INTERFACE_STANDARD, NULL);
|
||||
|
||||
status = WdfDeviceAddQueryInterface(Device, &ifaceCfg);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_XUSB,
|
||||
"Couldn't register REENUM_SELF_STD dummy interface %!GUID! (WdfDeviceAddQueryInterface failed with status %!STATUS!)",
|
||||
&GUID_REENUMERATE_SELF_INTERFACE_STANDARD,
|
||||
status);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
// Expose USB_BUS_INTERFACE_USBDI_GUID
|
||||
|
||||
// This interface actually IS used
|
||||
USB_BUS_INTERFACE_USBDI_V1 xusbInterface;
|
||||
|
||||
xusbInterface.Size = sizeof(USB_BUS_INTERFACE_USBDI_V1);
|
||||
xusbInterface.Version = USB_BUSIF_USBDI_VERSION_1;
|
||||
xusbInterface.BusContext = (PVOID)Device;
|
||||
|
||||
xusbInterface.InterfaceReference = WdfDeviceInterfaceReferenceNoOp;
|
||||
xusbInterface.InterfaceDereference = WdfDeviceInterfaceDereferenceNoOp;
|
||||
|
||||
xusbInterface.SubmitIsoOutUrb = UsbPdo_SubmitIsoOutUrb;
|
||||
xusbInterface.GetUSBDIVersion = UsbPdo_GetUSBDIVersion;
|
||||
xusbInterface.QueryBusTime = UsbPdo_QueryBusTime;
|
||||
xusbInterface.QueryBusInformation = UsbPdo_QueryBusInformation;
|
||||
xusbInterface.IsDeviceHighSpeed = UsbPdo_IsDeviceHighSpeed;
|
||||
|
||||
WDF_QUERY_INTERFACE_CONFIG_INIT(&ifaceCfg, (PINTERFACE)&xusbInterface, &USB_BUS_INTERFACE_USBDI_GUID, NULL);
|
||||
|
||||
status = WdfDeviceAddQueryInterface(Device, &ifaceCfg);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_XUSB,
|
||||
"WdfDeviceAddQueryInterface failed with status %!STATUS!",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS Xusb_AssignPdoContext(WDFDEVICE Device)
|
||||
{
|
||||
NTSTATUS status;
|
||||
WDF_OBJECT_ATTRIBUTES attributes;
|
||||
PUCHAR blobBuffer;
|
||||
|
||||
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
|
||||
attributes.ParentObject = Device;
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_XUSB, "Initializing XUSB context...");
|
||||
|
||||
PXUSB_DEVICE_DATA xusb = XusbGetData(Device);
|
||||
|
||||
RtlZeroMemory(xusb, sizeof(XUSB_DEVICE_DATA));
|
||||
|
||||
// Is later overwritten by actual XInput slot
|
||||
xusb->LedNumber = -1;
|
||||
// Packet size (20 bytes = 0x14)
|
||||
xusb->Packet.Size = 0x14;
|
||||
|
||||
// Allocate blob storage
|
||||
status = WdfMemoryCreate(
|
||||
&attributes,
|
||||
NonPagedPoolNx,
|
||||
XUSB_POOL_TAG,
|
||||
XUSB_BLOB_STORAGE_SIZE,
|
||||
&xusb->InterruptBlobStorage,
|
||||
&blobBuffer
|
||||
);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_XUSB,
|
||||
"WdfMemoryCreate failed with status %!STATUS!",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
||||
// Fill blob storage
|
||||
COPY_BYTE_ARRAY(blobBuffer, P99_PROTECT({
|
||||
// 0
|
||||
0x01, 0x03, 0x0E,
|
||||
// 1
|
||||
0x02, 0x03, 0x00,
|
||||
// 2
|
||||
0x03, 0x03, 0x03,
|
||||
// 3
|
||||
0x08, 0x03, 0x00,
|
||||
// 4
|
||||
0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0xe4, 0xf2,
|
||||
0xb3, 0xf8, 0x49, 0xf3, 0xb0, 0xfc, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
// 5
|
||||
0x01, 0x03, 0x03,
|
||||
// 6
|
||||
0x05, 0x03, 0x00,
|
||||
// 7
|
||||
0x31, 0x3F, 0xCF, 0xDC
|
||||
}));
|
||||
|
||||
// I/O Queue for pending IRPs
|
||||
WDF_IO_QUEUE_CONFIG holdingInQueueConfig;
|
||||
|
||||
// Create and assign queue for unhandled interrupt requests
|
||||
WDF_IO_QUEUE_CONFIG_INIT(&holdingInQueueConfig, WdfIoQueueDispatchManual);
|
||||
|
||||
status = WdfIoQueueCreate(Device, &holdingInQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, &xusb->HoldingUsbInRequests);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_XUSB,
|
||||
"WdfIoQueueCreate (HoldingUsbInRequests) failed with status %!STATUS!",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
VOID Xusb_GetConfigurationDescriptorType(PUCHAR Buffer, ULONG Length)
|
||||
{
|
||||
UCHAR XusbDescriptorData[XUSB_DESCRIPTOR_SIZE] =
|
||||
{
|
||||
0x09, // bLength
|
||||
0x02, // bDescriptorType (Configuration)
|
||||
0x99, 0x00, // wTotalLength 153
|
||||
0x04, // bNumInterfaces 4
|
||||
0x01, // bConfigurationValue
|
||||
0x00, // iConfiguration (String Index)
|
||||
0xA0, // bmAttributes Remote Wakeup
|
||||
0xFA, // bMaxPower 500mA
|
||||
|
||||
0x09, // bLength
|
||||
0x04, // bDescriptorType (Interface)
|
||||
0x00, // bInterfaceNumber 0
|
||||
0x00, // bAlternateSetting
|
||||
0x02, // bNumEndpoints 2
|
||||
0xFF, // bInterfaceClass
|
||||
0x5D, // bInterfaceSubClass
|
||||
0x01, // bInterfaceProtocol
|
||||
0x00, // iInterface (String Index)
|
||||
|
||||
0x11, // bLength
|
||||
0x21, // bDescriptorType (HID)
|
||||
0x00, 0x01, // bcdHID 1.00
|
||||
0x01, // bCountryCode
|
||||
0x25, // bNumDescriptors
|
||||
0x81, // bDescriptorType[0] (Unknown 0x81)
|
||||
0x14, 0x00, // wDescriptorLength[0] 20
|
||||
0x00, // bDescriptorType[1] (Unknown 0x00)
|
||||
0x00, 0x00, // wDescriptorLength[1] 0
|
||||
0x13, // bDescriptorType[2] (Unknown 0x13)
|
||||
0x01, 0x08, // wDescriptorLength[2] 2049
|
||||
0x00, // bDescriptorType[3] (Unknown 0x00)
|
||||
0x00,
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x81, // bEndpointAddress (IN/D2H)
|
||||
0x03, // bmAttributes (Interrupt)
|
||||
0x20, 0x00, // wMaxPacketSize 32
|
||||
0x04, // bInterval 4 (unit depends on device speed)
|
||||
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x01, // bEndpointAddress (OUT/H2D)
|
||||
0x03, // bmAttributes (Interrupt)
|
||||
0x20, 0x00, // wMaxPacketSize 32
|
||||
0x08, // bInterval 8 (unit depends on device speed)
|
||||
|
||||
0x09, // bLength
|
||||
0x04, // bDescriptorType (Interface)
|
||||
0x01, // bInterfaceNumber 1
|
||||
0x00, // bAlternateSetting
|
||||
0x04, // bNumEndpoints 4
|
||||
0xFF, // bInterfaceClass
|
||||
0x5D, // bInterfaceSubClass
|
||||
0x03, // bInterfaceProtocol
|
||||
0x00, // iInterface (String Index)
|
||||
|
||||
0x1B, // bLength
|
||||
0x21, // bDescriptorType (HID)
|
||||
0x00, 0x01, // bcdHID 1.00
|
||||
0x01, // bCountryCode
|
||||
0x01, // bNumDescriptors
|
||||
0x82, // bDescriptorType[0] (Unknown 0x82)
|
||||
0x40, 0x01, // wDescriptorLength[0] 320
|
||||
0x02, 0x20, 0x16, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x82, // bEndpointAddress (IN/D2H)
|
||||
0x03, // bmAttributes (Interrupt)
|
||||
0x20, 0x00, // wMaxPacketSize 32
|
||||
0x02, // bInterval 2 (unit depends on device speed)
|
||||
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x02, // bEndpointAddress (OUT/H2D)
|
||||
0x03, // bmAttributes (Interrupt)
|
||||
0x20, 0x00, // wMaxPacketSize 32
|
||||
0x04, // bInterval 4 (unit depends on device speed)
|
||||
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x83, // bEndpointAddress (IN/D2H)
|
||||
0x03, // bmAttributes (Interrupt)
|
||||
0x20, 0x00, // wMaxPacketSize 32
|
||||
0x40, // bInterval 64 (unit depends on device speed)
|
||||
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x03, // bEndpointAddress (OUT/H2D)
|
||||
0x03, // bmAttributes (Interrupt)
|
||||
0x20, 0x00, // wMaxPacketSize 32
|
||||
0x10, // bInterval 16 (unit depends on device speed)
|
||||
|
||||
0x09, // bLength
|
||||
0x04, // bDescriptorType (Interface)
|
||||
0x02, // bInterfaceNumber 2
|
||||
0x00, // bAlternateSetting
|
||||
0x01, // bNumEndpoints 1
|
||||
0xFF, // bInterfaceClass
|
||||
0x5D, // bInterfaceSubClass
|
||||
0x02, // bInterfaceProtocol
|
||||
0x00, // iInterface (String Index)
|
||||
|
||||
0x09, // bLength
|
||||
0x21, // bDescriptorType (HID)
|
||||
0x00, 0x01, // bcdHID 1.00
|
||||
0x01, // bCountryCode
|
||||
0x22, // bNumDescriptors
|
||||
0x84, // bDescriptorType[0] (Unknown 0x84)
|
||||
0x07, 0x00, // wDescriptorLength[0] 7
|
||||
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x84, // bEndpointAddress (IN/D2H)
|
||||
0x03, // bmAttributes (Interrupt)
|
||||
0x20, 0x00, // wMaxPacketSize 32
|
||||
0x10, // bInterval 16 (unit depends on device speed)
|
||||
|
||||
0x09, // bLength
|
||||
0x04, // bDescriptorType (Interface)
|
||||
0x03, // bInterfaceNumber 3
|
||||
0x00, // bAlternateSetting
|
||||
0x00, // bNumEndpoints 0
|
||||
0xFF, // bInterfaceClass
|
||||
0xFD, // bInterfaceSubClass
|
||||
0x13, // bInterfaceProtocol
|
||||
0x04, // iInterface (String Index)
|
||||
|
||||
0x06, // bLength
|
||||
0x41, // bDescriptorType (Unknown)
|
||||
0x00, 0x01, 0x01, 0x03,
|
||||
// 153 bytes
|
||||
|
||||
// best guess: USB Standard Descriptor
|
||||
};
|
||||
|
||||
RtlCopyBytes(Buffer, XusbDescriptorData, Length);
|
||||
}
|
||||
|
||||
VOID Xusb_GetDeviceDescriptorType(PUSB_DEVICE_DESCRIPTOR pDescriptor, PPDO_DEVICE_DATA pCommon)
|
||||
{
|
||||
pDescriptor->bLength = 0x12;
|
||||
pDescriptor->bDescriptorType = USB_DEVICE_DESCRIPTOR_TYPE;
|
||||
pDescriptor->bcdUSB = 0x0200; // USB v2.0
|
||||
pDescriptor->bDeviceClass = 0xFF;
|
||||
pDescriptor->bDeviceSubClass = 0xFF;
|
||||
pDescriptor->bDeviceProtocol = 0xFF;
|
||||
pDescriptor->bMaxPacketSize0 = 0x08;
|
||||
pDescriptor->idVendor = pCommon->VendorId;
|
||||
pDescriptor->idProduct = pCommon->ProductId;
|
||||
pDescriptor->bcdDevice = 0x0114;
|
||||
pDescriptor->iManufacturer = 0x01;
|
||||
pDescriptor->iProduct = 0x02;
|
||||
pDescriptor->iSerialNumber = 0x03;
|
||||
pDescriptor->bNumConfigurations = 0x01;
|
||||
}
|
||||
|
||||
VOID Xusb_SelectConfiguration(PUSBD_INTERFACE_INFORMATION pInfo)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_XUSB,
|
||||
">> >> >> URB_FUNCTION_SELECT_CONFIGURATION: Length %d, Interface %d, Alternate %d, Pipes %d",
|
||||
(int)pInfo->Length,
|
||||
(int)pInfo->InterfaceNumber,
|
||||
(int)pInfo->AlternateSetting,
|
||||
pInfo->NumberOfPipes);
|
||||
|
||||
pInfo->Class = 0xFF;
|
||||
pInfo->SubClass = 0x5D;
|
||||
pInfo->Protocol = 0x01;
|
||||
|
||||
pInfo->InterfaceHandle = (USBD_INTERFACE_HANDLE)0xFFFF0000;
|
||||
|
||||
pInfo->Pipes[0].MaximumTransferSize = 0x00400000;
|
||||
pInfo->Pipes[0].MaximumPacketSize = 0x20;
|
||||
pInfo->Pipes[0].EndpointAddress = 0x81;
|
||||
pInfo->Pipes[0].Interval = 0x04;
|
||||
pInfo->Pipes[0].PipeType = 0x03;
|
||||
pInfo->Pipes[0].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0081;
|
||||
pInfo->Pipes[0].PipeFlags = 0x00;
|
||||
|
||||
pInfo->Pipes[1].MaximumTransferSize = 0x00400000;
|
||||
pInfo->Pipes[1].MaximumPacketSize = 0x20;
|
||||
pInfo->Pipes[1].EndpointAddress = 0x01;
|
||||
pInfo->Pipes[1].Interval = 0x08;
|
||||
pInfo->Pipes[1].PipeType = 0x03;
|
||||
pInfo->Pipes[1].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0001;
|
||||
pInfo->Pipes[1].PipeFlags = 0x00;
|
||||
|
||||
pInfo = (PUSBD_INTERFACE_INFORMATION)((PCHAR)pInfo + pInfo->Length);
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_XUSB,
|
||||
">> >> >> URB_FUNCTION_SELECT_CONFIGURATION: Length %d, Interface %d, Alternate %d, Pipes %d",
|
||||
(int)pInfo->Length,
|
||||
(int)pInfo->InterfaceNumber,
|
||||
(int)pInfo->AlternateSetting,
|
||||
pInfo->NumberOfPipes);
|
||||
|
||||
pInfo->Class = 0xFF;
|
||||
pInfo->SubClass = 0x5D;
|
||||
pInfo->Protocol = 0x03;
|
||||
|
||||
pInfo->InterfaceHandle = (USBD_INTERFACE_HANDLE)0xFFFF0000;
|
||||
|
||||
pInfo->Pipes[0].MaximumTransferSize = 0x00400000;
|
||||
pInfo->Pipes[0].MaximumPacketSize = 0x20;
|
||||
pInfo->Pipes[0].EndpointAddress = 0x82;
|
||||
pInfo->Pipes[0].Interval = 0x04;
|
||||
pInfo->Pipes[0].PipeType = 0x03;
|
||||
pInfo->Pipes[0].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0082;
|
||||
pInfo->Pipes[0].PipeFlags = 0x00;
|
||||
|
||||
pInfo->Pipes[1].MaximumTransferSize = 0x00400000;
|
||||
pInfo->Pipes[1].MaximumPacketSize = 0x20;
|
||||
pInfo->Pipes[1].EndpointAddress = 0x02;
|
||||
pInfo->Pipes[1].Interval = 0x08;
|
||||
pInfo->Pipes[1].PipeType = 0x03;
|
||||
pInfo->Pipes[1].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0002;
|
||||
pInfo->Pipes[1].PipeFlags = 0x00;
|
||||
|
||||
pInfo->Pipes[2].MaximumTransferSize = 0x00400000;
|
||||
pInfo->Pipes[2].MaximumPacketSize = 0x20;
|
||||
pInfo->Pipes[2].EndpointAddress = 0x83;
|
||||
pInfo->Pipes[2].Interval = 0x08;
|
||||
pInfo->Pipes[2].PipeType = 0x03;
|
||||
pInfo->Pipes[2].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0083;
|
||||
pInfo->Pipes[2].PipeFlags = 0x00;
|
||||
|
||||
pInfo->Pipes[3].MaximumTransferSize = 0x00400000;
|
||||
pInfo->Pipes[3].MaximumPacketSize = 0x20;
|
||||
pInfo->Pipes[3].EndpointAddress = 0x03;
|
||||
pInfo->Pipes[3].Interval = 0x08;
|
||||
pInfo->Pipes[3].PipeType = 0x03;
|
||||
pInfo->Pipes[3].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0003;
|
||||
pInfo->Pipes[3].PipeFlags = 0x00;
|
||||
|
||||
pInfo = (PUSBD_INTERFACE_INFORMATION)((PCHAR)pInfo + pInfo->Length);
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_XUSB,
|
||||
">> >> >> URB_FUNCTION_SELECT_CONFIGURATION: Length %d, Interface %d, Alternate %d, Pipes %d",
|
||||
(int)pInfo->Length,
|
||||
(int)pInfo->InterfaceNumber,
|
||||
(int)pInfo->AlternateSetting,
|
||||
pInfo->NumberOfPipes);
|
||||
|
||||
pInfo->Class = 0xFF;
|
||||
pInfo->SubClass = 0x5D;
|
||||
pInfo->Protocol = 0x02;
|
||||
|
||||
pInfo->InterfaceHandle = (USBD_INTERFACE_HANDLE)0xFFFF0000;
|
||||
|
||||
pInfo->Pipes[0].MaximumTransferSize = 0x00400000;
|
||||
pInfo->Pipes[0].MaximumPacketSize = 0x20;
|
||||
pInfo->Pipes[0].EndpointAddress = 0x84;
|
||||
pInfo->Pipes[0].Interval = 0x04;
|
||||
pInfo->Pipes[0].PipeType = 0x03;
|
||||
pInfo->Pipes[0].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0084;
|
||||
pInfo->Pipes[0].PipeFlags = 0x00;
|
||||
|
||||
pInfo = (PUSBD_INTERFACE_INFORMATION)((PCHAR)pInfo + pInfo->Length);
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE,
|
||||
TRACE_XUSB,
|
||||
">> >> >> URB_FUNCTION_SELECT_CONFIGURATION: Length %d, Interface %d, Alternate %d, Pipes %d",
|
||||
(int)pInfo->Length,
|
||||
(int)pInfo->InterfaceNumber,
|
||||
(int)pInfo->AlternateSetting,
|
||||
pInfo->NumberOfPipes);
|
||||
|
||||
pInfo->Class = 0xFF;
|
||||
pInfo->SubClass = 0xFD;
|
||||
pInfo->Protocol = 0x13;
|
||||
|
||||
pInfo->InterfaceHandle = (USBD_INTERFACE_HANDLE)0xFFFF0000;
|
||||
}
|
||||
|
||||
NTSTATUS Xusb_GetUserIndex(WDFDEVICE Device, PXUSB_GET_USER_INDEX Request)
|
||||
{
|
||||
NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
|
||||
WDFDEVICE hChild;
|
||||
PPDO_DEVICE_DATA pdoData;
|
||||
CHAR userIndex;
|
||||
|
||||
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_XUSB, "%!FUNC! Entry");
|
||||
|
||||
hChild = Bus_GetPdo(Device, Request->SerialNo);
|
||||
|
||||
// Validate child
|
||||
if (hChild == NULL)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_XUSB,
|
||||
"Bus_GetPdo for serial %d failed", Request->SerialNo);
|
||||
return STATUS_NO_SUCH_DEVICE;
|
||||
}
|
||||
|
||||
// Check common context
|
||||
pdoData = PdoGetData(hChild);
|
||||
if (pdoData == NULL)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_XUSB,
|
||||
"PdoGetData failed");
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// Check if caller owns this PDO
|
||||
if (!IS_OWNER(pdoData))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR,
|
||||
TRACE_XUSB,
|
||||
"PID mismatch: %d != %d",
|
||||
pdoData->OwnerProcessId,
|
||||
CURRENT_PROCESS_ID());
|
||||
return STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
userIndex = XusbGetData(hChild)->LedNumber;
|
||||
|
||||
if (userIndex >= 0)
|
||||
{
|
||||
Request->UserIndex = (ULONG)userIndex;
|
||||
status = STATUS_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the index is negative at this stage, we've exceeded XUSER_MAX_COUNT
|
||||
// and need to fail this request with a distinct status.
|
||||
status = STATUS_INVALID_DEVICE_OBJECT_PARAMETER;
|
||||
}
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_XUSB, "%!FUNC! Exit with status %!STATUS!", status);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
EXTERN_C_END
|
||||
Reference in New Issue
Block a user