diff --git a/sys/CRTCPP.hpp b/sys/CRTCPP.hpp new file mode 100644 index 0000000..1e5db83 --- /dev/null +++ b/sys/CRTCPP.hpp @@ -0,0 +1,38 @@ +#pragma once +#include + +constexpr auto cpp_pool_tag = 'EGiV'; + +#ifdef _AMD64_ + +static void* operator new(size_t lBlockSize) +{ + return ExAllocatePoolWithTag(NonPagedPoolNx, lBlockSize, cpp_pool_tag); +} + +static void operator delete(void* p) +{ + if (p == nullptr) + { + return; + } + ExFreePoolWithTag(p, cpp_pool_tag); +} + +#else + +static void* __CRTDECL operator new(size_t lBlockSize) +{ + return ExAllocatePoolWithTag(NonPagedPoolNx, lBlockSize, CPP_POOL_TAG); +} + +static void __CRTDECL operator delete(void* p) +{ + if (!p) + { + return; + } + ExFreePoolWithTag(p, CPP_POOL_TAG); +} + +#endif diff --git a/sys/EmulationTargetPDO.cpp b/sys/EmulationTargetPDO.cpp new file mode 100644 index 0000000..078c372 --- /dev/null +++ b/sys/EmulationTargetPDO.cpp @@ -0,0 +1,58 @@ +#include "EmulationTargetPDO.hpp" + +using namespace ViGEm::Bus::Core; + +BOOLEAN USB_BUSIFFN EmulationTargetPDO::UsbIsDeviceHighSpeed(IN PVOID BusContext) +{ + UNREFERENCED_PARAMETER(BusContext); + + return TRUE; +} + +NTSTATUS USB_BUSIFFN EmulationTargetPDO::UsbQueryBusInformation(IN PVOID BusContext, IN ULONG Level, + IN OUT PVOID BusInformationBuffer, + IN OUT PULONG BusInformationBufferLength, + OUT PULONG BusInformationActualLength) +{ + UNREFERENCED_PARAMETER(BusContext); + UNREFERENCED_PARAMETER(Level); + UNREFERENCED_PARAMETER(BusInformationBuffer); + UNREFERENCED_PARAMETER(BusInformationBufferLength); + UNREFERENCED_PARAMETER(BusInformationActualLength); + + return STATUS_UNSUCCESSFUL; +} + +NTSTATUS USB_BUSIFFN EmulationTargetPDO::UsbSubmitIsoOutUrb(IN PVOID BusContext, IN PURB Urb) +{ + UNREFERENCED_PARAMETER(BusContext); + UNREFERENCED_PARAMETER(Urb); + + return STATUS_UNSUCCESSFUL; +} + +NTSTATUS USB_BUSIFFN EmulationTargetPDO::UsbQueryBusTime(IN PVOID BusContext, IN OUT PULONG CurrentUsbFrame) +{ + UNREFERENCED_PARAMETER(BusContext); + UNREFERENCED_PARAMETER(CurrentUsbFrame); + + return STATUS_UNSUCCESSFUL; +} + +VOID USB_BUSIFFN EmulationTargetPDO::UsbGetUSBDIVersion(IN PVOID BusContext, + IN OUT PUSBD_VERSION_INFORMATION VersionInformation, + IN OUT PULONG HcdCapabilities) +{ + UNREFERENCED_PARAMETER(BusContext); + + if (VersionInformation != nullptr) + { + VersionInformation->USBDI_Version = 0x500; /* Usbport */ + VersionInformation->Supported_USB_Version = 0x200; /* USB 2.0 */ + } + + if (HcdCapabilities != nullptr) + { + *HcdCapabilities = 0; + } +} diff --git a/sys/EmulationTargetPDO.hpp b/sys/EmulationTargetPDO.hpp new file mode 100644 index 0000000..eed7ca7 --- /dev/null +++ b/sys/EmulationTargetPDO.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include +#include + +#include +#include + +// +// 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) + +namespace ViGEm::Bus::Core +{ + class EmulationTargetPDO + { + public: + EmulationTargetPDO() = default; + + virtual ~EmulationTargetPDO() = default; + + virtual NTSTATUS PrepareDevice(PWDFDEVICE_INIT DeviceInit, USHORT VendorId, USHORT ProductId, + PUNICODE_STRING DeviceId, PUNICODE_STRING DeviceDescription) = 0; + + virtual NTSTATUS PrepareHardware(WDFDEVICE Device) = 0; + + virtual NTSTATUS InitContext(WDFDEVICE Device) = 0; + + virtual VOID GetConfigurationDescriptorType(PUCHAR Buffer, ULONG Length) = 0; + + virtual VOID GetDeviceDescriptorType(PUSB_DEVICE_DESCRIPTOR pDescriptor, USHORT VendorId, USHORT ProductId) = 0; + + virtual VOID SelectConfiguration(PUSBD_INTERFACE_INFORMATION pInfo) = 0; + protected: + static const ULONG _maxHardwareIdLength = 0xFF; + + static BOOLEAN USB_BUSIFFN UsbIsDeviceHighSpeed(IN PVOID BusContext); + + static NTSTATUS USB_BUSIFFN UsbQueryBusInformation( + IN PVOID BusContext, + IN ULONG Level, + IN OUT PVOID BusInformationBuffer, + IN OUT PULONG BusInformationBufferLength, + OUT PULONG BusInformationActualLength + ); + + static NTSTATUS USB_BUSIFFN UsbSubmitIsoOutUrb(IN PVOID BusContext, IN PURB Urb); + + static NTSTATUS USB_BUSIFFN UsbQueryBusTime(IN PVOID BusContext, IN OUT PULONG CurrentUsbFrame); + + static VOID USB_BUSIFFN UsbGetUSBDIVersion( + IN PVOID BusContext, + IN OUT PUSBD_VERSION_INFORMATION VersionInformation, + IN OUT PULONG HcdCapabilities + ); + }; +} diff --git a/sys/RCa08996 b/sys/RCa08996 new file mode 100644 index 0000000..e1da2b4 Binary files /dev/null and b/sys/RCa08996 differ diff --git a/sys/ViGEmBus.vcxproj b/sys/ViGEmBus.vcxproj index a8b76d3..5c57547 100644 --- a/sys/ViGEmBus.vcxproj +++ b/sys/ViGEmBus.vcxproj @@ -171,6 +171,7 @@ true true trace.h + stdcpp17 @@ -186,13 +187,16 @@ + + + @@ -203,10 +207,12 @@ + + diff --git a/sys/ViGEmBus.vcxproj.filters b/sys/ViGEmBus.vcxproj.filters index 66f00db..d13afda 100644 --- a/sys/ViGEmBus.vcxproj.filters +++ b/sys/ViGEmBus.vcxproj.filters @@ -20,6 +20,12 @@ {bbf85b1d-5a75-4302-af4e-46627fcf0d78} + + {b00da32a-ce46-490d-9e77-95bb90925995} + + + {3a87fd70-9882-47c4-a23b-50dd0b42b0f8} + @@ -66,6 +72,15 @@ Header Files + + Header Files\Targets + + + Header Files + + + Header Files + @@ -95,6 +110,12 @@ Source Files + + Source Files\Targets + + + Source Files + diff --git a/sys/Xusb.h b/sys/Xusb.h index 3a96810..54c796f 100644 --- a/sys/Xusb.h +++ b/sys/Xusb.h @@ -117,6 +117,7 @@ typedef struct _XUSB_DEVICE_DATA WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(XUSB_DEVICE_DATA, XusbGetData) +EXTERN_C_START NTSTATUS Bus_XusbSubmitReport( @@ -136,3 +137,5 @@ 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 diff --git a/sys/XusbPdo.cpp b/sys/XusbPdo.cpp new file mode 100644 index 0000000..d9a11a2 --- /dev/null +++ b/sys/XusbPdo.cpp @@ -0,0 +1,574 @@ +#include "XusbPdo.hpp" +#include "trace.h" +#include "XusbPdo.tmh" +#define NTSTRSAFE_LIB +#include +#include + + +using namespace ViGEm::Bus::Targets; + +PCWSTR EmulationTargetXUSB::_deviceDescription = L"Virtual Xbox 360 Controller"; + +NTSTATUS EmulationTargetXUSB::PrepareDevice(PWDFDEVICE_INIT DeviceInit, USHORT VendorId, + USHORT ProductId, 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)) + { + 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 EmulationTargetXUSB::PrepareHardware(WDFDEVICE Device) +{ + WDF_QUERY_INTERFACE_CONFIG ifaceCfg; + + INTERFACE dummyIface; + + dummyIface.Size = sizeof(INTERFACE); + dummyIface.Version = 1; + dummyIface.Context = static_cast(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, + static_cast(&dummyIface), + &GUID_PNP_LOCATION_INTERFACE, + nullptr + ); + + NTSTATUS 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, + static_cast(&dummyIface), + &GUID_D3COLD_SUPPORT_INTERFACE, + nullptr + ); + + 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, + static_cast(&dummyIface), + &GUID_REENUMERATE_SELF_INTERFACE_STANDARD, + nullptr + ); + + 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 = static_cast(Device); + + xusbInterface.InterfaceReference = WdfDeviceInterfaceReferenceNoOp; + xusbInterface.InterfaceDereference = WdfDeviceInterfaceDereferenceNoOp; + + xusbInterface.SubmitIsoOutUrb = UsbSubmitIsoOutUrb; + xusbInterface.GetUSBDIVersion = UsbGetUSBDIVersion; + xusbInterface.QueryBusTime = UsbQueryBusTime; + xusbInterface.QueryBusInformation = UsbQueryBusInformation; + xusbInterface.IsDeviceHighSpeed = UsbIsDeviceHighSpeed; + + WDF_QUERY_INTERFACE_CONFIG_INIT( + &ifaceCfg, + reinterpret_cast(&xusbInterface), + &USB_BUS_INTERFACE_USBDI_GUID, + nullptr + ); + + 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 EmulationTargetXUSB::InitContext(WDFDEVICE Device) +{ + 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 = XusbPdoGetContext(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 + NTSTATUS status = WdfMemoryCreate( + &attributes, + NonPagedPoolNx, + XUSB_POOL_TAG, + XUSB_BLOB_STORAGE_SIZE, + &xusb->InterruptBlobStorage, + reinterpret_cast(&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 EmulationTargetXUSB::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 EmulationTargetXUSB::GetDeviceDescriptorType(PUSB_DEVICE_DESCRIPTOR pDescriptor, USHORT VendorId, USHORT ProductId) +{ + 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 = VendorId; + pDescriptor->idProduct = ProductId; + pDescriptor->bcdDevice = 0x0114; + pDescriptor->iManufacturer = 0x01; + pDescriptor->iProduct = 0x02; + pDescriptor->iSerialNumber = 0x03; + pDescriptor->bNumConfigurations = 0x01; +} + +VOID EmulationTargetXUSB::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 = (USBD_PIPE_TYPE)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 = (USBD_PIPE_TYPE)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 = (USBD_PIPE_TYPE)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 = (USBD_PIPE_TYPE)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 = (USBD_PIPE_TYPE)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 = (USBD_PIPE_TYPE)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 = (USBD_PIPE_TYPE)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; +} diff --git a/sys/XusbPdo.hpp b/sys/XusbPdo.hpp new file mode 100644 index 0000000..289712a --- /dev/null +++ b/sys/XusbPdo.hpp @@ -0,0 +1,119 @@ +#pragma once + + +#include "EmulationTargetPDO.hpp" + +namespace ViGEm::Bus::Targets +{ + constexpr auto XUSB_POOL_TAG = 'EGiV'; + + typedef struct _XUSB_REPORT + { + USHORT wButtons; + BYTE bLeftTrigger; + BYTE bRightTrigger; + SHORT sThumbLX; + SHORT sThumbLY; + SHORT sThumbRX; + SHORT sThumbRY; + + } XUSB_REPORT, * PXUSB_REPORT; + + 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[0x08]; + + // + // 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, XusbPdoGetContext) + + class EmulationTargetXUSB : public Core::EmulationTargetPDO + { + public: + EmulationTargetXUSB() = default; + ~EmulationTargetXUSB() = default; + + NTSTATUS PrepareDevice(PWDFDEVICE_INIT DeviceInit, USHORT VendorId, USHORT ProductId, + PUNICODE_STRING DeviceId, PUNICODE_STRING DeviceDescription); + + NTSTATUS PrepareHardware(WDFDEVICE Device); + + NTSTATUS InitContext(WDFDEVICE Device); + + VOID GetConfigurationDescriptorType(PUCHAR Buffer, ULONG Length); + + VOID GetDeviceDescriptorType(PUSB_DEVICE_DESCRIPTOR pDescriptor, USHORT VendorId, USHORT ProductId); + + VOID SelectConfiguration(PUSBD_INTERFACE_INFORMATION pInfo); + + private: + static PCWSTR _deviceDescription; + +#if defined(_X86_) + static const int XUSB_CONFIGURATION_SIZE = 0x00E4; +#else + static const int XUSB_CONFIGURATION_SIZE = 0x0130; +#endif + static const int XUSB_DESCRIPTOR_SIZE = 0x0099; + static const int XUSB_RUMBLE_SIZE = 0x08; + static const int XUSB_LEDSET_SIZE = 0x03; + static const int XUSB_LEDNUM_SIZE = 0x01; + static const int XUSB_INIT_STAGE_SIZE = 0x03; + static const int XUSB_BLOB_STORAGE_SIZE = 0x2A; + + static const int XUSB_BLOB_00_OFFSET = 0x00; + static const int XUSB_BLOB_01_OFFSET = 0x03; + static const int XUSB_BLOB_02_OFFSET = 0x06; + static const int XUSB_BLOB_03_OFFSET = 0x09; + static const int XUSB_BLOB_04_OFFSET = 0x0C; + static const int XUSB_BLOB_05_OFFSET = 0x20; + static const int XUSB_BLOB_06_OFFSET = 0x23; + static const int XUSB_BLOB_07_OFFSET = 0x26; + }; +} diff --git a/sys/xusb.c b/sys/xusb.c index 08c4f3f..6e3ce29 100644 --- a/sys/xusb.c +++ b/sys/xusb.c @@ -29,6 +29,8 @@ #include #include "xusb.tmh" +EXTERN_C_START + NTSTATUS Xusb_PreparePdo( PWDFDEVICE_INIT DeviceInit, USHORT VendorId, @@ -637,3 +639,5 @@ NTSTATUS Xusb_GetUserIndex(WDFDEVICE Device, PXUSB_GET_USER_INDEX Request) return status; } + +EXTERN_C_END