Files
ViGEmBus/sys/Ds4Pdo.cpp
Benjamin Höglinger-Stelzer 0a2ff96e0c Bugfixes
2022-08-06 16:46:54 +02:00

1370 lines
42 KiB
C++

/*
* Virtual Gamepad Emulation Framework - Windows kernel-mode bus driver
*
* BSD 3-Clause License
*
* Copyright (c) 2018-2022, Nefarius Software Solutions e.U. and Contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <ntifs.h>
#include "Ds4Pdo.hpp"
#include "trace.h"
#include "Ds4Pdo.tmh"
#define NTSTRSAFE_LIB
#include <ntstrsafe.h>
PCWSTR ViGEm::Bus::Targets::EmulationTargetDS4::_deviceDescription = L"Virtual DualShock 4 Controller";
ViGEm::Bus::Targets::EmulationTargetDS4::EmulationTargetDS4(ULONG Serial, LONG SessionId, USHORT VendorId,
USHORT ProductId) : EmulationTargetPDO(
Serial, SessionId, VendorId, ProductId)
{
this->_TargetType = DualShock4Wired;
this->_UsbConfigurationDescriptionSize = DS4_DESCRIPTOR_SIZE;
//
// Set PNP Capabilities
//
this->_PnpCapabilities.SurpriseRemovalOK = WdfTrue;
//
// Set Power Capabilities
//
this->_PowerCapabilities.DeviceState[PowerSystemWorking] = PowerDeviceD0;
this->_PowerCapabilities.WakeFromD0 = WdfTrue;
}
NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::PdoPrepareDevice(PWDFDEVICE_INIT DeviceInit,
PUNICODE_STRING DeviceId, PUNICODE_STRING DeviceDescription)
{
NTSTATUS status;
DECLARE_UNICODE_STRING_SIZE(buffer, _maxHardwareIdLength);
// prepare device description
status = RtlUnicodeStringInit(DeviceDescription, _deviceDescription);
if (!NT_SUCCESS(status))
{
TraceError(
TRACE_DS4,
"RtlUnicodeStringInit failed with status %!STATUS!",
status);
return status;
}
// Set hardware IDs
RtlUnicodeStringPrintf(&buffer, L"USB\\VID_%04X&PID_%04X&REV_0100",
this->_VendorId, this->_ProductId);
status = WdfPdoInitAddHardwareID(DeviceInit, &buffer);
if (!NT_SUCCESS(status))
{
TraceError(
TRACE_DS4,
"WdfPdoInitAddHardwareID failed with status %!STATUS!",
status);
return status;
}
RtlUnicodeStringCopy(DeviceId, &buffer);
RtlUnicodeStringPrintf(&buffer, L"USB\\VID_%04X&PID_%04X",
this->_VendorId, this->_ProductId);
status = WdfPdoInitAddHardwareID(DeviceInit, &buffer);
if (!NT_SUCCESS(status))
{
TraceError(
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))
{
TraceError(
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))
{
TraceError(
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))
{
TraceError(
TRACE_DS4,
"WdfPdoInitAddCompatibleID (#03) failed with status %!STATUS!",
status);
return status;
}
return STATUS_SUCCESS;
}
NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::PdoPrepareHardware()
{
// 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(this->_Report, DefaultHidReport, DS4_REPORT_SIZE);
RtlZeroMemory(&this->_OutputReport, sizeof(DS4_OUTPUT_REPORT));
// Start pending IRP queue flush timer
WdfTimerStart(this->_PendingUsbInRequestsTimer, DS4_QUEUE_FLUSH_PERIOD);
return STATUS_SUCCESS;
}
NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::PdoInitContext()
{
NTSTATUS status;
// Initialize periodic timer
WDF_TIMER_CONFIG timerConfig;
WDF_TIMER_CONFIG_INIT_PERIODIC(
&timerConfig,
PendingUsbRequestsTimerFunc,
DS4_QUEUE_FLUSH_PERIOD
);
// Timer object attributes
WDF_OBJECT_ATTRIBUTES timerAttribs;
WDF_OBJECT_ATTRIBUTES_INIT(&timerAttribs);
// PDO is parent
timerAttribs.ParentObject = this->_PdoDevice;
do
{
// Create timer
if (!NT_SUCCESS(status = WdfTimerCreate(
&timerConfig,
&timerAttribs,
&this->_PendingUsbInRequestsTimer
)))
{
TraceError(
TRACE_DS4,
"WdfTimerCreate failed with status %!STATUS!",
status);
break;
}
// Load/generate MAC address
//
// TODO: tidy up this region
//
WDFKEY keyParams, keyTargets, keyDS, keySerial;
UNICODE_STRING keyName, valueName;
if (!NT_SUCCESS(status = WdfDriverOpenParametersRegistryKey(
WdfGetDriver(),
STANDARD_RIGHTS_ALL,
WDF_NO_OBJECT_ATTRIBUTES,
&keyParams
)))
{
TraceError(
TRACE_DS4,
"WdfDriverOpenParametersRegistryKey failed with status %!STATUS!",
status);
break;
}
RtlUnicodeStringInit(&keyName, L"Targets");
if (!NT_SUCCESS(status = WdfRegistryCreateKey(
keyParams,
&keyName,
KEY_ALL_ACCESS,
REG_OPTION_NON_VOLATILE,
nullptr,
WDF_NO_OBJECT_ATTRIBUTES,
&keyTargets
)))
{
TraceError(
TRACE_DS4,
"WdfRegistryCreateKey failed with status %!STATUS!",
status);
break;
}
RtlUnicodeStringInit(&keyName, L"DualShock");
if (!NT_SUCCESS(status = WdfRegistryCreateKey(
keyTargets,
&keyName,
KEY_ALL_ACCESS,
REG_OPTION_NON_VOLATILE,
nullptr,
WDF_NO_OBJECT_ATTRIBUTES,
&keyDS
)))
{
TraceError(
TRACE_DS4,
"WdfRegistryCreateKey failed with status %!STATUS!",
status);
break;
}
DECLARE_UNICODE_STRING_SIZE(serialPath, 4);
RtlUnicodeStringPrintf(&serialPath, L"%04d", this->_SerialNo);
if (!NT_SUCCESS(status = WdfRegistryCreateKey(
keyDS,
&serialPath,
KEY_ALL_ACCESS,
REG_OPTION_NON_VOLATILE,
nullptr,
WDF_NO_OBJECT_ATTRIBUTES,
&keySerial
)))
{
TraceError(
TRACE_DS4,
"WdfRegistryCreateKey failed with status %!STATUS!",
status);
break;
}
RtlUnicodeStringInit(&valueName, L"TargetMacAddress");
status = WdfRegistryQueryValue(
keySerial,
&valueName,
sizeof(MAC_ADDRESS),
&this->_TargetMacAddress,
nullptr,
nullptr
);
TraceEvents(TRACE_LEVEL_INFORMATION,
TRACE_DS4,
"MAC-Address: %02X:%02X:%02X:%02X:%02X:%02X\n",
this->_TargetMacAddress.Vendor0,
this->_TargetMacAddress.Vendor1,
this->_TargetMacAddress.Vendor2,
this->_TargetMacAddress.Nic0,
this->_TargetMacAddress.Nic1,
this->_TargetMacAddress.Nic2);
if (status == STATUS_OBJECT_NAME_NOT_FOUND)
{
GenerateRandomMacAddress(&this->_TargetMacAddress);
if (!NT_SUCCESS(status = WdfRegistryAssignValue(
keySerial,
&valueName,
REG_BINARY,
sizeof(MAC_ADDRESS),
static_cast<PVOID>(&this->_TargetMacAddress)
)))
{
TraceError(
TRACE_DS4,
"WdfRegistryAssignValue failed with status %!STATUS!",
status);
break;
}
}
else if (!NT_SUCCESS(status))
{
TraceError(
TRACE_DS4,
"WdfRegistryQueryValue failed with status %!STATUS!",
status);
break;
}
WdfRegistryClose(keySerial);
WdfRegistryClose(keyDS);
WdfRegistryClose(keyTargets);
WdfRegistryClose(keyParams);
} while (FALSE);
return status;
}
VOID ViGEm::Bus::Targets::EmulationTargetDS4::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);
}
NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::UsbGetDeviceDescriptorType(PUSB_DEVICE_DESCRIPTOR pDescriptor)
{
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 = this->_VendorId;
pDescriptor->idProduct = this->_ProductId;
pDescriptor->bcdDevice = 0x0100;
pDescriptor->iManufacturer = 0x01;
pDescriptor->iProduct = 0x02;
pDescriptor->iSerialNumber = 0x00;
pDescriptor->bNumConfigurations = 0x01;
return STATUS_SUCCESS;
}
NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::SelectConfiguration(PURB Urb)
{
if (Urb->UrbHeader.Length < DS4_CONFIGURATION_SIZE)
{
TraceEvents(TRACE_LEVEL_WARNING,
TRACE_USBPDO,
">> >> >> URB_FUNCTION_SELECT_CONFIGURATION: Invalid ConfigurationDescriptor");
return STATUS_INVALID_PARAMETER;
}
PUSBD_INTERFACE_INFORMATION pInfo = &Urb->UrbSelectConfiguration.Interface;
TraceVerbose(
TRACE_DS4,
">> >> >> URB_FUNCTION_SELECT_CONFIGURATION: Length %d, Interface %d, Alternate %d, Pipes %d",
static_cast<int>(pInfo->Length),
static_cast<int>(pInfo->InterfaceNumber),
static_cast<int>(pInfo->AlternateSetting),
pInfo->NumberOfPipes);
pInfo->Class = 0x03; // HID
pInfo->SubClass = 0x00;
pInfo->Protocol = 0x00;
pInfo->InterfaceHandle = reinterpret_cast<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 = static_cast<USBD_PIPE_TYPE>(0x03);
pInfo->Pipes[0].PipeHandle = reinterpret_cast<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 = static_cast<USBD_PIPE_TYPE>(0x03);
pInfo->Pipes[1].PipeHandle = reinterpret_cast<USBD_PIPE_HANDLE>(0xFFFF0003);
pInfo->Pipes[1].PipeFlags = 0x00;
return STATUS_SUCCESS;
}
void ViGEm::Bus::Targets::EmulationTargetDS4::AbortPipe()
{
// Higher driver shutting down, emptying PDOs queues
WdfTimerStop(this->_PendingUsbInRequestsTimer, TRUE);
}
NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::UsbClassInterface(PURB Urb)
{
struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST* pRequest = &Urb->UrbControlVendorClassRequest;
TraceVerbose(
TRACE_USBPDO,
">> >> >> URB_FUNCTION_CLASS_INTERFACE");
TraceVerbose(
TRACE_USBPDO,
">> >> >> TransferFlags = 0x%X, Request = 0x%X, Value = 0x%X, Index = 0x%X, BufLen = %d",
pRequest->TransferFlags,
pRequest->Request,
pRequest->Value,
pRequest->Index,
pRequest->TransferBufferLength);
switch (pRequest->Request)
{
case HID_REQUEST_GET_REPORT:
{
UCHAR reportId = hid_get_report_id(pRequest);
UCHAR reportType = hid_get_report_type(pRequest);
TraceVerbose(
TRACE_USBPDO,
">> >> >> >> GET_REPORT(%d): %d",
reportType, reportId);
switch (reportType)
{
case HID_REPORT_TYPE_FEATURE:
{
switch (reportId)
{
case HID_REPORT_ID_0:
{
// Source: http://eleccelerator.com/wiki/index.php?title=DualShock_4#Class_Requests
UCHAR Response[] =
{
0xA3, 0x41, 0x75, 0x67, 0x20, 0x20, 0x33, 0x20,
0x32, 0x30, 0x31, 0x33, 0x00, 0x00, 0x00, 0x00,
0x00, 0x30, 0x37, 0x3A, 0x30, 0x31, 0x3A, 0x31,
0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x31, 0x03, 0x00, 0x00,
0x00, 0x49, 0x00, 0x05, 0x00, 0x00, 0x80, 0x03,
0x00
};
pRequest->TransferBufferLength = ARRAYSIZE(Response);
RtlCopyBytes(pRequest->TransferBuffer, Response, ARRAYSIZE(Response));
break;
}
case HID_REPORT_ID_1:
{
// Source: http://eleccelerator.com/wiki/index.php?title=DualShock_4#Class_Requests
UCHAR Response[] =
{
0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87,
0x22, 0x7B, 0xDD, 0xB2, 0x22, 0x47, 0xDD, 0xBD,
0x22, 0x43, 0xDD, 0x1C, 0x02, 0x1C, 0x02, 0x7F,
0x1E, 0x2E, 0xDF, 0x60, 0x1F, 0x4C, 0xE0, 0x3A,
0x1D, 0xC6, 0xDE, 0x08, 0x00
};
pRequest->TransferBufferLength = ARRAYSIZE(Response);
RtlCopyBytes(pRequest->TransferBuffer, Response, ARRAYSIZE(Response));
break;
}
case HID_REPORT_MAC_ADDRESSES_ID:
{
// Source: http://eleccelerator.com/wiki/index.php?title=DualShock_4#Class_Requests
UCHAR Response[] =
{
0x12, 0x8B, 0x09, 0x07, 0x6D, 0x66, 0x1C, 0x08,
0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// Insert (auto-generated) target MAC address into response
RtlCopyBytes(Response + 1, &this->_TargetMacAddress, sizeof(MAC_ADDRESS));
// Adjust byte order
ReverseByteArray(Response + 1, sizeof(MAC_ADDRESS));
// Insert (auto-generated) host MAC address into response
RtlCopyBytes(Response + 10, &this->_HostMacAddress, sizeof(MAC_ADDRESS));
// Adjust byte order
ReverseByteArray(Response + 10, sizeof(MAC_ADDRESS));
pRequest->TransferBufferLength = ARRAYSIZE(Response);
RtlCopyBytes(pRequest->TransferBuffer, Response, ARRAYSIZE(Response));
break;
}
default:
break;
}
break;
}
default:
break;
}
break;
}
case HID_REQUEST_SET_REPORT:
{
UCHAR reportId = hid_get_report_id(pRequest);
UCHAR reportType = hid_get_report_type(pRequest);
TraceVerbose(
TRACE_USBPDO,
">> >> >> >> SET_REPORT(%d): %d",
reportType, reportId);
switch (reportType)
{
case HID_REPORT_TYPE_FEATURE:
{
switch (reportId)
{
case HID_REPORT_ID_3:
{
// Source: http://eleccelerator.com/wiki/index.php?title=DualShock_4#Class_Requests
UCHAR Response[] =
{
0x13, 0xAC, 0x9E, 0x17, 0x94, 0x05, 0xB0, 0x56,
0xE8, 0x81, 0x38, 0x08, 0x06, 0x51, 0x41, 0xC0,
0x7F, 0x12, 0xAA, 0xD9, 0x66, 0x3C, 0xCE
};
pRequest->TransferBufferLength = ARRAYSIZE(Response);
RtlCopyBytes(pRequest->TransferBuffer, Response, ARRAYSIZE(Response));
break;
}
case HID_REPORT_ID_4:
{
// Source: http://eleccelerator.com/wiki/index.php?title=DualShock_4#Class_Requests
UCHAR Response[] =
{
0x14, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00
};
pRequest->TransferBufferLength = ARRAYSIZE(Response);
RtlCopyBytes(pRequest->TransferBuffer, Response, ARRAYSIZE(Response));
break;
}
default:
break;
}
break;
}
default:
break;
}
break;
}
default:
break;
}
return STATUS_SUCCESS;
}
NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::UsbGetDescriptorFromInterface(PURB Urb)
{
NTSTATUS status = STATUS_INVALID_PARAMETER;
UCHAR Ds4HidReportDescriptor[] =
{
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x05, // Usage (Game Pad)
0xA1, 0x01, // Collection (Application)
0x85, 0x01, // Report ID (1)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x09, 0x32, // Usage (Z)
0x09, 0x35, // Usage (Rz)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x75, 0x08, // Report Size (8)
0x95, 0x04, // Report Count (4)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x09, 0x39, // Usage (Hat switch)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x07, // Logical Maximum (7)
0x35, 0x00, // Physical Minimum (0)
0x46, 0x3B, 0x01, // Physical Maximum (315)
0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter)
0x75, 0x04, // Report Size (4)
0x95, 0x01, // Report Count (1)
0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
0x65, 0x00, // Unit (None)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (0x01)
0x29, 0x0E, // Usage Maximum (0x0E)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x0E, // Report Count (14)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
0x09, 0x20, // Usage (0x20)
0x75, 0x06, // Report Size (6)
0x95, 0x01, // Report Count (1)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x7F, // Logical Maximum (127)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x33, // Usage (Rx)
0x09, 0x34, // Usage (Ry)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x75, 0x08, // Report Size (8)
0x95, 0x02, // Report Count (2)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
0x09, 0x21, // Usage (0x21)
0x95, 0x36, // Report Count (54)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x85, 0x05, // Report ID (5)
0x09, 0x22, // Usage (0x22)
0x95, 0x1F, // Report Count (31)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x04, // Report ID (4)
0x09, 0x23, // Usage (0x23)
0x95, 0x24, // Report Count (36)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x02, // Report ID (2)
0x09, 0x24, // Usage (0x24)
0x95, 0x24, // Report Count (36)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x08, // Report ID (8)
0x09, 0x25, // Usage (0x25)
0x95, 0x03, // Report Count (3)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x10, // Report ID (16)
0x09, 0x26, // Usage (0x26)
0x95, 0x04, // Report Count (4)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x11, // Report ID (17)
0x09, 0x27, // Usage (0x27)
0x95, 0x02, // Report Count (2)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x12, // Report ID (18)
0x06, 0x02, 0xFF, // Usage Page (Vendor Defined 0xFF02)
0x09, 0x21, // Usage (0x21)
0x95, 0x0F, // Report Count (15)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x13, // Report ID (19)
0x09, 0x22, // Usage (0x22)
0x95, 0x16, // Report Count (22)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x14, // Report ID (20)
0x06, 0x05, 0xFF, // Usage Page (Vendor Defined 0xFF05)
0x09, 0x20, // Usage (0x20)
0x95, 0x10, // Report Count (16)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x15, // Report ID (21)
0x09, 0x21, // Usage (0x21)
0x95, 0x2C, // Report Count (44)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x06, 0x80, 0xFF, // Usage Page (Vendor Defined 0xFF80)
0x85, 0x80, // Report ID (128)
0x09, 0x20, // Usage (0x20)
0x95, 0x06, // Report Count (6)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x81, // Report ID (129)
0x09, 0x21, // Usage (0x21)
0x95, 0x06, // Report Count (6)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x82, // Report ID (130)
0x09, 0x22, // Usage (0x22)
0x95, 0x05, // Report Count (5)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x83, // Report ID (131)
0x09, 0x23, // Usage (0x23)
0x95, 0x01, // Report Count (1)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x84, // Report ID (132)
0x09, 0x24, // Usage (0x24)
0x95, 0x04, // Report Count (4)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x85, // Report ID (133)
0x09, 0x25, // Usage (0x25)
0x95, 0x06, // Report Count (6)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x86, // Report ID (134)
0x09, 0x26, // Usage (0x26)
0x95, 0x06, // Report Count (6)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x87, // Report ID (135)
0x09, 0x27, // Usage (0x27)
0x95, 0x23, // Report Count (35)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x88, // Report ID (136)
0x09, 0x28, // Usage (0x28)
0x95, 0x22, // Report Count (34)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x89, // Report ID (137)
0x09, 0x29, // Usage (0x29)
0x95, 0x02, // Report Count (2)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x90, // Report ID (144)
0x09, 0x30, // Usage (0x30)
0x95, 0x05, // Report Count (5)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x91, // Report ID (145)
0x09, 0x31, // Usage (0x31)
0x95, 0x03, // Report Count (3)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x92, // Report ID (146)
0x09, 0x32, // Usage (0x32)
0x95, 0x03, // Report Count (3)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x93, // Report ID (147)
0x09, 0x33, // Usage (0x33)
0x95, 0x0C, // Report Count (12)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xA0, // Report ID (160)
0x09, 0x40, // Usage (0x40)
0x95, 0x06, // Report Count (6)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xA1, // Report ID (161)
0x09, 0x41, // Usage (0x41)
0x95, 0x01, // Report Count (1)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xA2, // Report ID (162)
0x09, 0x42, // Usage (0x42)
0x95, 0x01, // Report Count (1)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xA3, // Report ID (163)
0x09, 0x43, // Usage (0x43)
0x95, 0x30, // Report Count (48)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xA4, // Report ID (164)
0x09, 0x44, // Usage (0x44)
0x95, 0x0D, // Report Count (13)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xA5, // Report ID (165)
0x09, 0x45, // Usage (0x45)
0x95, 0x15, // Report Count (21)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xA6, // Report ID (166)
0x09, 0x46, // Usage (0x46)
0x95, 0x15, // Report Count (21)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xF0, // Report ID (240)
0x09, 0x47, // Usage (0x47)
0x95, 0x3F, // Report Count (63)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xF1, // Report ID (241)
0x09, 0x48, // Usage (0x48)
0x95, 0x3F, // Report Count (63)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xF2, // Report ID (242)
0x09, 0x49, // Usage (0x49)
0x95, 0x0F, // Report Count (15)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xA7, // Report ID (167)
0x09, 0x4A, // Usage (0x4A)
0x95, 0x01, // Report Count (1)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xA8, // Report ID (168)
0x09, 0x4B, // Usage (0x4B)
0x95, 0x01, // Report Count (1)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xA9, // Report ID (169)
0x09, 0x4C, // Usage (0x4C)
0x95, 0x08, // Report Count (8)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xAA, // Report ID (170)
0x09, 0x4E, // Usage (0x4E)
0x95, 0x01, // Report Count (1)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xAB, // Report ID (171)
0x09, 0x4F, // Usage (0x4F)
0x95, 0x39, // Report Count (57)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xAC, // Report ID (172)
0x09, 0x50, // Usage (0x50)
0x95, 0x39, // Report Count (57)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xAD, // Report ID (173)
0x09, 0x51, // Usage (0x51)
0x95, 0x0B, // Report Count (11)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xAE, // Report ID (174)
0x09, 0x52, // Usage (0x52)
0x95, 0x01, // Report Count (1)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xAF, // Report ID (175)
0x09, 0x53, // Usage (0x53)
0x95, 0x02, // Report Count (2)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xB0, // Report ID (176)
0x09, 0x54, // Usage (0x54)
0x95, 0x3F, // Report Count (63)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection
};
struct _URB_CONTROL_DESCRIPTOR_REQUEST* pRequest = &Urb->UrbControlDescriptorRequest;
TraceVerbose(
TRACE_USBPDO,
">> >> >> _URB_CONTROL_DESCRIPTOR_REQUEST: Buffer Length %d",
pRequest->TransferBufferLength);
if (pRequest->TransferBufferLength >= ARRAYSIZE(Ds4HidReportDescriptor))
{
RtlCopyMemory(pRequest->TransferBuffer, Ds4HidReportDescriptor, ARRAYSIZE(Ds4HidReportDescriptor));
status = STATUS_SUCCESS;
//
// Notify client library that PDO is ready
//
KeSetEvent(&this->_PdoBootNotificationEvent, 0, FALSE);
}
return status;
}
NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::UsbSelectInterface(PURB Urb)
{
UNREFERENCED_PARAMETER(Urb);
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::UsbGetStringDescriptorType(PURB Urb)
{
TraceVerbose(
TRACE_USBPDO,
"Index = %d",
Urb->UrbControlDescriptorRequest.Index);
switch (Urb->UrbControlDescriptorRequest.Index)
{
case 0:
{
// "American English"
UCHAR LangId[] =
{
0x04, 0x03, 0x09, 0x04
};
Urb->UrbControlDescriptorRequest.TransferBufferLength = ARRAYSIZE(LangId);
RtlCopyBytes(Urb->UrbControlDescriptorRequest.TransferBuffer, LangId, ARRAYSIZE(LangId));
break;
}
case 1:
{
TraceVerbose(
TRACE_USBPDO,
"LanguageId = 0x%X",
Urb->UrbControlDescriptorRequest.LanguageId);
if (Urb->UrbControlDescriptorRequest.TransferBufferLength < DS4_MANUFACTURER_NAME_LENGTH)
{
auto pDesc = static_cast<PUSB_STRING_DESCRIPTOR>(Urb->UrbControlDescriptorRequest.TransferBuffer);
pDesc->bLength = DS4_MANUFACTURER_NAME_LENGTH;
break;
}
// "Sony Computer Entertainment"
UCHAR ManufacturerString[DS4_MANUFACTURER_NAME_LENGTH] =
{
0x38, 0x03, 0x53, 0x00, 0x6F, 0x00, 0x6E, 0x00,
0x79, 0x00, 0x20, 0x00, 0x43, 0x00, 0x6F, 0x00,
0x6D, 0x00, 0x70, 0x00, 0x75, 0x00, 0x74, 0x00,
0x65, 0x00, 0x72, 0x00, 0x20, 0x00, 0x45, 0x00,
0x6E, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00,
0x74, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6E, 0x00,
0x6D, 0x00, 0x65, 0x00, 0x6E, 0x00, 0x74, 0x00
};
Urb->UrbControlDescriptorRequest.TransferBufferLength = DS4_MANUFACTURER_NAME_LENGTH;
RtlCopyBytes(Urb->UrbControlDescriptorRequest.TransferBuffer, ManufacturerString, DS4_MANUFACTURER_NAME_LENGTH);
break;
}
case 2:
{
TraceVerbose(
TRACE_USBPDO,
"LanguageId = 0x%X",
Urb->UrbControlDescriptorRequest.LanguageId);
if (Urb->UrbControlDescriptorRequest.TransferBufferLength < DS4_PRODUCT_NAME_LENGTH)
{
auto pDesc = static_cast<PUSB_STRING_DESCRIPTOR>(Urb->UrbControlDescriptorRequest.TransferBuffer);
pDesc->bLength = DS4_PRODUCT_NAME_LENGTH;
break;
}
// "Wireless Controller"
UCHAR ProductString[DS4_PRODUCT_NAME_LENGTH] =
{
0x28, 0x03, 0x57, 0x00, 0x69, 0x00, 0x72, 0x00,
0x65, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x73, 0x00,
0x73, 0x00, 0x20, 0x00, 0x43, 0x00, 0x6F, 0x00,
0x6E, 0x00, 0x74, 0x00, 0x72, 0x00, 0x6F, 0x00,
0x6C, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x72, 0x00
};
Urb->UrbControlDescriptorRequest.TransferBufferLength = DS4_PRODUCT_NAME_LENGTH;
RtlCopyBytes(Urb->UrbControlDescriptorRequest.TransferBuffer, ProductString, DS4_PRODUCT_NAME_LENGTH);
break;
}
default:
break;
}
return STATUS_SUCCESS;
}
NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::UsbBulkOrInterruptTransfer(_URB_BULK_OR_INTERRUPT_TRANSFER* pTransfer, WDFREQUEST Request)
{
NTSTATUS status = STATUS_SUCCESS;
WDFREQUEST notifyRequest;
// Data coming FROM us TO higher driver
if (pTransfer->TransferFlags & USBD_TRANSFER_DIRECTION_IN
&& pTransfer->PipeHandle == reinterpret_cast<USBD_PIPE_HANDLE>(0xFFFF0084))
{
TraceVerbose(
TRACE_USBPDO,
">> >> >> Incoming request, queuing...");
/* 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);
return (NT_SUCCESS(status)) ? STATUS_PENDING : status;
}
// Store relevant bytes of buffer in PDO context
RtlCopyBytes(&this->_OutputReport,
static_cast<PUCHAR>(pTransfer->TransferBuffer) + DS4_OUTPUT_BUFFER_OFFSET,
DS4_OUTPUT_BUFFER_LENGTH);
this->_AwaitOutputCache.Size = sizeof(DS4_AWAIT_OUTPUT);
this->_AwaitOutputCache.SerialNo = this->_SerialNo;
RtlCopyMemory(
this->_AwaitOutputCache.Report.Buffer,
pTransfer->TransferBuffer,
pTransfer->TransferBufferLength <= sizeof(DS4_OUTPUT_BUFFER)
? pTransfer->TransferBufferLength
: sizeof(DS4_OUTPUT_BUFFER)
);
DumpAsHex("!! XUSB_REQUEST_NOTIFICATION",
&this->_AwaitOutputCache,
sizeof(DS4_AWAIT_OUTPUT)
);
if (!NT_SUCCESS(status = DMF_NotifyUserWithRequestMultiple_DataBroadcast(
this->_OutputReportNotify,
&this->_AwaitOutputCache,
sizeof(DS4_AWAIT_OUTPUT),
STATUS_SUCCESS
)))
{
TraceError(
TRACE_USBPDO,
"DMF_NotifyUserWithRequestMultiple_DataBroadcast failed with status %!STATUS!",
status
);
}
if (NT_SUCCESS(WdfIoQueueRetrieveNextRequest(
this->_PendingNotificationRequests,
&notifyRequest)))
{
PDS4_REQUEST_NOTIFICATION notify = nullptr;
status = WdfRequestRetrieveOutputBuffer(
notifyRequest,
sizeof(DS4_REQUEST_NOTIFICATION),
reinterpret_cast<PVOID*>(&notify),
nullptr
);
if (NT_SUCCESS(status))
{
// Assign values to output buffer
notify->Size = sizeof(DS4_REQUEST_NOTIFICATION);
notify->SerialNo = this->_SerialNo;
notify->Report = this->_OutputReport;
DumpAsHex("!! XUSB_REQUEST_NOTIFICATION",
notify,
sizeof(DS4_REQUEST_NOTIFICATION)
);
WdfRequestCompleteWithInformation(notifyRequest, status, notify->Size);
}
else
{
TraceError(
TRACE_USBPDO,
"WdfRequestRetrieveOutputBuffer failed with status %!STATUS!",
status);
}
}
else
{
PVOID clientBuffer, contextBuffer;
if (NT_SUCCESS(DMF_BufferQueue_Fetch(
this->_UsbInterruptOutBufferQueue,
&clientBuffer,
&contextBuffer
)))
{
RtlCopyMemory(
clientBuffer,
&this->_OutputReport,
DS4_OUTPUT_BUFFER_LENGTH
);
*static_cast<size_t*>(contextBuffer) = DS4_OUTPUT_BUFFER_LENGTH;
TraceVerbose(TRACE_USBPDO, "Queued %Iu bytes", DS4_OUTPUT_BUFFER_LENGTH);
DMF_BufferQueue_Enqueue(this->_UsbInterruptOutBufferQueue, clientBuffer);
}
}
return status;
}
NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::UsbControlTransfer(PURB Urb)
{
NTSTATUS status;
switch (Urb->UrbControlTransfer.SetupPacket[6])
{
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;
}
return status;
}
NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::SubmitReportImpl(PVOID NewReport)
{
NTSTATUS status;
WDFREQUEST usbRequest;
/*
* The logic here is unusual to keep backwards compatibility with the
* original API that didn't allow submitting the full report.
*/
status = WdfIoQueueRetrieveNextRequest(this->_PendingUsbInRequests, &usbRequest);
if (!NT_SUCCESS(status))
return status;
// Get pending IRP
PIRP pendingIrp = WdfRequestWdmGetIrp(usbRequest);
// Get USB request block
const auto urb = static_cast<PURB>(URB_FROM_IRP(pendingIrp));
// Get transfer buffer
const auto buffer = static_cast<PUCHAR>(urb->UrbBulkOrInterruptTransfer.TransferBuffer);
// Set correct buffer size
urb->UrbBulkOrInterruptTransfer.TransferBufferLength = DS4_REPORT_SIZE;
// Cast to expected struct
const auto pSubmit = static_cast<PDS4_SUBMIT_REPORT>(NewReport);
/*
* Copy report to cache and transfer buffer
* Skip first byte as it contains the never changing report ID
*/
//
// "Old" API which only allows to update partial report
//
if (pSubmit->Size == sizeof(DS4_SUBMIT_REPORT))
{
TraceVerbose(TRACE_DS4, "Received DS4_SUBMIT_REPORT update");
RtlCopyBytes(
&this->_Report[1],
&(static_cast<PDS4_SUBMIT_REPORT>(NewReport))->Report,
sizeof((static_cast<PDS4_SUBMIT_REPORT>(NewReport))->Report)
);
}
//
// "Extended" API allowing complete report update
//
if (pSubmit->Size == sizeof(DS4_SUBMIT_REPORT_EX))
{
TraceVerbose(TRACE_DS4, "Received DS4_SUBMIT_REPORT_EX update");
RtlCopyBytes(
&this->_Report[1],
&(static_cast<PDS4_SUBMIT_REPORT_EX>(NewReport))->Report,
sizeof((static_cast<PDS4_SUBMIT_REPORT_EX>(NewReport))->Report)
);
}
if (buffer)
RtlCopyBytes(buffer, this->_Report, DS4_REPORT_SIZE);
// Complete pending request
WdfRequestComplete(usbRequest, status);
return status;
}
VOID ViGEm::Bus::Targets::EmulationTargetDS4::ReverseByteArray(PUCHAR Array, INT Length)
{
const auto s = static_cast<PUCHAR>(ExAllocatePoolZero(
NonPagedPoolNx,
sizeof(UCHAR) * Length,
'U4SD'
));
INT c, d;
if (s == nullptr)
return;
for (c = Length - 1, d = 0; c >= 0; c--, d++)
*(s + d) = *(Array + c);
for (c = 0; c < Length; c++)
*(Array + c) = *(s + c);
ExFreePoolWithTag(s, 'U4SD');
}
VOID ViGEm::Bus::Targets::EmulationTargetDS4::GenerateRandomMacAddress(PMAC_ADDRESS Address)
{
// Vendor "C0:13:37"
Address->Vendor0 = 0xC0;
Address->Vendor1 = 0x13;
Address->Vendor2 = 0x37;
ULONG seed = KeQueryPerformanceCounter(NULL).LowPart;
Address->Nic0 = RtlRandomEx(&seed) % 0xFF;
Address->Nic1 = RtlRandomEx(&seed) % 0xFF;
Address->Nic2 = RtlRandomEx(&seed) % 0xFF;
}
void ViGEm::Bus::Targets::EmulationTargetDS4::ProcessPendingNotification(WDFQUEUE Queue)
{
NTSTATUS status;
WDFREQUEST request;
PVOID clientBuffer, contextBuffer;
PDS4_REQUEST_NOTIFICATION notify = nullptr;
FuncEntry(TRACE_DS4);
//
// Loop through and drain all queued requests until buffer is empty
//
while (NT_SUCCESS(WdfIoQueueRetrieveNextRequest(Queue, &request)))
{
status = DMF_BufferQueue_Dequeue(
this->_UsbInterruptOutBufferQueue,
&clientBuffer,
&contextBuffer
);
//
// Shouldn't happen, but if so, error out
//
if (!NT_SUCCESS(status))
{
//
// Don't requeue request as we maya be out of order now
//
WdfRequestComplete(request, status);
continue;
}
if (NT_SUCCESS(WdfRequestRetrieveOutputBuffer(
request,
sizeof(DS4_REQUEST_NOTIFICATION),
reinterpret_cast<PVOID*>(&notify),
nullptr
)))
{
//
// Assign values to output buffer
//
notify->Size = sizeof(DS4_REQUEST_NOTIFICATION);
notify->SerialNo = this->_SerialNo;
notify->Report = *static_cast<PDS4_OUTPUT_REPORT>(clientBuffer);
DumpAsHex("!! XUSB_REQUEST_NOTIFICATION",
notify,
sizeof(DS4_REQUEST_NOTIFICATION)
);
WdfRequestCompleteWithInformation(request, status, notify->Size);
}
DMF_BufferQueue_Reuse(this->_UsbInterruptOutBufferQueue, clientBuffer);
//
// If no more buffer to process, exit loop and await next callback
//
if (DMF_BufferQueue_Count(this->_UsbInterruptOutBufferQueue) == 0)
{
break;
}
}
TraceVerbose(TRACE_USBPDO, "%!FUNC! Exit");
}
VOID ViGEm::Bus::Targets::EmulationTargetDS4::PendingUsbRequestsTimerFunc(
_In_ WDFTIMER Timer
)
{
const auto ctx = reinterpret_cast<EmulationTargetDS4*>(Core::EmulationTargetPdoGetContext(
WdfTimerGetParentObject(Timer))->Target);
WDFREQUEST usbRequest;
FuncEntry(TRACE_DS4);
// Get pending USB request
const auto status = WdfIoQueueRetrieveNextRequest(ctx->_PendingUsbInRequests, &usbRequest);
if (NT_SUCCESS(status))
{
// Get pending IRP
const auto pendingIrp = WdfRequestWdmGetIrp(usbRequest);
const auto irpStack = IoGetCurrentIrpStackLocation(pendingIrp);
// Get USB request block
const auto urb = static_cast<PURB>(irpStack->Parameters.Others.Argument1);
// Get transfer buffer
const auto buffer = static_cast<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, ctx->_Report, DS4_REPORT_SIZE);
// Complete pending request
WdfRequestComplete(usbRequest, status);
}
TraceVerbose(TRACE_DS4, "%!FUNC! Exit with status %!STATUS!", status);
}
VOID ViGEm::Bus::Targets::EmulationTargetDS4::DmfDeviceModulesAdd(_In_ PDMFMODULE_INIT DmfModuleInit)
{
UNREFERENCED_PARAMETER(DmfModuleInit);
}
VOID ViGEm::Bus::Targets::EmulationTargetDS4::SetOutputReportNotifyModule(DMFMODULE Module)
{
this->_OutputReportNotify = Module;
}